USART 常用来实现控制器与电脑之间的数据传输。这使得我们调试程序非常方便,比如我们可以把一些变量的值、函数的返回值、寄存器标志位等等通过 USART 发送到串口调试助手,这样我们可以非常清楚程序的运行状态,当我们正式发布程序时再把这些调试信息去除即可。
本实例要实现开发板与电脑通信,在开发板上电时通过 USART 发送一串字符串给电脑,然后开发板进入中断接收等待状态,如果电脑有发送数据过来,开发板就会产生中断,我们在中断服务函数接收数据,并马上把数据返回发送给电脑。
一、使能RX、TX引脚GPIO与USART时钟
bsp_usart的创建同前面外设一样,需要c文件和h文件,包含stm32f10x、防止重复定义的宏定义等。此外,需要对USART的RCC时钟、波特率、RXTX的引脚、GPIO的RCC时钟、USART中断进行一些宏定义,增加程序可读性的同时,提高可移植性。
此处注意USART的PA9、PA10对应RXTX,开了GPIOA的时钟后,记得还有USART1的时钟要开,USART和GPIO一样都是外设,用外设就要开外设的时钟,像前面用中断配置信号源就要用到AFIO,要开AFIO的时钟。
二、配置USART、优先级分组NVIC配置
USART输入输出RX、TX配置
模式配置:输出TX因为要直接输出不经过寄存器,而是直接从片上外设输出,需要复用推挽输出;输入RX要读取因此用浮空输入;
引脚配置:USART1对应的是PA9、PA10;
配置好后调用GPIO的初始化函数即可。
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
USART通信参数配置
波特率 115200,字长为 8,1 个停止位,没有校验位,不使用硬件流控制,收发一体工作模式,然后调用 USART 初始化函数完成配置。
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
// 配置校验位
USART_InitStructure.USART_Parity = USART_Parity_No ;
// 配置硬件流控制
USART_InitStructure.USART_HardwareFlowControl =
USART_HardwareFlowControl_None;
// 配置工作模式,收发一起
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 完成串口的初始化配置
USART_Init(DEBUG_USARTx, &USART_InitStructure);
NVIC配置、串口接收中断使能以及串口使能
USART 接收中断,需要配置 NVIC,这里调用 NVIC_Configuration 函数完成配置。配置完 NVIC 之后调用 USART_ITConfig 函数使能 USART 接收中断。
最后调用 USART_Cmd 函数使能 USART,这个函数最终配置的是 USART_CR1 的 UE 位,具体的作用是开启 USART 的工作时钟,没有时钟那 USART 这个外设自然就工作不了
// 串口中断优先级配置
NVIC_Configuration();
// 使能串口接收中断
USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
// 使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
三、字符发送
上电发送字符
在bsp_usart.c文件中,编写一个数据发送函数,Usart_SendByte 函数用来在指定 USART 发送一个 ASCLL 码值字符,它有两个形参,第一个为USART,第二个为待发送的字符。它是通过调用库函数 USART_SendData 来实现的,并且增加了等待发送完成功能。通过使用 USART_GetFlagStatus 函数来获取 USART 事件标志来实现发送完成功能等待,它接收两个参数,一个是 USART,一个是事件标志。这里我们循环检测发送数据寄存器为空这个标志,当跳出 while 循环时说明发送数据寄存器为空这个事实。
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
3 {
4 /* 发送一个字节数据到 USART */
5 USART_SendData(pUSARTx,ch);
6
7 /* 等待发送数据寄存器为空 */
8 while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
9 }
发送字符功能函数完成后,在主程序中先调用USART配置函数进行初始化,后调用此字符发送函数即可完成功能。
nt main(void)
{
USART_Config();
Usart_SendByte(DEBUG_USARTx , 100);
}
返回8位的数组
同样在bsp_usart.c中声明一个传数组函数,参数位USART结构体类型指针、数组名、数量,用for循环执行上面的发送字节函数。通过USARAT_FlagStatus函数获取状态寄存器SR的TC状态,若为0表明没传输完成,为1则结束等待。
注:单个字符发送使用SR的TXE判断发送是否完成,多字符发送用SR的TC判断是否完成发送。
void Usart_SendArray(USART_TypeDef* pUSARTx, uint8_t *array, uint8_t num)
{
uint8_t i;
for( i = 0; i < num; i++)
{
Usart_SendByte(pUSARTx, array[i]);
}
while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET );
}
返回字符串
在c中,字符串是以char数组形式储存,当然返回字符串可以用上面返回数组的方式,这里用另一种do while循环实现。
void Usart_SendString(USART_TypeDef* pUSARTx, char *str)
{
unsigned int k=0;
do
{
Usart_SendByte(pUSARTx, *(str + k));
k++;
}while(*(str + k) != '\0');
/* 等待发送完成 */
while (USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}
printf函数重定向到串口
重定向:c语言中标准io函数都是使用标准io流(stdin,stdout,stderr)进行输入输出的,在默认情况下,stdin是读取键盘输入,stdout和stderr是输出到屏幕。一般来说,io设备被看做文件,所以这些io流实际上都是FILE*,也就是说,他们可以指向其他文件,这就是重定向的功能,可以将信息打印到串口。
因为printf函数内部就是调用的fputc,因此在bsp_usart.c中定义fputc,在fputc中使用上面
的USART_SendData函数将信息从串口发送,这样在主函数中直接printf("Hello")即可在串口打印内容。
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到串口 */
USART_SendData(DEBUG_USARTx, (uint8_t) ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
return (ch);
}
参考
https://doc.embedfire.com/mcu/stm32/f103zhinanzhe/std/zh/latest/index.html