STM32-08-串口

【STM32系列文章】

STM32-01-认识单片机
STM32-02-基础知识
STM32-03-HAL库
STM32-04-时钟树
STM32-05-SYSTEM文件夹
STM32-06-GPIO
STM32-07-外部中断

STM32 串口

1. 数据通信的基本概念

  1. 通信方式:
    在这里插入图片描述

  2. 数据传输方向:
    在这里插入图片描述

  3. 数据同步方式:
    在这里插入图片描述

  • 通信速率:

    在数字通信系统中,通信速率(传输速率)指数据在信道中传输的速度,分为传信率传码率

    传信率(比特率):每秒钟传输的信息量,即每秒钟传输的二进制位数,单位为bit/s;

    传码率(波特率):每秒钟传输的码元个数,单位为Band.

    比特率与波特率之间的关系:

    比特率 = 波特率 * log2M

    M表示码元承载的信息量,可以理解为码元的进制数,码元采用二进制编码时,数值上波特率与比特率相等。

  • 常见的串行通信接口

    通信接口接口引脚数据同步方式数据传输方向
    UART (通用异步收发器)TXD:发送端 RXD:接收端 GND:公共地异步通信全双工
    1-wireDQ:发送/接收端异步通信半双工
    IICSCL:同步时钟 SDA:数据输入/输出端同步通信半双工
    SPISCK:同步时钟 MISO:主机输入,从机输出 MOSI:主机输出,从机输入 CS:片选信号同步通信全双工

2. 串口通信协议

串行通信协议是指规定了数据包的内容,内容包含了起始位主体数据校验位停止位,双方需要约定一致的数据包格式才能正常收发数据的有关规范。
在这里插入图片描述

  1. 波特率:波特率表示每秒传送的码元符号的个数,决定数据帧里面一个位的时间长度,两个要通信的设备的波特率一定要设置相同,常见的波特率是4800,9600,115200.
  2. 数据帧格式:起始位、停止位、有效数据位、校验位。
    • 起始位和停止位:串行通信的一个数据帧是从起始位开始,直到停止位。数据帧中的起始位是由一个逻辑0的数据位表示,而数据帧的停止位可以是0.5、1、1.5、2个逻辑1的数据位表示,只要双方约定一致即可。
    • 有效数据位:数据帧的起始位之后,紧接着数据位,这是我们发送的有效数据,有效数据位是低位在前,高位在后。
    • 校验位:校验位可以认为是一个特殊的数据位。一般用来判断接收的数据位有无错误,检验的方法有:奇校验、偶校验、0校验、1校验。奇校验代表的是数据位和校验位中1的个数为奇数;偶校验代表的是数据位和校验位中1的个数为偶数;0 校验是指不管有效数据中的内容是什么,校验位总为0,1 校验是校验位总为1。 无校验是指数据帧中不包含校验位。

3. 串口

  • 串口:串行通信接口,指按位发送和接收的接口。

  • RS232接口
    在这里插入图片描述

    通信示意图
    在这里插入图片描述

  • USB接口
    在这里插入图片描述

  • USART框图
    在这里插入图片描述

  • 简化版框图
    在这里插入图片描述

  • 设置波特率
    在这里插入图片描述
    - 波特率相关寄存器
    在这里插入图片描述

4. 相关寄存器

  • 控制寄存器1CR1
    在这里插入图片描述

在这里插入图片描述在这里插入图片描述

  • 控制寄存器2CR2
    在这里插入图片描述

  • 控制寄存器3CR3
    在这里插入图片描述

  • 数据寄存器DR
    在这里插入图片描述

  • 状态寄存器SR
    在这里插入图片描述

5. MSP回调机制

在这里插入图片描述

在这里插入图片描述

6. HAL库中断回调机制

在这里插入图片描述

在这里插入图片描述

7. USART/UART异步通信配置步骤

  1. 配置串口工作参数

    HAL_UART_Init()
    
  2. 串口底层初始化

    HAL_UART_MspInit() 
    
  3. 开启串口异步接收中断

    HAL_UART_Receive_IT()
    
  4. 设置优先级,使能中断

    HAL_NVIC_SetPriority()
    HAL_NVIC_EnableIRQ()
    
  5. 编写中断服务函数

    USARTx_IRQHandler()
    UARTx_IRQHandler()
    
  6. 串口数据发送

    HAL_UART_Transmit()
    

8. IO引脚复用功能

  • 通用与复用

    通用:IO端口的输入或输出是由GPIO外设控制

    复用:IO端口的输入或输出由其它非GPIO外设控制

  • IO复用功能冲突问题:同一时间IO只能用作一种复用功能,否则会发生冲突,当遇到冲突时,可以考虑重映射功能。

  • 实现:IO复用功能的实现是通过AFIO寄存器,STM32F103ZET6共有6个AFIO的寄存器,事件控制寄存器AFIO_EVCR,复用重映射和调试I/O配置寄存器AFIO_MAPR,外部中断配置寄存器AFIO_EXTICR1,外部中断配置寄存器AFIO_EXTICR2,外部中断配置寄存器AFIO_EXTICR3,外部中断配置寄存器AFIO_EXTICR4

    复用重映射和调试I/O配置寄存器AFIO_MAPR寄存器用来实现引脚的重映射功能。

    例如 AFIO_MAPR 寄存器位 2 是对 USART1 的重映射,置 0: 没有重映像(TX/PA9,RX/PA10); 置 1: 重映像(TX/PB6,RX/PB7)。默认情况下,PA9 和 PA10 是作为串口 1 的引脚使用,假如 PA9 和 PA10 被用作其它地方,但还是需要用到串口 1,那么就可以在 AFIO_MAPR 的位 2 置 1,把串口 1 的引脚重映射到 PB6和PB7。
    在这里插入图片描述

9. 代码实现

  • 主要实现功能:通过串口接收或发送一个字符。

  • 硬件原理图
    电脑的USB引脚CH340 D+CH340 D-通过CH340C芯片与串口的TXD与RXD相连接,然后通过一个端子连接到单片机的USART1 RX PA10USART1 TX PA9引脚,从而完成单片机的串口与电脑的USB口的连接。
    在这里插入图片描述

  • 代码实现

    1. 配置串口工作参数

      void usart_init(uint32_t baudrate)
      {
          /*UART 初始化设置*/
          g_uart1_handle.Instance = USART1;                                     /* USART_UX */
          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); 
      }
      
    2. 串口底层初始化

      void HAL_UART_MspInit(UART_HandleTypeDef *huart)
      {
          GPIO_InitTypeDef gpio_init_struct;
      
          if (huart->Instance == USART1)                             /* 如果是串口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;               /* 复用推挽输出 */
              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;                   //设置上拉输入
              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
          }
      }
      
    3. 开启串口异步接收中断

      1.中实现
      
    4. 设置中断优先级

      2.中实现
      
    5. 编写中断服务函数

      void USART1_IRQHandler(void)
      {
          HAL_UART_IRQHandler(&g_uart1_handle);   /* 调用HAL库中断处理公用函数 */
      }
      
    6. 编写接收完成回调函数

      void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
      {
          if (huart->Instance == USART1)                    /* 如果是串口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);
          }
      }
      

      在接收完成回调函数中,首先判断串口的类型,然后按照接收的数据格式进行判断,以回车符(0x0d)和换行符(0x0a)为结尾,如果未接收到回车符和换行符,表示数据未接收完成,g_usart_rx_sta为串口接收标志,用于判断是否接收完成;g_rx_buffer数据接收缓冲区,串口发送过来的数据会存放在该缓冲区,然后把接收到的数据放在g_usart_rx_buf数组里面。在接收完成后,开启串口异步接收中断,等待下一次数据的接收。

    7. 主函数的编写

      // 1. 定义串口接收数据长度的变量和超时时间变量
          uint8_t len;
          uint16_t times = 0;
      
      //2. 进行各种外设的初始化操作,打开相关外设的时钟
          HAL_Init();                         /* 初始化HAL库 */
          sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
          delay_init(72);                     /* 延时初始化 */
          led_init();                         /* LED初始化 */
          key_init();            //按键初始化
          beep_init();           //蜂鸣器初始化
          exti_init();           //外部中断初始化
          usart_init(115200);    //串口初始化 
      
      //3. 循环主体
           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");             /* 插入换行 */
                  g_usart_rx_sta = 0;
              }
              else
              {
                  times++;
        
                  if (times % 30  == 0)
                      LED_toggle(); /* 闪烁LED,提示系统正在运行. */
                                           
                  delay_ms(10);
              }
          }
      

      主函数的循环里面主要是判断是否接收到数据,然后把接收到的数据发送出去,通过SR寄存器的TC位判断数据是否发送完成;同时使用LED灯代表系统在正常运行。

声明:资料来源(战舰STM32F103ZET6开发板资源包)

  1. Cortex-M3权威指南(中文).pdf
  2. STM32F10xxx参考手册_V10(中文版).pdf
  3. STM32F103 战舰开发指南V1.3.pdf
  4. STM32F103ZET6(中文版).pdf
  5. 战舰V4 硬件参考手册_V1.0.pdf
  • 31
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值