AT32F421专题--USART串口,官方风格

【啰嗦两句】

    本文主要内容是按官方代码风格讲解如何使用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);

【实测效果】

 

 【跟STM32的一些差异】

    其实我发现绝大多数网上的例程都是按STM32风格写的,并且用的BSP固件库也不知道从哪里来的,由于我全程按官方的库来写,也就不知道如何按STM32风格来写,其实那些例程对我有点误导,走了一些弯路。主要区别有大小写的不同,我更喜欢STM32的驼峰写法,个人也比较推荐。另外官方风格中断函数还要多判断一次USART1->ctrl1_bit.rdbfien != RESET,也不知道为了什么。

    比较明显的感觉STM32的库调用更清晰。官方几个enable,而STM32区分enable、cmd、init等,更直观贴切。

   

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值