打印
[AT32F421]

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

[复制链接]
1247|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2023-7-19 09:04 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本文主要内容是按官方代码风格讲解如何使用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

使用特权

评论回复
沙发
tpgf|  楼主 | 2023-8-9 11:34 | 只看该作者
各位可以实测一下 看看跟自己的有什么不一样

使用特权

评论回复
板凳
tpgf|  楼主 | 2023-8-9 13:20 | 只看该作者
什么叫官方风格?就是官方的例程呗 是不是

使用特权

评论回复
地板
qcliu| | 2023-8-9 14:40 | 只看该作者
代码看起来非常的清晰 各个功能能分清楚

使用特权

评论回复
5
drer| | 2023-8-9 15:44 | 只看该作者
是使用查询法比较好还是使用中断法比较好呢

使用特权

评论回复
6
coshi| | 2023-8-9 16:16 | 只看该作者
为什么在while循环里边不加任何等待函数呢

使用特权

评论回复
7
kxsi| | 2023-8-9 16:49 | 只看该作者
这种单一串口单字节纯收发的 自己写就非常简单

使用特权

评论回复
8
wiba| | 2023-8-9 17:56 | 只看该作者
通过什么字符来识别是否发送新行了呢

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1747

主题

15155

帖子

10

粉丝