目录
1. 实验任务
2. 硬件原理
3. 利用STM32CubeMX创建MDK工程
3.1 STM32CubeMX工程创建
3.2 配置调试方式
3.3 配置时钟电路
3.4 配置GPIO
3.5 配置串口参数
3.6 配置时钟
3.7 项目配置
4. 串行通信实验
4.1 UART串口printf,scanf函数串口重定向
4.2 开启中断
4.3 中断回调函数
4.4 main()函数修改
5.调试与验证
6.总结
1. 实验任务
利用STM32CubeMX,创建MDK工程,使用中断方式,实现串口接收数据,然后在转发到串口。
本实验是串行通信的第二部分,基础知识已在上一篇讲述:
基础篇007. 串行通信(一)--阻塞方式发送接收
2. 硬件原理
1) 指示灯 DS0、 DS1分别连接到PA8和PD2.
2) 3个按键: KEY0、 KEY1 和 KEY_UP,分别连接到PA13、PA15、PA0。
3. 利用STM32CubeMX创建MDK工程
3.1 STM32CubeMX工程创建
选择File下的New Project:
选择芯片类型(本文为stm32f103RBt6),选择下边的item,然后Start Project:
3.2 配置调试方式
点击左侧的System Core下的SYS,将Debug设置为Serial Wire:
3.3 配置时钟电路
配置时钟:将RCC下的HSE设置为Crystal/Ceramic Resonator
3.4 配置GPIO
结合开发版的硬件电路,进行GPIO设置
选择GPIO,依次将PA8、PD2设置为GPIO_Output,3个按键对应的IO口设置为输入,KEY0、 KEY1 和 KEY_UP,分别连接到PA13、PA15、PA0。
KEY0(PA13)是低电平有效的,需要将PA13设置为GPIO_Input,在STM32内部设置上拉:
各IO口设置后的参见见上图。
3.5 配置串口参数
USART1参数配置:
在 Connectivity 中选择 USART1 设置,并选择 Asynchronous 异步通信。
波特率为 115200 Bits/s。传输数据长度为 8 Bit。奇偶检验 None,停止位 1 ,接收和发送都使能。
配置串口中断:
检查NVIC
3.6 配置时钟
结合开发版的硬件电路,选择Clock Configuration,做如下配置:
结合开发版的硬件电路,选择Clock Configuration,做如下配置:
3.7 项目配置
在Project Manager下的Project中设置工程名称和工程路径,并选择编译软件。取消勾选Use lastest available version,选择其他版本:
代码生成设置:
在Code Generate中选择第二个,然后Generate Code,即生成代码:
可以打开MDK工程编辑了。
4. 串行通信实验
4.1 UART串口printf,scanf函数串口重定向
这部分内容与上一节相同。
在学习C语言时我们经常使用C语言标准函数库输入输出函数,比如printf、scanf、getchar等。为让开发板也支持这些函数需要把USART发送和接收函数添加到这些函数的内部函数内。
在C语言HAL库中,fputc函数是printf函数内部的一个函数,功能是将字符ch写入到文件指针f所指向文件的当前写指针位置,简单理解就是把字符写入到特定文件中。
fgetc函数与fputc函数非常相似,实现字符读取功能。在使用scanf函数时需要注意字符输入格式。
注意:
使用fput和fgetc函数达到重定向C语言HAL库输入输出函数必须在MDK的工程选项把“UseMicroLIB”勾选上,MicoroLIB是缺省C库的备选库,它对标准C库进行了高度优化使代码更少,占用更少资源。
为使用printf、scanf函数需要在文件中包含stdio.h头文件。
在usart.c文件的user code 0 区域内:
输入如下内容:
//! 用户添加的代码--->>>#include "stdio.h"// 加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1 // 方便调试,改为“#if 0”不要以下功能
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) // AC6编译器
// 加入以下代码,支持printf函数,而不需要选择use MicroLIB
__asm(".global __use_no_semihosting\n\t");
void _sys_exit(int x)
{ // 定义_sys_exit()以避免使用半主机模式x = x;
}
/* __use_no_semihosting was requested, but _ttywrch was */
void _ttywrch(int ch)
{ch = ch;
}
FILE __stdout;
#elif defined(__CC_ARM) // AC5编译器
// #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#pragma import(__use_no_semihosting)
// 标准库需要的支持函数
struct __FILE
{int handle;
};
FILE __stdout;
void _sys_exit(int x)
{x = x;
}
#endif
//重定义fputc函数
// int fputc(int ch, FILE *f)
//{
// while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
// USART1->DR = (u8) ch;
// return ch;
// }// 重定向 c 库函数 printf 到串口 USARTx,重定向后可使用 printf 函数
// 重定向 c 库函数 scanf 到串口 USARTx,重写向后可使用 scanf、 getchar 等函数
// 使用 printf、 scanf 函数需要在文件中包含 stdio.h 头文件。
#if defined(__GNUC__) && !defined(__clang__)
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#define GETCHAR_PROTOTYPE int __io_getchar(FILE *f)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#define GETCHAR_PROTOTYPE int fgetc(FILE *f)
#endif
// 改写fputc函数。【有多个串口是,需要修改下面的代码】
PUTCHAR_PROTOTYPE
{// 发送一个字节数据到串口USARTxHAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000); // 发送数据到串口return ch;
}GETCHAR_PROTOTYPE
{uint8_t ch = 0;// 等待串口输入数据// while (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) == RESET);HAL_UART_Receive(&huart1, &ch, 1, 0xffff);return ch;
}
#endif//! 用户添加的变量定义--->>>
uint8_t Rx1_Byte; // 定义串口1接收字节寄存器
uint8_t Rx1_Buff[256]; // 定义串口1接收缓冲器,此处默认最大缓存为256字节。
uint16_t Rx1_Count; // 定义串口1接收计数器
4.2 开启中断
在usart.c文件中的函数void MX_USART1_UART_Init(void)内:
//! 用户添加的代码--->>>HAL_UART_Receive_IT(&huart1, &Rx1_Byte, 1); // 开启接收中断:标志位UART_IT_RXNE,并设置接收缓冲以及接收缓冲接收最大数据量//__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE); //开启接收中断
4.3 中断回调函数
在usart.c文件中末尾的:
输入如下内容:
//! 用户添加的代码--->>>
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if (huart->Instance == USART1) // 如果是串口1{Rx1_Buff[Rx1_Count++] = Rx1_Byte; //接收到的数据存入缓存if (Rx1_Byte == 0x0a) // 要求发送数据到串口时,以\n结束【0x0a是"\n"换行符NewLine】{//转发接收到的数据到串口while(HAL_UART_Transmit(&huart1,Rx1_Buff,Rx1_Count-1,1000)!=HAL_OK);Rx1_Count = 0; //计数器复位}if (Rx1_Count >= 254){Rx1_Count = 0;}while(HAL_UART_Receive_IT(&huart1,&Rx1_Byte,1)!=HAL_OK); // 一次处理完成之后,重新开中断}
}
注意:根据上述串口数据接收程序,发送数据时,需使用“\n”结束,否则会一直等待发送,看不到输出结果。
可以在主程序中使用printf("\n");发送数据,也可以使用串口调试助手。
4.4 main()函数修改
#include "stdio.h"
uint16_t times = 0;
if(times%500==0){if (__ARMCC_VERSION > 6000000)printf("您编译工程时选择的是ARMCLANG(AC6)编译器\n");elseprintf("您编译工程时选择的是ARMCC(AC5)编译器\n");printf("\n");printf("实验内容:串口采用中断方式接收数据,然后转发输出。\n");printf("请按下开发板中的Key0按键,或者用串口调试助手发送数据!\n");printf("重要提示:使用野人家园串口助手时,请在发送设置中选择自动发送附加位,校验算法需选择”无“,指令结束符处填写:”0A“\n\n");}if(!HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)){printf("您刚才按下了key0!\n");HAL_Delay(200);}times++;HAL_Delay(10);}
5.调试与验证
程序编译通过后,可将其下载到开发板进行验证
实验需要使用串口调试助手验证。
串口调试助手: 下载地址
打开串口调试助手,实验结果如下:
重要提醒:串口调试助手必须按如下方法配置,才能接收上述代码发送的数据!!!
发送“\n”字符作为结束符的设置方法:
提示:“\n”的ASCII为“0xa”。
按下发送按钮后,显示如下:
按下开发板的Key0按键后,显示如下:
6.总结
暂无
----------------------------------------------------------------------------
如果你需要AC5编译器,请参考如下博文安装设置:
Keil MDK5.37以上版本自行添加AC5(ARMCC)编译器的方法_armcc下载:
Keil MDK5.37以上版本自行添加AC5(ARMCC)编译器的方法_armcc下载_笑春风oO的博客-CSDN博客【对安装AC5后,编译时提示找不到序列号的错误,文中有提示的解决方法】从MDK5.37开始,AC5(ARMCC)编译器不再默认安装,需要独立安装。路径、字符等安装问题,都可能引起AC5的编译错误。下面给出不用爬坑的方法。下面是安装步骤:下载AC5(ARMCC)编译器:1. 官方页面(可能下载不成功)2.网盘下载百度网盘:链接:https://pan.baidu.com/s/1ND3vKLzqxanWVP304txRtQ ,提取码:idvc...........................https://blog.csdn.net/qcmyqcmy/article/details/125814461
---------------------------------------------------------------------------
本实验是串行通信的第二部分,基础知识已在上一篇讲述:
基础篇007. 串行通信(一)--阻塞方式发送接收https://blog.csdn.net/qcmyqcmy/article/details/130674914