前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >蓝桥杯嵌入式之USART讲解

蓝桥杯嵌入式之USART讲解

作者头像
用户5935416
发布2019-08-01 10:03:45
1.2K0
发布2019-08-01 10:03:45
举报
文章被收录于专栏:IT界的小白帽IT界的小白帽

USART是通用同步/异步串行接收/发送器。大多数通过USB与电脑进行通信,也是比较好的调试方法。当程序出现长时间找不出错误时,可以在相应的变量后面通过串口将数值发送到电脑端,方便对变量的值进行跟踪查看,快速定位出现错误的地方。

在比赛的时候也可以通过USART来定位错误的地方,以免在比赛的时候由于紧张而不知所措。

USART的使用要用到stm32f10x_usart.c和stm32f10x_usart.h,可以在CT117E嵌入式竞赛板V1.1\CT117E嵌入式竞赛板\STM32_MCU\stm32f10x_stdperiph_lib\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\STM32F10x_StdPeriph_Driver\src中复制.c文件,在inc文件夹里复制.h文件。

USART的初始化讲解

USART的初始化函数为

代码语言:javascript
复制
/**
  * @说明     USART2 相关GPIO和工作模式配置
  * @参数     None 
  * @返回值   None
  */
void USART_Config(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
  
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

    //配置USART2 TX引脚工作模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    //配置USART2 RX引脚工作模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    //串口2工作模式配置
    USART_InitStructure.USART_BaudRate = 19200;
    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(USART2, &USART_InitStructure);
    USART_Cmd(USART2, ENABLE);
}

例程中使用的的是USART2,可以在CT117E嵌入式竞赛板V1.1\CT117E嵌入式竞赛板\STM32_MCU\stm32f10x_stdperiph_lib\STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\USART\DMA_Interrupt中main.c文件的代码进行修改。修改方法如下:

1、时钟初始化

原函数为

代码语言:javascript
复制
/**
  * @brief  Configures the different system clocks.
  * @param  None
  * @retval None
  */
void RCC_Configuration(void)
{   
  /* Enable GPIO clock */
  RCC_APB2PeriphClockCmd(USARTy_GPIO_CLK | USARTz_GPIO_CLK | RCC_APB2Periph_AFIO, ENABLE);

#ifndef USE_STM3210C_EVAL
  /* Enable USARTy Clock */
  RCC_APB2PeriphClockCmd(USARTy_CLK, ENABLE); 
#else
  /* Enable USARTy Clock */
  RCC_APB1PeriphClockCmd(USARTy_CLK, ENABLE); 
#endif
  /* Enable USARTz Clock */
  RCC_APB1PeriphClockCmd(USARTz_CLK, ENABLE);  
}

可以对USARTy 的初始化函数进行修改,将USARTy_CLK修改为RCC_APB1Periph_USART2。其中参数RCC_APB1Periph_USART2需要在stm32f10x_rcc.h的文件中查找。需要注意的是要用函数RCC_APB2PeriphClockCmd初始化GPIOA的时钟,这是根据电路图进行确定的。

2、端口模式、速度等的初始化

(部分)原代码为

代码语言:javascript
复制
 /* Configure USARTy Rx as input floating */
  GPIO_InitStructure.GPIO_Pin = USARTy_RxPin;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(USARTy_GPIO, &GPIO_InitStructure);
  
   /* Configure USARTy Tx as alternate function push-pull */
  GPIO_InitStructure.GPIO_Pin = USARTy_TxPin;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(USARTy_GPIO, &GPIO_InitStructure);

其中变量USARTy_RxPin和参数USARTy_GPIO要根据电路图进行确定。

3、串口工作模式的配置

源代码为

代码语言:javascript
复制
  */
  USART_InitStructure.USART_BaudRate = 9600;
  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;
  
    /* Configure USARTy */
  USART_Init(USARTy, &USART_InitStructure);
  
  /* Enable the USARTy */
  USART_Cmd(USARTy, ENABLE);

其中两函数的参数USARTy改为USART2即可。需要注意的是源代码的两个函数的中间的代码没有使用到。

USART发送数据讲解

代码语言:javascript
复制
/**
  * @说明     USART2字符串发送函数
  * @参数     str: 指向字符串的指针
  * @返回值   None
  */
void USART_SendString(int8_t *str)
{
    uint8_t index = 0;
    
    do
    {
        USART_SendData(USART2,str[index]);
        while(USART_GetFlagStatus(USART2,USART_FLAG_TXE) == RESET);
        index++;        
    }
    while(str[index] != 0);  //检查字符串结束标志
    
}

函数USART_SendData定义在stm32f10x_usart.c中的第592行,声明在stm32f10x_usart.h中的第378行。其第一个参数USARTx由初始化函数确定的哪个USART口,一般用USART2。第二个参数Data为一个整数,是需要发送的两字节整数。如果要发送小数的话,需要将小数点作为一个数发送(一般不会这样考)。

发送完一个整数或字符后,需要判断该参数是否发送成功。函数USART_GetFlagStatus是判断标志量为什么。该函数定义在stm32f10x_usart.c中的第874行,声明在stm32f10x_usart.h中的第390行。

其中函数USART_GetFlagStatus的第一个参数USART2就是确定是判断哪个USART口。第二个参数是根据整体的代码功能确定,如果是判断USART的发送是否成功的话,可以用IS_USART_FLAG;如果是判断USART的接收是否成功的话,可以用USART_FLAG_RXNE。其所有的取值可以根据函数IS_USART_FLAG的定义位置查看。

当判断出函数的某一个字符为数值0时,表明数组的每一个元素发送完毕。

最后需要注意的是,发送函数的参数int8_t *str为数组的指针(首地址)。

利用官方提供的软件进行通信

USART的通信软件有很多,但是都离不开这几个功能。端口的链接、波特率、校验位、数据位、停止位等的设置;输入\输出窗口;发送和清除按钮等。官方提供的串口通信软件的界面如下(软件在Tools下的AccessPort文件夹下)

点击红色按钮可进入常规设置界面

需要注意的是串口的设置不能自动设置,需要手动设置。查看方法为右击“我的电脑”—>“管理”—>“设备管理器”中查看(每次查看的结果不唯一)

成功接收后会在输出显示器中进行显示

USART接收显示数据讲解

因为USART的数据接收是不定时的,所以需要利用中断进行实时的监控。中断的设置代码如下

代码语言:javascript
复制
/**
  * @说明     配置中断向量控制器
  * @参数     None
  * @返回值   None
  */
void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  /* Enable the RTC Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

源代码(比赛时的例程代码)为

代码语言:javascript
复制
/**
  * @brief  Configures the nested vectored interrupt controller.
  * @param  None
  * @retval None
  */
void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  /* Configure the NVIC Preemption Priority Bits */  
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
  
  /* Enable the USARTy Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = USARTy_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

  /* Enable the USARTz Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = USARTz_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

只需把NVIC_PriorityGroup_0改为NVIC_PriorityGroup_1。原因可以看下图,看不懂的可以看正点原子的视频。

利用对USARTz的中断配置进行设置,需要将USARTz_IRQn改为USART2_IRQn。

接下来就是对中断函数的内容进行编写。中断函数都放在stm32f10x_it.c中,所以可以将同文件夹下的stm32f10x_it.c中的中断函数复制到程序的stm32f10x_it.c的文件中。正确的配置代码如下

代码语言:javascript
复制
/**
  * @brief  This function handles PPP interrupt request.
  * @param  None
  * @retval None
  */
void USART2_IRQHandler(void)
{
  uint8_t temp;
  
  if(USART_GetITStatus(USART2,USART_IT_RXNE) != RESET){
    USART_ClearITPendingBit(USART2,USART_IT_RXNE);    
    temp = USART_ReceiveData(USART2);
    if((temp == 'x') || (RXCUNT == 20)){
      RXCUNT = 0;
      RXOVER = 1;  //接收完成标志位置位
      USART_ITConfig(USART2,USART_IT_RXNE,DISABLE);
    }
    else{
      USART_RXBUF[RXCUNT] = temp;
      ++RXCUNT;      
    }
  }
}

比赛给的代码为

代码语言:javascript
复制
/**
  * @brief  This function handles USARTy global interrupt request.
  * @param  None
  * @retval None
  */
void USARTy_IRQHandler(void)
{
  if(USART_GetITStatus(USARTy, USART_IT_RXNE) != RESET)
  {
    /* Read one byte from the receive data register */
    RxBuffer1[RxCounter1++] = USART_ReceiveData(USARTy);

    if(RxCounter1 == NbrOfDataToRead1)
    {
      /* Disable the USARTy Receive interrupt */
      USART_ITConfig(USARTy, USART_IT_RXNE, DISABLE);
    }
  }
  
  if(USART_GetITStatus(USARTy, USART_IT_TXE) != RESET)
  {   
    /* Write one byte to the transmit data register */
    USART_SendData(USARTy, TxBuffer1[TxCounter1++]);

    if(TxCounter1 == NbrOfDataToTransfer1)
    {
      /* Disable the USARTy Transmit interrupt */
      USART_ITConfig(USARTy, USART_IT_TXE, DISABLE);
    }    
  }
}

对比来看,需要修改的地方还是很多的。第一个if语句是可以利用的,只需在里面添加清除接收标志,然后判断接收到的字符或字符数量是否满足要求,即是否满足接收完成的要求。如果满足要求,则将接收标志位置位,在其它函数中判断该位是否为1。若为1,则表示接收完成,否则表示没有接收完成。如果没有符合接收完成的条件,则将接收到的字符复制到数组里。需要注意的是,因为将接收到的字符保存在数组里,所以两个文件需要共用一个数组,方便对数组的调用;也共用一个变量,表示接收完成的标志;在中断函数中需要有一个变量控制字符接收的个数,例程中是用全局变量来控制的。

在stm32f10x_it.c文件中对这些变量的声明或定义是

代码语言:javascript
复制
extern uint8_t USART_RXBUF[20];   //接收缓冲区
uint8_t RXOVER = 0;              //接收完成标志位
uint8_t RXCUNT = 0;             //字符个数控制

在调用函数或main.c文件中对这些变量的声明或定义是

代码语言:javascript
复制
uint8_t USART_RXBUF[20];
extern uint8_t RXOVER;

这些变量的名字不需刻意记忆,完全可以根据自己的爱好(不要太俗了也不能与其它变量重名)定义。

对接收到的数据进行显示的代码为

代码语言:javascript
复制
if(RXOVER == 1){
      LCD_ClearLine(Line7);
      LCD_DisplayStringLine(Line7,USART_RXBUF);
      for(i=0;i<20;i++){
        USART_RXBUF[i] = 0;  //清空接收区
      }
      RXOVER = 0;
      USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);
    }

需要注意的是,在每次显示完毕后要对数字中的每个元素进行清除,否则在下一次的接收时出现错误显示;对接收完成标志位进行清零;使能USART的接收标志位。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-05-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT界的小白帽 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档