总:STM32——学习总纲

参考资源:

【免费】STmcu选型手册资源资源-CSDN下载

【免费】芯片资料STM32F103ZET6(中文版)资源-CSDN下载

【免费】STM32F1系列参考手册-V10(中)

一、数据通信的基础概念

1.1 串行/并行 通信

 

1.2 单工/半双工/全双工 通信

1.3 同步/异步 通信

有时钟就是同步通信。

1.4 波特率

码元:信号经过调制,并且进行了编码。

 

M 可以理解为,进制数,比如二进制码元,M就为2。

1.5 常见的串行通信接口

二、串口(RS-232)

2.1 串口简介 

这里先学 RS-232 串口

RS-232接口 也就是DB9接口 数据 握手 地线 其他
TXD(pin3):串口数据输出 RTS(pin7):请求发送 GND(pin5):信号地 RI(pin9):振铃指示
RXD(pin2):串口数据输入 CTS(pin8):清除发送
DSR(pin6):数据发送就绪
DCD(pin1):数据载波检测
DTR(pin4):数据终端就绪

2.2 RS-232电平 与 CMOS/TTL 电平对比

所以 CMOS/TTL 不能与 RS-232 直接通信。

2.3 设备间的 RS-232 通信示意图

2.4 STM32 串口与电脑USB口通信示意图

2.5 RS-232 异步通信协议

三、STM32 的 USART

3.1 STM32 的 USART 简介

USART

UART

 

3.2 STM32 的 USART 主要特征

如何快速查看STM32某个外设的数量及其对应的引脚?

异步通信只用到 TXD&&RXD

【免费】STmcu选型手册资源资源-CSDN下载

 

【免费】芯片资料STM32F103ZET6(中文版)资源-CSDN下载

 搜索

3.3 STM32 F1 的 USART 框图

【免费】STM32F1系列参考手册-V10(中)_stm32寄存器映射资源-CSDN下载

在此链接中文版STM32F1系列参考手册第25.3节中提到。

收发引脚是 PA9 PA10

3.4 STM32 H7、F4、F7 的 USART 图(上B站)

3.5 STM32 F1、F4、F7、H7的 USART 框图简化版

3.6 设置 USART 波特率(F1)(F4、F7、H7上B站)

3.6.1 公式介绍

波特率计算公式:baud = \frac{f_{ck}}{16*USART DIV}

其中 f_{ck} 是串口的时钟即图中  f_{PCLK_{x(x=1,2)}} ,如 USART1 的时钟是 PCLK2,其他串口都是PCLK1。

PCLK2时钟来自 APB2,最高稳定值是 72M。

PCLK1时钟来自APB1,最高稳定值是36M。

寄存器值计算公式:USARTDIV = DIV_{Mantissa} + (DIV_{Fraction}/16)

DIV_Mantissa 是USARTDIV的整数部分,DIV_Fraction是USART的小数部分。

而 baud波特率由用户定义,通过波特率公式计算出 USARTDIV,再寄存器值计算公式得出整数部分DIV_Mantissa,进而可算出小数部分DIV_Fraction的值。

3.6.2 寄存器操作设置波特率演示

整型类型的特性(去整不进位),通过 +0.5 的操作实现四舍五入的效果。减小误差。

3.6.3 波特率设置寄存器操作通用公式推演(F1)

一步一步的计算寄存器存储的值来设置波特率还是繁琐。使用通用的公式会更简便。

二进制左移1位操作 等于 数值*2。那么DIV_Mantissa左移4位等于 数值*16。

USART_BRR = USARTDIV的整数部分<<4 + USART的小数部分 * 16 + 0.5
          = USARTDIV的整数部分 * 16 + USART的小数部分 * 16 + 0.5
          =(USARTDIV的整数部分 + USART的小数部分)* 16 +0.5
          = USARTDIV * 16 + 0.5

HAL库操作会更简单,赋值baud给初始化成员即可,会自动设置波特率。

3.7 USART 寄存器介绍      

【免费】STM32F1系列参考手册-V10(中)_stm32寄存器映射资源-CSDN下载

3.7.1 控制寄存器1 (USART_CR1)

3.7.2 控制寄存器2 (USART_CR2)

3.7.3 控制寄存器3(CR3)

3.7.4 数据寄存器(USART_DR)

由USART_CR1 位12决定,0 -》有效位0~7,1-》有效位0~8

3.7.5 状态寄存器(USART_SR)

根据 TC 位知道能否发送数据。

3.8 需要配置的时序总结

四、HAL 库外设初始化MSP回调机制

4.1 机制简介

4.2 HAL库外设初始化MSP回调机制 - USART为例

五、HAL 库中断回调机制(了解)

5.1 机制简介

5.2 HAL库中断回调机制 - USART 为例(F1)

5.3 官方串口中断处理函数

六、USART/UART  异步通信配置步骤(掌握)

6.1 HAL库函数简介

6.1.1  HAL_UART_Init

6.1.2 HAL_UART_Receive_IT

6.1.3  HAL_UART_Transmit

 后缀无IT,非中断

其余HAL库函数在之前的篇章。

七、IO 引脚复用功能

7.1 何为复用

7.2 STM32 F1 的IO引脚复用

 注意事项:

冲突时,复用的功能都不能正常运行。

此时可将其中一个功能重映射到另一个可重映射功能的引脚上。

7.3 STM32 F4/F7/H7 的IO引脚复用(上B站)

在手册中找到对应的AFx复用器功能,赋值寄存器。

八、编程实战:串口异步通信实验

工程:【免费】串口实验工程通过串口接收或者发送字符资源-CSDN下载

调试:【免费】XCOM正点原子(ALIENTEK)官方推出的串口调试助手XCOM资源-CSDN下载

8.1 工程代码梳理

参考第六章 USART/UART 异步通信配置步骤

参考3.8 需要配置的时序总结

usart_init()

1.串口初始化

2.开启接收中断

#define RXBUFFERSIZE   1            /* 缓存大小 */
#define USART_UX       USART1

uint8_t g_rx_buffer[RXBUFFERSIZE];  /* HAL库使用的串口接收中断缓冲区,一个字符 */
UART_HandleTypeDef g_uart1_handle;  /* UART句柄 */

void usart_init(uint32_t baudrate)
{
    /*UART 初始化设置*/
    g_uart1_handle.Instance         = USART_UX;             /* USART1,2...的基地址 */
    g_uart1_handle.Init.BaudRate    = baudrate;             /* 波特率 */
    g_uart1_handle.Init.WordLength  = UART_WORDLENGTH_8B;   /* 字长为8位数据格式 */
    g_uart1_handle.Init.StopBits    = UART_STOPBITS_1;      /* 一个停止位 */
    g_uart1_handle.Init.Parity      = UART_PARITY_NONE;     /* 无奇偶校验位 */
    g_uart1_handle.Init.HwFlowCtl   = UART_HWCONTROL_NONE;  /* 无硬件流控 */
    g_uart1_handle.Init.Mode        = UART_MODE_TX_RX;      /* 收发模式 */
    HAL_UART_Init(&g_uart1_handle);                         /* HAL_UART_Init()会使能UART1 */

    /* 该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量 */
    HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)g_rx_buffer, RXBUFFERSIZE); 
}

HAL_UART_MspInit

1.使能 usartx 的时钟 -》stm32f1xx_hal_rcc.h

2.初始化IO

3.使能中断,HAL_Init()函数已经设置分组为2,所以抢占优先级和响应优先级的范围都是:0~3

/* 引脚 和 串口 定义 
 * 默认是针对USART1的.
 * 注意: 通过修改这几个宏定义,可以支持USART1~UART5任意一个串口.
 */
#define USART_TX_GPIO_PORT            GPIOA
#define USART_TX_GPIO_PIN             GPIO_PIN_9
#define USART_TX_GPIO_CLK_ENABLE()    do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   /* PA口时钟使能 */

#define USART_RX_GPIO_PORT            GPIOA
#define USART_RX_GPIO_PIN             GPIO_PIN_10
#define USART_RX_GPIO_CLK_ENABLE()    do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   /* PA口时钟使能 */

#define USART_UX                      USART1
#define USART_UX_IRQn                 USART1_IRQn
#define USART_UX_IRQHandler           USART1_IRQHandler
#define USART_UX_CLK_ENABLE()         do{ __HAL_RCC_USART1_CLK_ENABLE(); }while(0)  /* USART1 时钟使能 */

/**
 * @brief       UART底层初始化函数
 * @param       huart: UART句柄类型指针
 * @note        此函数会被HAL_UART_Init()调用
 *              完成时钟使能,引脚配置,中断配置
 * @retval      无
 */
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef gpio_init_struct;

    if (huart->Instance == USART_UX)                      /* 如果是串口1,进行串口1 MSP初始化 */
    {
        USART_TX_GPIO_CLK_ENABLE();                       /* 使能串口TX脚时钟 */
        USART_RX_GPIO_CLK_ENABLE();                       /* 使能串口RX脚时钟 */
        USART_UX_CLK_ENABLE();                            /* 使能串口时钟 */

        gpio_init_struct.Pin    = USART_TX_GPIO_PIN;      /* 串口发送引脚号 */
        gpio_init_struct.Mode   = GPIO_MODE_AF_PP;        /* 复用推挽输出 */
        /* 作为输出的话,在F1中不能配置上下拉,此配置可不管。 */
        gpio_init_struct.Pull   = GPIO_PULLUP;            /* 上拉 */
        gpio_init_struct.Speed  = GPIO_SPEED_FREQ_HIGH;   /* IO速度设置为高速 */
        HAL_GPIO_Init(USART_TX_GPIO_PORT, &gpio_init_struct);
                
        gpio_init_struct.Pin    = USART_RX_GPIO_PIN;      /* 串口RX脚 模式设置 */
        gpio_init_struct.Mode   = GPIO_MODE_AF_INPUT;     /* 串口RX脚 必须设置成输入模式 */
        gpio_init_struct.Pull   = GPIO_PULLUP;            /* 上拉 */
        gpio_init_struct.Speed  = GPIO_SPEED_FREQ_HIGH;   /* IO速度设置为高速 */
        HAL_GPIO_Init(USART_RX_GPIO_PORT, &gpio_init_struct);   
        
#if USART_EN_RX
        HAL_NVIC_EnableIRQ(USART_UX_IRQn);                /* 使能USART1中断通道 */
        HAL_NVIC_SetPriority(USART_UX_IRQn, 3, 3);        /* 组2,最低优先级:抢占优先级3,子优先级3 */
#endif
    }
}

中断服务函数与callback函数

参考第五章 HAL 库中断回调机制

#define USART_UX_IRQHandler                 USART1_IRQHandler

/**
 * @brief       串口1中断服务函数
 * @param       无
 * @retval      无
 */
void USART_UX_IRQHandler(void)
{
#if SYS_SUPPORT_OS                          /* 使用OS */
    OSIntEnter();    
#endif

    HAL_UART_IRQHandler(&g_uart1_handle);   /* 调用HAL库中断处理公用函数 */

#if SYS_SUPPORT_OS                          /* 使用OS */
    OSIntExit();
#endif

}
/* 接收缓冲, 最大USART_REC_LEN个字节. */
uint8_t g_usart_rx_buf[USART_REC_LEN];

/*  接收状态
 *  bit15,      接收完成标志
 *  bit14,      接收到0x0d
 *  bit13~0,    接收到的有效字节数目
*/
uint16_t g_usart_rx_sta = 0;

/**
 * @brief       串口数据接收回调函数
                数据处理在这里进行
 * @param       huart:串口句柄
 * @retval      无
 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART_UX)                    /* 如果是串口1 */
    {
        if ((g_usart_rx_sta & 0x8000) == 0)             /* 接收未完成 */
        {
            if (g_usart_rx_sta & 0x4000)                /* 接收到了0x0d(即回车键) */
            {
                if (g_rx_buffer[0] != 0x0a)             /* 接收到的不是0x0a(即不是换行键) */
                {
                    g_usart_rx_sta = 0;                 /* 接收错误,重新开始 */
                }
                else                                    /* 接收到的是0x0a(即换行键) */
                {
                    g_usart_rx_sta |= 0x8000;           /* 接收完成了 */
                }
            }
            else                                        /* 还没收到0X0d(即回车键) */
            {
                if (g_rx_buffer[0] == 0x0d)
                    g_usart_rx_sta |= 0x4000;
                else
                {
                    g_usart_rx_buf[g_usart_rx_sta & 0X3FFF] = g_rx_buffer[0];
                    g_usart_rx_sta++;

                    if (g_usart_rx_sta > (USART_REC_LEN - 1))
                    {
                        g_usart_rx_sta = 0;             /* 接收数据错误,重新开始接收 */
                    }
                }
            }
        }

        HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)g_rx_buffer, RXBUFFERSIZE);
    }
}

main.c

#include "./stm32f1xx_it.h"
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"


int main(void)
{
    uint8_t len;
    uint16_t times = 0;

    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟为72Mhz */
    delay_init(72);                         /* 延时初始化 */
    usart_init(115200);                     /* 串口初始化为115200 */
    led_init();                             /* 初始化LED */
    while (1)
    {
        if (g_usart_rx_sta & 0x8000)        /* 接收到了数据? */
        {
            len = g_usart_rx_sta & 0x3fff;  /* 得到此次接收到的数据长度 */
            printf("\r\n您发送的消息为:\r\n");

            /* 发送接收到的数据 */
            HAL_UART_Transmit(&g_uart1_handle,(uint8_t*)g_usart_rx_buf, len, 1000); 

            /* 等待发送结束 */
            while(__HAL_UART_GET_FLAG(&g_uart1_handle,UART_FLAG_TC) != SET); 

            printf("\r\n\r\n"); /* 插入换行 */

            /* flag 复位 */
            g_usart_rx_sta = 0;
        }
        else
        {
            times++;

            if (times % 5000 == 0)
            {
                printf("\r\n正点原子 STM32开发板 串口实验\r\n");
                printf("正点原子@ALIENTEK\r\n\r\n\r\n");
            }

            if (times % 200 == 0) printf("请输入数据,以回车键结束\r\n");

            if (times % 30  == 0) LED0_TOGGLE(); /* 闪烁LED,提示系统正在运行. */

            delay_ms(10);
        }
    }
}

此宏函数用于判断 句柄的SR的falg状态

/** @brief  Checks whether the specified UART flag is set or not.
  * @param  __HANDLE__ specifies the UART Handle.
  *         UART Handle selects the USARTx or UARTy peripheral
  *         (USART,UART availability and x,y values depending on device).
  * @param  __FLAG__ specifies the flag to check.
  *        This parameter can be one of the following values:
  *            @arg UART_FLAG_CTS:  CTS Change flag (not available for UART4 and UART5)
  *            @arg UART_FLAG_LBD:  LIN Break detection flag
  *            @arg UART_FLAG_TXE:  Transmit data register empty flag
  *            @arg UART_FLAG_TC:   Transmission Complete flag
  *            @arg UART_FLAG_RXNE: Receive data register not empty flag
  *            @arg UART_FLAG_IDLE: Idle Line detection flag
  *            @arg UART_FLAG_ORE:  Overrun Error flag
  *            @arg UART_FLAG_NE:   Noise Error flag
  *            @arg UART_FLAG_FE:   Framing Error flag
  *            @arg UART_FLAG_PE:   Parity Error flag
  * @retval The new state of __FLAG__ (TRUE or FALSE).
  */
#define __HAL_UART_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->SR & (__FLAG__)) == (__FLAG__))

printf()函数

“10” -》 1个启动位 + 8个数据位 + 1个停止位

实际产品最好不用printf,占用资源。

8.2 串口调试工具使用

九、课堂总结

【免费】串口总结-脑图,思维导图资源-CSDN下载

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐