CubeMX配置串口通讯(轮询方式)
- 前言
- 一、串口的介绍
- 二、实验过程
- 1.实验材料
- 2.STM32CubeMX配置PWM
- 3.代码实现
- 重载printf
- 轮询接收
- 4.编译烧录
- 5.硬件连接
- 6.实验结果
- 重载printf结果
- 串口轮询接收结果
- 总结
前言
本章介绍使用STM32CubeMX对串口进行配置的方法,因为我的最小系统没有其他外设,所以使用串口进行调试很重要,首先实现重载printf串口输出,然后实现串口通讯有三种方式:轮询,中断和DMA,接下来进行逐一介绍 ,本章接仅仅实现串口轮询接收和重载printf功能。
一、串口的介绍
通用同步异步收发器 (Universal Synchronous Asynchronous Receiver and Transmitter) 是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换。有别于 USART 还有一个 UART(Universal Asynchronous Receiver and Transmitter),它是在 USART 基础上裁剪掉了同步通信功能,只有异步通信。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是 UART。
串行通信一般是以帧格式传输数据,即是一帧一帧的传输,每帧包含有起始信号、数据信息、停止信息,可能还有校验信息。 USART 就是对这些传输参数有具体规定,当然也不是只有唯一一个参数值,很多参数值都可以自定义设置,只是增强它的兼容性。
- 波特率
对于异步通信,没有时钟信号进行同步,因此需要事先定好设备之间的波特率。 波特率表示每秒钟传送的码元符号的个数,所以它决定了数据帧里面每一个位的时间长度。两个要通信的设备的波特率必须相同,我们常见的波特率是 4800、9600、 115200 等。
- 数据帧格式
数据帧格式也需要约定好,串口通信的数据帧包括起始位、停止位、有效数据位以及校验位。
-
起始位和停止位:串口通信的一个数据帧是从起始位开始,直到停止位。数据帧中的起始位是由一个逻辑 0的数据位表示,而数据帧的停止位可以是 0.5、 1、 1.5 或 2 个逻辑 1 的数据位表示,通讯双方要必须设定一致,一般情况下我们设为1。
-
有效数据位:数据帧的起始位之后,就接着是数据位,也称有效数据位,这就是我们真正需要的数据,有效数据位通常会被约定为 5、 6、 7 或者 8 个位长。有效数据位是低位(LSB)在前,高位(MSB)在后,一般情况下我们设定为8位。
-
校验位:校验位可以认为是一个特殊的数据位。校验位一般用来判断接收的数据位有无错误,无校验是指数据帧中不包含校验位。一般情况下我们使用无校验位。
二、实验过程
1.实验材料
- STM32F103C6T6最小系统开发板
- ST-LINK V2
- 杜邦线
- 示波器
2.STM32CubeMX配置PWM
选择芯片stm32f103c6t6,新建工程
设置时钟源,最小系统外部晶振8Mhz,作为外部高速HSE时钟源。由于没有外接外部低速晶振,这里低速时钟源选择旁路时钟源。
配置时钟树,这里使用官方推荐的配置
stm32f103c6t6中有2个USART口,全都支持DMA方式,其中一个通讯速率可以达到4.5Mb/s,另个一通讯速录可达2.25Mb/s,都具有CTS和RTS信号引脚。
配置引脚功能,从datasheet中可以PA8-PA11可以USART1的复用引脚,因为我们这里使用异步模式,所以仅仅需要TX和RX两个引脚(即PA9,PA10),这样就可以在PA9管脚得到PWM输出。datasheet截图如下:
前面设置HSE时钟为72MHz;USART1的时钟挂载在APB2上
![
CubeMX配置如下:
USART1的参数配置如下,波特率115200,传输数据长度为8 Bit,奇偶检验无,停止位1.其他参数默认
![在这里插入图片描述](https://img-blog.csdnimg.cn/b9668266ed864849882aae9a530cbbcc.png)
SYS选项卡中Debug选项选择串口(这个选项可以设置,不会有影响)
Code Generator中设置只拷贝使用到的库,分离.c和.h文件
设置好项目名称和路径,点击GENERATE CODE即可,生成后使用keil5 IDE打开。
3.代码实现
重载printf
C语言中的标准库中所用的标准输入输出函数,默认的输出设备是显示器,要实现串口或LCD的输出,必须重新定义标准库函数里与输出函数相关的函数。例如:printf输出到串口,需要将fputc里面的输出指向串口(重定向),方法如下:只要自己添加一个int fputc(int ch, FILE *f)函数,能够输出字符就可以了。
在usart.c文件后面添加如下代码,代码中添加了#ifdef宏定义进行条件编译,如果使用GUNC编译,则PUTCHAR_PROTOTYPE 定义为int __io_putchar(int ch)函数,否则定义为int fputc(int ch, FILE *f)函数。
/* USER CODE BEGIN 0 */
#include "stdio.h"
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* USER CODE END 0 */
main函数如下,使用printf注意包含stdio.h头文件
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
printf("Hello STM32\r\n");
HAL_Delay(1000);
}
/* USER CODE END 3 */
}
轮询接收
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
unsigned char data[16]= {0};
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_UART_Receive(&huart1, data, 8, 0xffff);
HAL_UART_Transmit(&huart1, data, 8, 0xffff);
HAL_Delay(1000);
}
/* USER CODE END 3 */
}
这里实现轮询接收,HAL_UART_Receive(&huart1, data, 8, 0xffff);函数的最后一个参数是超时数值,这里设置为0xffff,也就是说函数的执行超时是65535ms。
在接收函数中,如果65535ms还未接收完8个字节的数据,则会向后执行,如果接收完则立即向后执行。发送数据的函数也一样。
轮询模式使用起来最简单,但是会占用大量的cpu时间,在等待接收和等待发送完毕时,cpu不能去做别的运算,只能在这里空等,运行的效率很低。
4.编译烧录
代码编译成功
注意:一定要记住勾选MicroLIB选项哦!
在下载前检查keil是否检测到ST-LINK,选择魔法棒进行设置
如图所示,已经检测到ST-LINK 下载器
点击load,自动下载固件
5.硬件连接
烧录器ST-LINK V2和最小系统板的连接如图所示: 四线连接 SWDIO,GND,SWCLK,和3.3V电源,PA9 PA10连接TTL转USB工具
6.实验结果
重载printf结果
串口轮询接收结果
总结
轮询方式:CPU不断查询IO设备,如设备有请求则加以处理。例如CPU不断查询串口是否传输完成,如传输超过则返回超时错误。轮询方式会占用CPU处理时间,效率较低。
中断方式:当I/O操作完成时,输入输出设备控制器通过中断请求线向处理器发出中断信号,处理器收到中断信号之后,转到中断处理程序,对数据传送工作进行相应的处理。
直接内存存取技术(DMA)方式:所谓直接传送,即在内存与IO设备间传送一个数据块的过程中,不需要CPU的任何中间干涉,只需要CPU在过程开始时向设备发出“传送块数据”的命令,然后通过中断来得知过程是否结束和下次操作是否准备就绪。
本章介绍使用STM32CubeMX对串口进行配置的方法,首先实现重载printf串口输出,然后实现串口轮询接收功能,接下来介绍中断方式和DMA方式。