本文主要内容是按官方代码风格讲解如何使用USART1和USART2,意在为更多人提供指导性方案而不是按部就班照抄,也不是深入讲解。
USART串口可以说是用单片机必须一定肯定会用到的一个内置硬件,多数人用USART1做printf输出调试信息,其它串口才是用于功能业务层。
可是官方的串口例程写的真让人不舒服,14个串口示例,没有一个专门写单一串口的单字节纯收发,非得USART1和USART2互传,还要等凑够指定字节才发,很不友好。表面看上去好像不错哦,即介绍了USART1又同时介绍了USART2,并且还指导如何使用数组去批量收发内容,事实上呢?完全是多余的,实际更多人的串口都是独立功能,串口所连接的外设是啥就按啥来通信,往往不同类型,所以演示双工通信更有意义,一般学习阶段会在PC端用串口调试助手调试,期望“发了啥就返回啥”的目标,只有这个效果达到,剩下的就是软件功夫,所以说厂家研发人员是不是缺少实用经验?
【软硬件准备】
官方AT32F421C8T7开发板、官方BSP支持包。
开发板硬件如图:
软件包:AT32F421_Firmware_Library_V2.1.2
官方14个例程路径为:\AT32F421_Firmware_Library_V2.1.2\project\at_start_f421\examples\usart
开发环境:Keil5
【中断法USART1】
功能目标:使用USART1跟上位机PC端【串口调试助手】通信,上位机发给单片机的内容,带回车换行(\r\n),单片机全部返回给上位机。
特别注意:AT32F421的SSOP20型号,USART1只能做普通UART,也就是没有流控功能,USART2则是完整串口,同时只有SPI1,没有SPI2。48脚的型号都有。
大部分代码采用官方代码,即本文所谓的采用官方风格。
main.c文件:
uint8_t usart1_rx_buffer[100];
uint8_t usart1_length=0;
/*************************************************
* 功能描述: 配置USART1参数
* 参 数: 无
* 返 回: 无
*************************************************/
void usart_configuration(void)
{
gpio_init_type gpio_init_struct;
crm_periph_clock_enable(CRM_USART1_PERIPH_CLOCK, TRUE);//启用USART1时钟总线
crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);//启用GPIOB时钟总线,因为准备使用PB6,PB7引脚
gpio_default_para_init(&gpio_init_struct); //官方文档都这样设一次默认值,照做喽
/* 配置USART1的TX、RX引脚 */
/* -------------------------重要提醒-----------------------------*/
/* 官方文档这里没有初始化,我实际测试发现无法通信,因此这里补齐这堆代码,才能使用*/
/* 原因暂时不清楚,我是从这个坑爬出来的 */
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
gpio_init_struct.gpio_pins = GPIO_PINS_6 | GPIO_PINS_7;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init(GPIOB, &gpio_init_struct);
/* IOMUX也就是引脚的复用定义 */
/* 复用、映射是很常见的做法,可以参考《AT32F421数据手册_V2.01_CH.PDF》章节6.2.9 IOMUX功能输入/输出*/
/* PB6/PB7是MUX0,类似的还可以转移到PA9/PA10,MUX1... */
gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE6, GPIO_MUX_0);
gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE7, GPIO_MUX_0);
/* 中断配置,优先级0 */
nvic_irq_enable(USART1_IRQn, 0, 0);
/* USART1的参数配置,主要配置波特率,停止位,常见应用就下面这些参数 */
usart_init(USART1, 115200, USART_DATA_8BITS, USART_STOP_1_BIT);
usart_transmitter_enable(USART1, TRUE);
usart_receiver_enable(USART1, TRUE);
/* 启用接收中断,虽然上面配置了中断使能,但是这里还需要对中断类型进一步配置,中断有收有发,中断不同 */
usart_interrupt_enable(USART1, USART_RDBF_INT, TRUE);
/* 如果想识别发送完成,也可以启用这个发送中断,但是,很少这样用,因为发送完成不代表接收方也正常收到,
如果想保证收发完成,可以在通信协议上让接收方回复应答数据即可 */
//usart_interrupt_enable(USART1, USART_TDBE_INT, TRUE);
/* 启用USART1(确实有点繁琐,初学者可能会无法理解怎么那么多enable) */
usart_enable(USART1, TRUE);
}
/*************************************************
* 功能描述: USART1发送
* 参 数: p--发送的数组地址,length--要发送的长度
* 返 回: 无
*************************************************/
void usart1_send_byte(u8 *p,u16 length)
{
while (length--)
{
while(usart_flag_get(USART1, USART_TDBE_FLAG) == RESET)//等待上一个数据发出去
{
}
usart_data_transmit(USART1, *p++);//发一个字节
}
}
/*************************************************
* 功能描述: 入口函数
* 参 数: 无
* 返 回: 无
*************************************************/
int main(void)
{
system_clock_config();//初始化外部时钟
nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);//中断优先级分组
at32_board_init();//板载LED灯和按钮的初始化,开发板有3个灯和一个按钮
usart_configuration();//串口1初始化
while(1)
{
if(usart1_length>0)//当数据有长度才处理,实际应用时,可以用定义一个变量flag=1来判断
{
if(usart1_rx_buffer[usart1_length-1]=='\n')
{
usart1_send_byte(usart1_rx_buffer,usart1_length);
at32_led_toggle(LED2); //指示等闪烁一下
usart1_length=0;//接收长度清零,下一次接收将从数组的索引0开始
}
}
}
}
at32f421_int.c文件:(部分)
extern uint8_t usart1_length;
extern uint8_t usart1_rx_buffer[];
//..................
//其它中断回调函数忽略
//..................
/*************************************************
* 功能描述: USART1串口中断.
* 参 数: 无
* 返 回: 无
*************************************************/
void USART1_IRQHandler(void)
{
if(USART1->ctrl1_bit.rdbfien != RESET)//接收中断
{
if(usart_flag_get(USART1, USART_RDBF_FLAG) != RESET)
{
//每次读1个字节,注意:作为演示,没有对usart1_length进行长度校验
usart1_rx_buffer[usart1_length++]=usart_data_receive(USART1);
}
}
if(USART1->ctrl1_bit.tdbeien != RESET)
{
if(usart_flag_get(USART1, USART_TDBE_FLAG) != RESET)
{
//如果需要对发送中断识别则在此添加代码
}
}
}
【补充说明】
1、采用查询法也可以,只是不在中断中识别串口状态(usart_flag_get(USART1, USART_RDBF_FLAG))。
2、由于端口复用比较零活,跟实际产品设计有很大关系,所以如果学习过程一直无法通信,可能要参考《AT32F421数据手册_V2.01_CH.PDF》章节6.2.9 IOMUX功能输入/输出。
【中断法USART2】
写到这里,忽然觉得USART2其实跟USART1没什么差别,只是需要注意端口复用和引脚所在总线的不同而修改代码,大多数只是把数字1--->2,真的,没骗人。
以下列出USART2和USART1注意的引脚配置差异:
/* 启用时钟总线,这里跟USART1不同总线 */
crm_periph_clock_enable(CRM_USART2_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
/* 配置PA2/PA3引脚 */
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
gpio_init_struct.gpio_pins = GPIO_PINS_2 | GPIO_PINS_3;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init(GPIOA, &gpio_init_struct);
/* 端口复用配置,PA2/PA3是复用1,要注意看手册 */
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE2, GPIO_MUX_1);
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE3, GPIO_MUX_1);
【实测效果】
————————————————
版权声明:本文为CSDN博主「移动中的鸭子」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/duckmoving/article/details/131317616
|