串口通信实验

Keil初见

警告
建议严格按照群文件内的安装方法来安装Keil,否则容易出现无法识别烧写器/无法烧录等问题
B就是这样踩了坑的

  1. 运行MDK529.exe安装Keil本体
  2. 关闭Pack Installer,在主界面找到File -> License Management
  3. 复制License Management窗口上的CID
  4. 打开Keygen.exe,在CID项粘贴,Target项选择ARM,点击Generate

    如果发现这个程序没了,说明你杀毒软件没关(比如Microsoft Defender)

  5. 将生成的代码复制到Keil的LIC处,点击Add LIC完成破解
  6. 打开文件夹MDK芯片固件包,运行三个pack安装包

打开Keil,可以看到一些常用的按键:

  1. File 打开/关闭/保存 文件, 查看许可证状态
  2. 编译(Build) c语言在运行前必须先经过编译才能够运行
  3. 下载(Download) 将编译后的代码下载到单片机中。如果你修改了一段代码而没有再次编译,下载时会下载之前已经编译过的代码
  4. 魔法棒工具(Options for Target) 对当前的项目进行设置
  5. Debug 必须在连接了单片机的情况下才可用;可以运行代码并监看数据
  6. 设置(Configuation) 能调整以什么编码方式打开文件(比如UTF-8),界面颜色等等
  7. 文件资源树 一般只关注Application/User/Core中的文件(其他文件都是CubeMX生成的快说谢谢CubeMX)
    Keil界面常用按键

先点击魔法棒工具,转到Debug,对我们所使用的烧写器进行配置
将右上方的USE项改成CMSIS-DAP Debugger,然后点击旁边的Settings
如果可以成功连接到烧写器,那么弹出的新窗口的SWDIO处会显示一串地址;否则,会像图中显示No Debug Unit Device found
在新窗口选择上方的Flash Download,点亮选项

Reset and run

再点击下方的按钮ADD,添加STM32H750xx
Options for Target

设置完以上的选项后,关闭魔法棒界面,点击编译,编译完成后点击下载
不出意外的话Keil会在左下方显示下载进度条,下载完成后进度条会自己消失

完成代码的烧写之后就可以点击Debug来调试运行代码了
调试模式中原先的编译键会替换为Reset,Start,Stop等键,分别代表程序停止并重置,开始/继续运行,暂停运行
可以在代码中选中变量,右键将其加入Watch中来监看变量的值

中断

理论介绍

了解了中断的理论之后,来看看代码中是怎么体现,实现中断的

正常进程

处在main()中的死循环while(1)中的代码就是单片机运行的正常进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// main.c
int main(void)
{//while外的这些函数是CubeMX自动生成的,不要动就是了
MPU_Config();
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM14_Init();
MX_UART5_Init();
while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
// 此处写入的代码就是正常进程中运行代码
}
/* USER CODE END 3 */
}

值得注意的是,用户(也就是我们这些编写者)只能在注释/* USER CODE BEGIN n *//* USER CODE END n */之间填写代码
如果在这个注释以外的地方编写代码,Keil在编译时会将注释外的代码删除(来防止你改了什么东西导致代码不能正常跑)
也就是说你可以简单的理解为:USER CODE 3中的代码就是正常进程运行的代码

定时器中断

处在 定时器中断回调函数HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)中的代码即为触发定时器中断会执行的代码
在项目的代码库中,这个函数以弱函数的方式被定义
因此要使用它,我们需要自己再定义一次,并填入我们想要的功能
一定要写在 USER CODE 0中,不然会被清除
另外,要使用定时器中断,应该在USER CODE 2中写上HAL_TIM_Base_Start_IT(&htim_n);来打开对应计时器的计时器中断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//main.c
/* USER CODE BEGIN 0 */
//定时器中断回调函数,每1ms进入一次
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
//触发定时器中断时会执行的代码
}
/* USER CODE END 0 */

int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
MPU_Config();
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM14_Init();
MX_UART5_Init();
/* USER CODE BEGIN 2 */
//打开定时器中断
//不要忘了开哦
HAL_TIM_Base_Start_IT(&htim14);
/* USER CODE END 2 */

/* Infinite loop */
while (1)
{
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}

定时器中断回调函数执行的频率由你在CubeMX中的设置决定
指针htim指向触发此回调函数的定时器,比如在这个项目中,我们设置打开的定时器是TIM14,那么就有

1
2
//在回调函数里可以利用这一点来判断触发中断回调函数的是哪一个定时器
htim->Instance == TIM14

可以通过加入一个cnt变量来得到当前的时间

1
2
3
4
5
uint32_t cnt=0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
cnt++;
}

串口收发通信中断

处在 串口接收通信中断函数HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)中的代码即为触发串口收发通信中断时执行的代码
当一个串口的tx端向另外一个串口的rx端发送一串uint8_t的数据,接收到数据的串口会调用一次串口接收通信中断函数
同样的,在项目的代码库中这个函数以弱函数的方式被定义,因此我们在USER CODE 0中可以重新定义一个串口接收通信中断函数
也不要忘了要在USER CODE 2中打开中断接收HAL_UART_Receive_IT(&huart_n,rxbuffer,sizeof(rxbuffer));
其中rxbuffer是用来接收数据的数组,变量类型必须是uint8_t

除了要写接收端的代码,我们当然还要写发送端的代码
USER CODE 3中写数据发送函数HAL_UART_Transmit_IT(&huart_n,txbuffer,sizeof(txbuffer));来发送函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/* USER CODE BEGIN 0 */
//数据发送数组
uint8_t txbuffer[2]={0,1};
//数据接收数组
uint8_t rxbuffer[2];
//串口接收中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
// 接收中断的代码

//如果需要再次接收数据,那么就需要再次打开中断接收
HAL_UART_Receive_IT(huart,rxbuffer,sizeof(rxbuffer));
}

/* USER CODE END 0 */

int main(void)
{
MPU_Config();
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_UART5_Init();
/* USER CODE BEGIN 2 */
//(打开)中断接收
HAL_UART_Receive_IT(&huart5,rxbuffer,sizeof(rxbuffer));
/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
txbuffer[0] = 5;
//数据发送
HAL_UART_Transmit_IT(&huart5,txbuffer,sizeof(txbuffer));
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}

串口接收中断回调函数中的指针huart指向接收到数据并触发此次中断的串口
利用这个指针可以判断到底是哪个串口接收到了数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//数据发送数组
uint8_t txbuffer[2]={0,1};
//数据接收数组
uint8_t rxbuffer[2];
//记录串口通信状态
uint8_t connect[7] = {0};
//清除connect中记录的状态
void clear_connect(void)
{
for(int i=0;i<7;i++)
{
connect[i] = 0;
}
}

//串口接收中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART2)
{
clear_connect();
connect[1] = 1;
}
else if(huart->Instance == USART3)
{
clear_connect();
connect[2] = 1;
}
else if(huart->Instance == UART4)
{
clear_connect();
connect[3] = 1;
}
else if(huart->Instance == UART5)
{
clear_connect();
connect[4] = 1;
}
else if(huart->Instance == USART6)
{
clear_connect();
connect[5] = 1;
}
else if(huart->Instance == UART7)
{
clear_connect();
connect[6] = 1;
}
connect[rxbuffer[0]-1] = 1;
HAL_UART_Receive_IT(huart,rxbuffer,sizeof(rxbuffer));
}

—END—

参考:
stm32H750工程配置,定时器中断与串口收发通信(中断与DMA)测试


串口通信实验
http://chose-b-log.netlify.app/串口通信实验/
作者
B
发布于
2025年10月16日
许可协议