实训1:定时器与串口综合训练

要求

  1. 开机后,LED1与LED2依次点亮,然后熄灭,进行灯光检测。LED1接到STM32的PF9, LED2接到STM32的PF10,高电平点亮。
  2. 系统通过串口 1向上位机发送一个字符串“STM32欢迎你! ”。
  3. LED1作为一个秒闪灯,系统向上位机发送完字符串后,开始亮0.5 秒,灭0.5秒…循环闪烁,并开始启动系统运行时间的记录,其 时分秒格式为“XX: XX: XX”。
  4. 上位机通过一个由3个字节组成的命令帧控制LED2灯的开关。该命 令帧的格式为“OxBF控制字OxFB"
    OxBF为帧头,OxFB为帧尾,控制字的定义如下:
    0xA1:打开LED2,返回信息 “XX:XX:XX LED2打开。"
    0xA2:关闭LED2,返回信息 "XX:XX:XX LED2关闭。”
    其他:返回信息“XX:XX:XX这个一个错误指令! ”
  • sprinft()​函数的用法:

    • sprintf()​,指的是字符串格式化函数,把格式化的数据写入某个字符串中
      int sprintf(char *string,char *format[,argument,...]);

    • 引入头文件 include "stdio.h"

    • 例:有一个表示温度的整形变量tmp​,现在要将其格式化为字符串“温度是:XX摄氏度”,并将其通过串口1发送出去。

      1
      2
      3
      uint8_t Str_buff[64];
      sprintf((char*)Str_buff,"温度是:%d摄氏度",tmp);
      HAL_UART_Transmit(&huart1,Str_buff,sizeof(Str_buff),0xFFFF);

1. 使用STM32CubeMX进行初始配置

  • 配置仿真器接口

  • 配置时钟为外部时钟

  • 配置时钟树

  • 配置TIM2作为1秒计时并打开中断

    注意:不同时钟与主频的关系不一定是1:1,TIM2的时钟频率是主频的二分之一

  • 配置串口1为异步并打开中断

    生成代码

2. 编写代码

  • 宏定义,简化部分代码

    1
    2
    3
    4
    5
    6
    7
    //宏定义
    #define LED1_ON() HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_SET) //设置引脚为高电平
    #define LED2_ON() HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_SET) //设置引脚为高电平
    #define LED1_OFF() HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_RESET) //设置引脚为高电平
    #define LED2_OFF() HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_RESET) //设置引脚为高电平
    #define LED1_TOG() HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_9) //反转电平
    #define LED2_TOG() HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_10) //反转电平
  • 定义相关全局变量

    1
    2
    3
    4
    5
    //全局变量定义
    uint8_t str1[] = "STM32欢迎你!\r\n";
    uint8_t hh =0,mm=0,ss=0,ss05=0; //存储时间
    uint8_t str_buff[64]; //字符串缓冲数组
    uint8_t Rx_dat[16]; //串口接收
  • 功能1的实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //自定义函数
    void Check_LED() //放到main()函数中的对应位置
    {
    LED1_ON();
    HAL_Delay(500);
    LED2_ON();
    HAL_Delay(500);
    LED1_OFF();
    HAL_Delay(500);
    LED2_OFF();
    HAL_Delay(500);

    }
  • 功能2的实现

    • 打开定时器

      1
      HAL_TIM_Base_Start_IT(&htim2);
    • 重写回调函数

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      //计时以及灯光闪烁
      void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //时钟走完一次后执行
      {
      LED1_TOG();
      ss05++;
      if(ss05==1)
      {
      ss05=0;
      ss++;
      if(ss==60)
      {
      ss=0;
      mm++;
      if(mm==60)
      {
      mm=0;
      hh++;
      }
      }
      }
      }
  • 功能3的实现

    • 引用头文件

      1
      #include <stdio.h>
    • 非阻塞性接收

      1
      HAL_UART_Receive_IT(&huart1,Rx_dat,3);	//放在while(1)外
    • 重定义虚函数

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)	//接收完后执行
      {
      if(huart->Instance == USART1)
      {
      if(Rx_dat[0]==0xBF && Rx_dat[2] == 0xFB)
      {
      switch(Rx_dat[1])
      {
      case 0xa1:LED2_ON();sprintf((char*)str_buff,"%d:%d:%d LED2打开!\r\n",hh,mm,ss);break;
      case 0xa2:LED2_OFF();sprintf((char*)str_buff,"%d:%d:%d LED2关闭!\r\n",hh,mm,ss);break;
      default: sprintf((char*)str_buff,"%d:%d:%d 这是一个错误指令!\r\n",hh,mm,ss);break;
      }
      HAL_UART_Transmit(&huart1,str_buff,sizeof(str_buff),10000);
      HAL_UART_Receive_IT(&huart1,Rx_dat,3); //如果这句话不在while(1)函数内则在回调函数内添加,不然执行一次后就不再接收
      }
      }
      }

3. 实验结果


附件

SHIXUN1.7z


这是跟随B站小蜜蜂老师的干货铺【小蜜蜂笔记】基于STM32CubeMX的嵌入式开发基础教程所写的学习笔记。

此教程以应用为主,辅以简单的原理讲解,能够快速的上手STM32的开发。