目录
1. 实验任务
利用STM32CubeMX,创建MDK工程,使用中断方式,实现串口接收数据,然后在转发到串口。
本实验是串行通信的第二部分,基础知识已在上一篇讲述:
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
{
// 发送一个字节数据到串口USARTx
HAL_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");
else
printf("您编译工程时选择的是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下载:
---------------------------------------------------------------------------
本实验是串行通信的第二部分,基础知识已在上一篇讲述:
基础篇007. 串行通信(一)--阻塞方式发送接收https://blog.youkuaiyun.com/qcmyqcmy/article/details/130674914