STM32项目实例:串口时间同步

文摘   2024-10-19 07:01   河北  

在嵌入式系统中,微控制器经常需要与外围设备(如触控屏、传感器等)或其他微控制器交换数据,一般采用并行或串行的方式。本节介绍如何使用串口通信功能实现微控制器与PC机时间同步功能。

1 项目分析

本项目实现如下功能:STM32微控制器通过串口和上位机建立通信连接,上位机获取本机日期、星期和时间,通过串口发送给STM32微控制器,微控制器在收到上位机发送过来的一组数据后,提取出年、月、日、星期、时、分、秒的数值,将时间高亮显示于数码管上,同步刷新于LCD,仅当收到上位机全部7个数据才对显示于LCD的日期和星期数据进行更新。微控制器每产生一次接收中断,就会将接收到的数据个数回传至上位机。

本项目包括两部分程序,一是单片机串口收发程序,二是上位机串口通信程序,本项目讨论的重点是单片机串口收发程序的设计,上位机程序设计只是简单介绍。微控制器端采用UART典型应用方法,即串口中断接收数据,阻塞发送数据。串口使用的一般步骤是先对其初始化,然后调用串口中断接收函数,等待接收数据;当上位机数据到来时,产生串口接收中断,在中断服务程序中完成数据的提取。因为上位机一次发送一组数据,需要按照一定的规则对数据进行提取,本项目是采用了一种较为简单的数据提取方法,即按数据到来的顺序进行赋值,即一批数据中第一个数据是年份,第二个数据是月份,以此类推,完成全部数据提取。完成数据接收工作之后,还需要调用阻塞发送函数,将系统接收到的数据个数回送至上位机,回为串口中断接收函数是一次性的,所以数据处理完成之后还需要再次调用中断接收函数。

2 微控制器端程序设计

2.1 复制工程文件

因为本项目涉及时间同步,所以复制第9章创建的工程文件0901 BasicTimer到桌面,并将文件夹重命名为1101 USART。

2.2 CubeMX配置

打开工程模板文件夹里面的Template.ioc文件,启动CubeMX配置软件,在左侧配置类别Categories下面的Connectivity列表中的找到USART1串口,打开其配置对话框,操作界面如图1所示。

图1 USART1配置界面

在模式设置部分只有两个参数,分别说明如下:

  • Mode:用于设置USART的工作模式,有Asynchronous(异步)、Synchronous(同步)、Single Wire(半双工)、IrDA(红外通信)、SmartCard(智能卡)等工作模式,我们使用的是UART功能,所以此处选择异步模式Asynchronous。

  • Hardware Flow Control (RS232):硬件流控制,有Disable、CTS Only、RTS Only、CTS/RTS共4个选项,由于本项目并没有使用硬件流控制,所以应选择无硬件流控制Disable。

参数设置部分包括串口通信的4个基本参数和微控制器的2个扩展参数。4个基本参数说明如下:

  • Baud Rate:串口通信的波特率,初始化配置时只要给出具体的波特率数值即可,CubeMX会根据设置的波特率自动配置相关寄存器。常用的串口波特率有9600b/s、14400b/s、19200b/s、115200b/s、460800b/s、500000b/s等,此处保留其默认值115200b/s。

  • Word Length:字长(数据位+奇偶校验位),可以设置为8位或9位,这里设置为8位。

  • Parity:奇偶校验位,可选None(无)、Even(偶校验)、Odd(奇校验),此处设置为None。如果设置为奇校验或偶校验时,字长应设置为9位。

  • Stop Bits:停止位,可选1位或2位,这里设置为1位。

  • STM32 MCU扩展的两个参数说明如下:

  • Data Direction:数据方向,可选Receive and Transmit(接收和发送)、Receive Only(仅接收)、Transmit Only(仅发送)。此处设置为Receive and Transmit。

  • Over Sampling:过采样,可选16 Samples或8 Samples,这里设置为16 Samples。选择不同的过采样数值会影响波特率的可设置范围,而CubeMX会自动更新波特率的可设置范围。

上述USART1串口配置过程仅需选择Mode下拉列表中的异步工作模式选项,其余参数均采用默认设置即可。完成上述配置,CubeMX会自动配置PA9和PA10作为USART1_TX和USART1_RX信号复用引脚,这与开发板电路设计是一致的,故无需再做任何GPIO设置。因为本项目需要使用串口中断接收数据,所以还需要打开USART1的全局中断。整个工程中断优先级分组设置为NVIC_PRIORITYGROUP_3,串口通信对中断响应时间并无苛刻要求,所以此处为USART1设置了一个中等的优先级。USART1的GPIO和NVIC配置结果如图2所示。

图2 USART1的GPIO和NVIC配置

2.3 初始化代码分析及用户程序编写

打开MDK-RAM文件夹下面的工程文件Template.uvprojx,将生成工程编译一下,没有错误和警告之后开始初始化代码分析和用户代码编写。

1.初始化代码分析

用户在CubeMX中启用了某个口串口,系统会自动生成串口初始化源文件usart.c和串口初始化头文件usart.h,分别用于串口初始化的实现和定义。usart.h仅用于外部变量和初始化函数的声明,usart.c内容如下,其中省略了程序沙箱和部分注释。

#include "usart.h"

UART_HandleTypeDef huart1;

void MX_USART1_UART_Init(void)

{

huart1.Instance = USART1;

huart1.Init.BaudRate = 115200;

huart1.Init.WordLength = UART_WORDLENGTH_8B;

huart1.Init.StopBits = UART_STOPBITS_1;

huart1.Init.Parity = UART_PARITY_NONE;

huart1.Init.Mode = UART_MODE_TX_RX;

huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;

huart1.Init.OverSampling = UART_OVERSAMPLING_16;

if (HAL_UART_Init(&huart1) != HAL_OK)

{    Error_Handler();  }

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)

{

GPIO_InitTypeDef GPIO_InitStruct = {0};

if(uartHandle->Instance==USART1)

{

/* USART1 clock enable */

__HAL_RCC_USART1_CLK_ENABLE();

__HAL_RCC_GPIOA_CLK_ENABLE();

/**  USART1 GPIO Configuration  PA9--> USART1_TX  PA10--> USART1_RX  **/

GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;

GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

GPIO_InitStruct.Pull = GPIO_NOPULL;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

GPIO_InitStruct.Alternate = GPIO_AF7_USART1;

HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/* USART1 interrupt Init */

HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);

HAL_NVIC_EnableIRQ(USART1_IRQn);

}

}

上述代码中,定义了一个串口外设对象指针变量huart1,调用MX_USART1_UART_Init()函数对USART1进行初始化,其选项配置信息和图1的CubeMX设置信息是一一对应的。HAL_UART_MspInit()函数是串口初始化的MSP函数,它在HAL_UART_Init()函数内被调用。重新实现这一函数主要用于串口的GPIO引脚配置和NVIC中断设置。

2.串口回调函数实现

当我们在CubeMX中启用USART1外设并使能其全局中断,则在stm32f4xx_it.c文件中会生成USART1的中断服务程序框架USART1_IRQHandler(),在其中仅调用串口通用处理函数HAL_UART_IRQHandler(),对各种中断事件进行判断,并调用各自的回调函数。对于本项目来说,当串口接收数据完成时会调用HAL_UART_RxCpltCallback()回调函数,依然选择在main.c中重新实现这一回调函数,其参考代码如下:

/* USER CODE BEGIN 4 */

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

{

  static uint8_t k=0;  

if(huart->Instance==USART1)

{

k++;

switch (k%7)

{

case 1:

year= RxData;

break;

case 2:

month=RxData;

break;

case 3:

day= RxData;

break;

case 4:

WeekIndex=RxData;

break;

case 5:

hour= RxData;

break;

case 6:

minute=RxData;

break;

case 0:

second= RxData;

DateFresh=1;  //更新日期、星期标志

break;

default:

break;

}

HAL_UART_Transmit(&huart1,&k,1,100);

HAL_UART_Receive_IT(&huart1,&RxData,1);

}

}

/* USER CODE END 4 */

在上述代码中,首先定义了一个静态变量k,用于记录串口中断发生的次数,随后根据数据到来的次序提取数值,另外为了不对日期和星期数据反复更新,还设置了一个全部数据接收完成标志位。最后将接收到的数据个数以阻塞方式回传上位机,并重新启动串口数据接收。

3.用户主程序编写

用户编写主程序参考代码如下:

#include "main.h"

#include "tim.h"

#include "usart.h"

#include "gpio.h"

#include "fsmc.h"

/* USER CODE BEGIN Includes */

#include "stdio.h"

#include "lcd.h"

/* USER CODE END Includes */

/* USER CODE BEGIN PV */

uint8_t hour=9,minute=30,second=25,ShowBuff[6],RxData=0;

uint8_t year=22,month=5,day=9,WeekIndex=1,DateFresh=0,TempStr[30]; //初始时间

char *WeekName[7]={"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday" };

/* USER CODE END PV */

void SystemClock_Config(void);

int main(void)

{

/* USER CODE BEGIN 1 */

uint8_t i;

/* USER CODE END 1 */

HAL_Init();

SystemClock_Config();

/* Initialize all configured peripherals */

MX_GPIO_Init();

MX_FSMC_Init();

MX_TIM6_Init();

MX_TIM7_Init();

MX_USART1_UART_Init();

/* USER CODE BEGIN WHILE */

LCD_Init();

HAL_TIM_Base_Start_IT(&htim6);

HAL_TIM_Base_Start_IT(&htim7);

HAL_UART_Receive_IT(&huart1,&RxData,1);  //以中断方式接收1个字节数据

for(i=0;i<5;i++)   //清屏0~4行

{ LCD_ShowString(0,24*i,(u8 *)"                           ",BLUE,WHITE,24,0); }

LCD_ShowString(4,24*1,(u8 *)"USART Between PC and STM32",BLUE,WHITE,24,0);

for(i=5;i<10;i++)  //清屏5~9行

{ LCD_ShowString(0,24*i,(u8 *)"                           ",WHITE,BLUE,24,0); }

sprintf(TempStr,"20%02d-%02d-%02d   %s",year,month,day,WeekName[WeekIndex-1]);

LCD_ShowString(40,24*7,TempStr,WHITE,BLUE,24,0);

while (1)

{

if(DateFresh==1)

    {

sprintf(TempStr,"20%02d-%02d-%02d %s",year,month,day,WeekName[WeekIndex-1]);

LCD_ShowString(40,24*7,TempStr,WHITE,BLUE,24,0);  //将日期和星期显示于LCD

DateFresh=0;

}

sprintf((char *)TempStr," %02d:%02d:%02d ",hour,minute,second);

LCD_ShowString(32*2+16,24*2+16,TempStr,RED,GREEN,32,0);  //将时间显示于LCD

/* USER CODE END WHILE */

}

}

本项目是在第9章基本定时器应用的基础上修改的,在保持时间数码管显示方式不变的基础上,将时间、日期、星期均显示于LCD,时间实时更新,日期和星期仅当串口接收到全部7个数据才进行更新。

程序首先增加定义了本项需要用到的变量,随后对所有外设进行初始化,紧接着调用串口接收单字节函数,启动数据接收工作,数据提取和处理是在回调函数中完成的。最后在主程序中进行显示信息处理。

 

在上述代码中多次使用了sprintf函数,其用法十分类似于printf函数,只不过printf是将字符串输出到标准设备显示器,而sprintf函数是将字符串输出到其第一个参数所指定的字符串指针。使用上述两个函数均需要包含stdio.h文件。

2.4 下载调试

编译工程,直到没有错误为止,下载程序到开发板,复位运行;上位机同步运行通信程序,待二者建立通信连接后,进行通信测试,检验实验效果。

3 上位机程序设计

因为本项目需要实现PC机与MCU之间的USART通信,所以除了编写微控制器端程序而外,还需要编写上位机控制程序,上位机程序是在个人计算机上编写的,其开发方法和使用平台形式各异,作者采用Visual Basic 6.0进行串口程序设计,其他开发平台与此类似。

首先在VB6.0软件中新建一个窗体Form1,并在窗体上添加串口通信控件MSComm1,串口组合列表框cboPort,状态指示图标shpCOM,串口状态标签cmdOpenCom,当前时间标签Label10,文本框Text1~Text7,发送数据标签Label2,接收数据标签Label3,退出按钮Command2,发送时间按钮Command4,定时器Timer1以及多个信息指示标签,创建完成界面如图3所示。

图3 串口通信窗体创建

上位机通信程序主要包括窗体载入,定时器中断,发送时间,串口接收等。项目功能不算复杂,代码量却不少,所以作者将项目工程及其源代码以本书配套资料形式提供,有上位机开发需求的读者,可自行下载查看。

4 串口通信调试

MCU与PC机USART通信调试有两种方法,一种方法是使用作者开发的上位机软件调试,另一种方法是使用第三方提供的串口调试助手调试。相比于作者2020年出版的教材中提供的上位机通信软件,新版本修补一些缺陷,功能也得到进一步增强,所以作者推荐第一种调试方法。

4.1 通信软件调试

VB6.0开发的程序可以通过“文件/单片机与PC机通信.exe”菜单,生成可执行文件“单片机与PC机通信.exe”,具体的文件名和工程名有关,并且可以修改,生成的可执行文件可以独立运行。

1串口通信控件注册

在很多串口通信软件中都会用到串口通信控件mscomm32.ocx,作者编写的上位机通信软件也不例外,该控件在有些Win7或Win10系统中没有注册,运行时会提示找不到控件错误,此时需要对控件进行注册。

(1)在微软官网下载或从本书配套资源获取控件mscomm32.ocx。

(2)将控件放到相应文件夹内,32位系统路径为:C:\Windows\System32,64位系统路径为:C:\Windows\Syswow64。

(3)然后在对应目录下找到cmd.exe文件,单击鼠标右键,以管理员身份运行(关键),在命令窗口输入regsvr32 mscomm32.ocx。

经过以上3步即可完成控件注册。

2.MCU与PC机通信

打开开发板电源,下载串口通信程序,并复位运行。在PC机上双击运行“单片机与PC机通信.exe”程序,操作界面如图4所示,单击“发送时间”按钮,Windows系统当前日期、星期和时间共7个数据发送至微控制器。MCU收到上位机数据之后,将时间信息动态显示于数码管和LCD,日期和星期信息更新于LCD显示屏。通信软件支持手动选择串口,设置波特率,同时提供了一个附加功能,即将系统当前日期、星期和时间信息实时转化为十六进制。

图4 串口通信调试

4.2 串口助手调试

事实上,很多同学可能没有掌握一门可视化编程语言,解决这一问题较好的方法是使用串口调试助手,需要说明的是各种版本串口调试助手略有差别,但大同小异,可以举一反三。

具体调试步骤如下:

(1)打开开发板电源,运行微控制器程序

(2)运行串口调试助手,并打开串口通信设置对话框,设置结果如图5所示,本项目全部采用默认设置,该步骤也可以跳过。

图5 串口通信设置

(3)串口调试选项设置,设置结果如图6所示,其中重要选项如红色框线所示。

图6 串口调试选项设置

(4)串口收发通信,采用两种方式进行实验,第一种方式将年、月、日、星期、时、分、秒共7个数值分开发送,第二种方式是所有数据一起发送(用空格分隔),操作过程如图7所示。若设定的时间为“2022-11-28 Monday 13:50:45”,则需要发送16进制数据“16 0B 1C 01 0D 32 2D”,此处要注意发送和接收的数据均为十六进制,且输入和显示均没有“0x”或“H”等附加格式。年份仅发送最后两位数字,例如2022年,发送的是数字22,微控制器仅当收到一组7个数据时才对日期和时间信息进行更新。

图7 串口收发数据界面

本节实验所需的上位机通信软件及所有配套资源可以通过如下方二维码获取:

5 使用教材

6 教程下载

STM32项目实例:串口时间同步.docx



人工智能科学与技术
分享教学成果 | 传播前沿科技| 推荐优秀图书
 最新文章