-
1 知识介绍
-
2 PPT
-
3 学习视频
-
4 国家脊梁
任务要求
设计一个LED流水灯系统,该系统与上位机之间通过串行通信接口相连。
上位机可发送命令对LED流水灯系统进行控制,系统中有8个LED灯,分别是LED1~LED8。系统上电时,8个LED灯默认为熄灭状态。系统运行时,8个LED灯依次点亮。(

LED流水灯系模式1(

LED流水灯系模式2(

案例分析
使用上位机发送串口指令mode_1#、mode_2#、stop#对应流水灯的模式一、模式二以及停止状态(

通过对任务进行分析,得到需要解决的任务点如下:
GPIO口模式设置
串口参数设置
如何判断数据是否接收完成
知识储备
(一)串口资源:
STM32 的串口资源相当丰富的,功能也相当强劲。STM32F1系列微控制器有5个收发器外设(俗称“串口”)可用于串行通信,包括3个USART和2个UART(通用异步收发器,Universal Asynchronous Receiver and Transmitter),如
UART与USART相比,裁剪了同步通信的功能,只有异步通信功能。同步通信与异步通信的区别在于通信中是否需要发送器输出同步时钟信号USART_CK,实际应用中一般使用异步通信。

从

当需要发送数据时,MCU内核将待发送的数据写入输出数据发送缓冲器后,串行口会自动地按照软件设定的格式将待发送的数据组成数据帧,发送控制器将适时地自动把数据从数据寄存器加载到串行输出移位寄存器,然后通过引脚TXD,把数据一位一位地发送出去。
当需要接收数据时,通过引脚RXD数据一位一位传到接收位移寄存器中,在传输接收数据寄存器中。
(二)数据格式:
一个数据帧包括:
1个起始位(低电平)
8个数据位
1个校验位
1个停止位(高电平表示结束)
注意:串口空闲时,数据线为高电平,所以起始位是一个周期的低电平做为起始。同理:停止位是一个周期的高电平。
STM32发送一个数据帧(也称为字符帧)需要分为三个部分:起始位+数据位+停止位。起始位是一个位周期的低电平,位周期就是每一位占用的时间;数据位就是要发送的8位或9位数据;停止位是一定时间周期的高电平。如

STM32接收一个数据帧:开始通信,信号线为空闲,当检测到由1到0的跳变时,开始对接收时钟计数;接收端检测到起始位后,隔16个接收时钟对输入信号检测一次,把对应的值作为D0位数据;再隔16个接收时钟,对输入信号检测一次,把对应的值作为D1位数据,直到全部数据位都输入;接收到规定的数据位个数和校验位之后,通信接口电路希望收到停止位(逻辑1),若此时未收到逻辑1,说明出现了错误,在状态寄存器中置“帧错误”标志;若没有错误,对全部数据位进行奇偶校验,无校验错时,把数据位从移位寄存器中取出送至数据输入寄存器,若校验错,在状态寄存器中置“奇偶错”标志;本帧信息全部接收完,把线路上出现的高电平置为空闲位;接收期间需要先监测 接收数据寄存器不为空(准备好读取接收到的数据),在检测线路是否空闲,最后如果没有错误中断标志位(

任务实施
(一)工程创建
1:新建STM32CubeMX工程,2:选择MCU型号(STM32F103VE),3:配置时钟树,先找RCC将图上红色框选1部分选择成外部晶振后,点击红色框选2部分进入时钟树配置界面,将HCLK配置为72MHz,PCLK1配置为36MHz,PCLK2配置为72MHz(可参照视频)。(

配置led灯控制引脚,GPIO模式配置位output模式(输出模式),其余参数如

如

(二)编写程序代码
在“usart.h”中输入以下代码,将USART发送函数重定向到printf()函数。
/* USER CODE BEGIN 0 */
int fputc(int ch,FILE *f)
{
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xffff);
return ch;
}
/* USER CODE END 0 */
在main.c中,完成变量、接收缓冲器、指令等的声明及赋值。
/* USER CODE BEGIN PV */
#include "string.h"
const char stringMode1[8] = "mode1#";
const char stringMode2[8] = "mode_2#";
const char stringStop[8] = "stop#";
int8_t ledMode = -1;
uint16_t LED_value = 0;
uint8_t uart1RxState = 0;
uint8_t uart1RxCounter = 0;
uint8_t uart1RxBuff[128] = {0};
/* USER CODE END PV */
在main.c中编写USER_UART_IRQHandler的业务逻辑代码。
/* USER CODE BEGIN 4 */
void USER_UART_IRQHandler()
{
if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE) != RESET)
{
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
uart1RxBuff[uart1RxCounter] = (uint8_t)(huart1.Instance->DR&(uint8_t)0x00ff);
uart1RxCounter++;
__HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_RXNE);
}
if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != RESET)
{
__HAL_UART_DISABLE_IT (&huart1,UART_IT_IDLE);
uart1RxState = 1;
}
/* USER CODE END 4 */
判断接收到的指令进行对比。并将指令对于的工作模式值存放在ledMode变量中。判断ledMode的值进行模式切换。具体操作可查看视频。
printf("hello word.\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
if(uart1RxState =1)
{
if(strstr((const char *)uart1RxBuff,stringMode1)!=NULL)
{
printf("I'm in mode_1!\r\n");
ledMode = 1;
LED_value = 0x80;
}
else if(strstr((const char *)uart1RxBuff,stringMode2)!=NULL)
{
printf("I'm in mode_2!\r\n");
ledMode = 2;
LED_value = 0x01;
}
else if(strst((const char *)uart1RxBuff,stringStop)!=NULL)
{
printf("I'm stop!\r\n");
ledMode 0;
LED value 0;
}
uartlRxState = 0;
uartlRxCounter = 0;
memset(uart1RxBuff,0,128);
}
HAL_GPIO_WritePin(GPIOE,0xff,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOE,LED_value,GPIO_PIN_RESET);
HAL_Delay(1000);
switch(ledMode)
{
case 1:
LED_value = LED_value>>1;
if(LED_value==0)
LED_value 0x80;
break;
case 2:
LED_value = LED_value<<1;
if(LED_value==0x100)
LED_value=0x01;
break;
case 0:
LED_value = 0;
break;
}
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */


