STM32 串口收发实验

实验内容

  • 用Keil编写程序,通过串口,完成STM32底座和电脑之间的数据接收和数据发送功能,并控制LED3的亮灭。

实验目的

  • 了解串口工作原理
  • 学会使用串口进行数据通信

实验环境

实验所需硬件

序号 名称 数量 备注
1 电脑 1台 电脑安装有Keil 5和ST-Link驱动
2 STM32底座模块 1个 ·
3 LED模块 1个 ·
4 ST-Link下载器 1个 ·
5 ST-Link下载器连接线 1根 ·
6 USB转TTL模块 1个 ·
7 杜邦线(母) 3根 ·
8 配套串口收发实验代码 1份 ·

实验所需软件

  • MDK5 安装步骤

  • ST-LINK 驱动安装步骤

  • Git 软件下载(可选)

ST-Link下载器 & ST-Link下载器连接线

STLink下载器

STM32底座:HIVE PRO STM32是一种基于STM32F103C8T6芯片的蜂巢底座。

STM32底座

LED模块:LED模块共有四个按键,4个LED灯,可供完成流水灯、按键处理等相关实验。按键的触发为低电平,LED灯低电平点亮。

LED模块

USB转TTL模块

USB转TTL模块

实验要求

  • 熟悉串口初始化的配置过程;
  • 能够独立配置串口、编写串口收发函数进行数据传输;
  • 能够使用串口调试助手对串口进行通信调试。

实验原理

串口介绍

串口作为MCU的重要外部接口,同时也是软件开发重要的调试手段,其重要性不言而喻。基本上所有的MCU都会带有串口,STM32自然也不例外。STM32的串口资源相当丰富的,功能也相当强劲。

接下来将主要从HAL库函数操作层面结合寄存器的描述,介绍如何设置串口,以达到最基本的通信功能。实现利用串口2的输入输出功能结合电脑端的串口调试助手进行实验介绍及实验演示。

串口的基本设置,就是波特率的设置。在STM32中只要开启了串口时钟,并设置了相应的IO口复用模式,再配置通讯的波特率、数据长度、奇偶校验位等信息,就可以正常使用STM32的串口功能。

  • 串口时钟使能:串口作为STM32的一个外设,其时钟由外设时钟使能寄存器控制。本节使用的串口2的时钟使能寄存器是APB1ENR寄存器,在STM32中,除了串口1的时钟使能寄存器是APB2ENR寄存器,其余串口的时钟使能寄存器都是APB1ENR寄存器。
  • 串口复位:当外设出现异常的时候,可以通过复位寄存器里面对应位的设置来实现该外设的复位,然后重新配置这个外设达到让其重新工作的目的。一般在系统刚开始配置外设的时候,都会先执行复位该外设的操作。
  • 串口波特率设置:每个串口都有一个自己独立的波特率寄存器USART_BRR,通过设置该寄存器达到配置不同波特率的目的。这里波特率的计算通过如下公式计算:

公式

这里的fpclkx(x=1、2)是给外设的时钟(PCLK1用于串口2、3、4、5,PCLK2用于串口1),USARTDIV是一个无符号的定点数,它的值可以由串口的BRR寄存器值得到。如何从USARTDIV的值得到USART_BRR的值,因为一般知道的是波特率,和PCLKx的时钟,要求的就是USART_BRR的值。

下面介绍如何通过USARTDIV得到串口USART_BRR寄存器的值,假设串口1要设置为9600的波特率,而PCLK2的时钟为72M。这样,根据上面的公式有:USARTDIV=72000000/(960016)=468.75 那么得到:DIV_Fraction=160.75=12=0x0C;DIV_Mantissa=468=0x1D4。 这样,就得到了USART1 -> BRR的值为0x1D4C。只要设置串口1的BRR寄存器值为0X1D4C就可以得到9600的波特率。 串口设置的一般步骤可以总结为如下几个步骤:

  • 串口时钟使能,GPIO时钟使能;
  • 串口初始化;
  • GPIO端口模式设置;
  • 开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤);
  • 调用串口的中断传输或者接收函数;
  • 编写中断处理函数。

下面,简单介绍下与串口基本配置直接相关的几个HAL库函数。这些函数和定义主要分布在stm32f1xx_hal_usart.h和stm32f1xx_hal_usart.c文件中。

A.串口时钟使能函数,定义在stm32f1xx_hal_rcc.h中

__HAL_RCC_USART2_CLK_ENABLE();//USART2时钟使能

B.串口初始化

UART2_Handler.Instance=USART2;//USART2
UART2_Handler.Init.BaudRate=115200;//波特率
UART2_Handler.Init.WordLength=UART_WORDLENGTH_8B;//字长为8位数据格式
UART2_Handler.Init.StopBits=UART_STOPBITS_1;//一个停止位
UART2_Handler.Init.Parity=UART_PARITY_NONE;//无奇偶校验位
UART2_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE;//无硬件流控
UART2_Handler.Init.Mode=UART_MODE_TX_RX;//收发模式
UART2_Handler.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&UART2_Handler);//HAL_UART_Init()初始化UART2

分别设置串口波特率、字长、奇偶校验位、停止位、开启发送和接收等。

C.串口IO初始化

GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE();//使能GPIOA时钟
__HAL_RCC_USART2_CLK_ENABLE();//使能USART2时钟
GPIO_Initure.Pin=GPIO_PIN_2;//PA2
GPIO_Initure.Mode=GPIO_MODE_AF_PP;//复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP;//上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;//高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure);//初始化PA2
GPIO_Initure.Pin=GPIO_PIN_3;//PA3
GPIO_Initure.Mode=GPIO_MODE_AF_INPUT;//设置为复用输入模式

HAL_GPIO_Init(GPIOA,&GPIO_Initure);//初始化PA3

这里分别设置了PA3和PA2作为串口的TX和RX引脚

D.串口的发送和接收。 普通发送和接收函数:

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, 
uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, 
uint8_t *pData, uint16_t Size, uint32_t Timeout);

这两个函数是阻塞发送的,需要设置超时时间,只有在规定时间内发送完所有数据后才会返回。

中断发送和接收函数:

HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, 
uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, 
uint8_t *pData, uint16_t Size);

要使用中断收发函数,必须使能串口中断:

HAL_NVIC_EnableIRQ(USART2_IRQn);//使能USART2中断通道
HAL_NVIC_SetPriority(USART2_IRQn,3,3);//抢占优先级3,子优先级3

上面两句话分别是开启中断和设置串口的中断优先级。

用中断传输和接收函数,是一个异步处理的过程,代码会立即返回,当发送完所有的数据,或者接收完所有的数据后,会分别调用,HAL_UART_TxCpltCallback()和HAL_UART_RxCpltCallback()函数。

通过以上的介绍,就了解了串口的最基本配置。关于串口更详细的介绍,请参考《STM32F10xxx参考手册》 通用同步异步收发器章节。

实验步骤

① 用杜邦线连接底座与USB转TTL模块,如下图。USB转TTL模块TXD、RXD、GND分别连接到底座的PA3、PA2、GND。

USB转TLL连接底座

② 访问github,进入github界面后点击Code,Clone HTTPS安全链接,如下图所示:

操作步骤

③ 打开电脑终端,进入工作目录workspace (workspace 为工程文件夹所在目录):

cd workspace

④ 运行clone命令:

git clone https://github.com/aiotcom/eps.git

下载目录至指定文件夹下。
如果提示“command not found”表示电脑没有安装Git,请至Git官网下载。
如果电脑没有安装 Git 软件,也可以进入Github,点击 Code -> DownLoad ZIP 下载所有工程代码。如下图所示:
下载代码
如果电脑没有公网,可以进:D盘\实验教程与代码选择相应的代码。

⑤ 打开 Keil 5 工程软件,点击工具栏: ` Project -> Open Project,选择工程文件:STM32基础实验\5.串口通信实验\USER\comm.uvprojx` 并打开。
打开工程
选择文件
如果没有安装该软件,请至Keil官网下载。

⑥ 工程启动后,点击 Rebuild 重新编译。如下图:

重新编译工程

⑦ 编译成功,如下图:

编译成功

⑧ 点击 Download 按钮下载程序,如下图所示:

下载程序 下载成功

⑨ 下载完成后,将USB线进行重连操作(即:将STLink的USB线从底座上取下,再重新接上)。

⑩ 打开电脑端的串口调试助手,在串口号一栏中选择相应的COM口,设置参数如下,点击“打开”按钮开启串口工具,如下图所示:

设置串口调试助手参数

⑪ 将USB转TTL模块连接到电脑,底座重新上电。

⑫ 串口调试助手显示如下图:

开机打印信息

⑬ 取消以16进制发送。输入指令(输入LED3 on LED3亮起,输入LED3 off LED3 熄灭)如下图:
发送点亮指令
发送点亮指令
发送熄灭指令
发送熄灭指令

代码讲解

① 程序目录结构,如下图。CORE文件夹为STM32内核代码,HALLIB文件文件夹为底层HAL库文件,我们主要关心main.c及HARDWARE中的代码。
代码目录结构

② main.c中进行硬件的初始化、初始化串口波特率115200。通过串口打印使用信息,重点了解串口2接收中断的中断服务函数中的代码如何接收串口数据保存在缓冲中。

#define UART2_RX_SIZE  32 //串口2接收缓冲的大小
uint8_t UART2_RxBuf[UART2_RX_SIZE];//串口2接收弹缓冲
uint8_t UART2_RxCnt = 0;//串口2接收计数
char *usage="LED3 on->点亮LED3\r\nLED3 off->熄灭LED3\r\n";
int main(void)
{
    HAL_Init();//初始化HAL库,为随后用到的HAL_Delay()函数提供时钟。    
    UART2_Init(115200);//串口1波特率115200
    LED_Init();//只初始化了LED3  
    HAL_UART_Transmit(&UART2_Handler,(uint8_t*)usage,strlen(usage),10);//打印使用方法
    while(1)
    {
        if(UART2_RxCnt){
            HAL_Delay(100);
            HAL_UART_Transmit(&UART2_Handler,UART2_RxBuf,UART2_RxCnt,10);
            if(strstr((void*)&UART2_RxBuf[0],"LED3 on")){//收到LED3 on指示,点亮LED3
                LED3_ON();
            }
            if(strstr((void*)&UART2_RxBuf[0],"LED3 off")){//收到LED3 off指示,熄灭LED3
                LED3_OFF();
            }            
            UART2_RxCnt = 0;
        }
    }
}

串口2中断服务函数:

void USART2_IRQHandler(void)
{
    uint8_t res;
    if((__HAL_UART_GET_FLAG(&UART2_Handler,UART_FLAG_RXNE)!=RESET)){  
        /*是串口的接收中断*/
        res = USART2->DR;
        UART2_RxBuf[UART2_RxCnt++] = res;
        
        if(UART2_RxCnt > (UART2_RX_SIZE-1)){//如果接收数据大于缓冲区,UART2_RxCnt清零。
            UART2_RxCnt = 0;
        }
    }
    HAL_UART_IRQHandler(&UART2_Handler);//清除中断标志位
}

③ LED.c为LED驱动代码,代码实现对LED3灯控制的IO进行初始化设置IO口为推挽上拉模式。重点不要忘记使用能IO接口的时钟及设置输出的速度。

void LED_Init(void)
{
    GPIO_InitTypeDef GPIO_Initure;
    
    LED_IO_RCC();

    GPIO_Initure.Pin   = LED3_PIN;//LED3_PIN定义在LED.h
    GPIO_Initure.Mode  = GPIO_MODE_OUTPUT_PP;//推挽输出
    GPIO_Initure.Pull  = GPIO_PULLUP;//上拉
    GPIO_Initure.Speed = GPIO_SPEED_HIGH;//高速
    HAL_GPIO_Init(LED_PORT,&GPIO_Initure);
        
    LED_OFF();///初始化完成全部LED灯灭
}

常见问题

  1. 弹出警告窗口,不能下载程序。

    • 请确认STLink驱动、STM32F103C8的DFP包是否安装。
    • STLink仿真器是否正常接入。
  2. 下载代码后程序没观察到实验现象。

    • 请重新上电,或者按下底座上的复位按键。
    • 模块没有安装稳妥。
  3. 接口接收不到数据或者下发命令没有反应。

    • USB转TTL模块与底座没有共地。
    • 串口通信的TXD、RXD接反。

实验思考

  1. 使用命令turn on led3turn on led3控制LED3的状态。

  2. 在原代码基础,当接收、并执行命令后,向上位返回当前灯的状态“灯己打开”、“灯已熄灭”。