Toriyung

导航

USART串口学习

硬件

  单片机---STM32F103C6T6

  串口---COMPIM

 

软件

  虚拟串口---VSPD Pro 9.0

  串口助手---友善串口调试助手

 

原理

  USART有五个引脚,本次实验使用异步通信,只用到RX(接受)和TX(发送)两个引脚,通讯协议如下图,先拉低电平表示开始,而后发送8或9个数据位,再发送1个校验位,最后拉高电平为停止位(1或2个)

 

  通讯双方应RX-TX,TX-RX连接,STM32与PC通讯则通过CH340等芯片改变电平模式,本次实验使用虚拟COM口则无需改变

proteus原理图:

 

虚拟串口配对,其中上图COMPIM使用COM1

 

 

串口调试接口,使用COM2,波特率等设置一致

 

 

代码

  引脚初始化:需要注意的是引脚模式

void GPIO_Init(void)
{
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    GPIO_InitTypeDef GPIO_Struct;
    
    GPIO_Struct.Pin = GPIO_PIN_9;
    GPIO_Struct.Mode = GPIO_MODE_AF_PP;    //复用推挽输出
    GPIO_Struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA,&GPIO_Struct);
    
    GPIO_Struct.Pin = GPIO_PIN_10;
    GPIO_Struct.Mode = GPIO_MODE_INPUT;    //输入模式
    GPIO_Struct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA,&GPIO_Struct);
}

  USART初始化:

UART_HandleTypeDef usart1;

void USART_Init(void)
{
    __HAL_RCC_USART1_CLK_ENABLE();
    
    usart1.Instance = USART1;    //串口选择
    usart1.Init.BaudRate = 115200;    //波特率
    usart1.Init.WordLength = UART_WORDLENGTH_8B;    //数据位长
    usart1.Init.StopBits = UART_STOPBITS_1;    //停止位数
    usart1.Init.Parity = UART_PARITY_NONE;    //奇偶校验法选择
    usart1.Init.Mode = UART_MODE_TX_RX;     //单双工模式选择
    usart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;     //硬件流控制
    usart1.Init.OverSampling = UART_OVERSAMPLING_16;    //超采样
    
    HAL_UART_Init(&usart1);        
}

 

实验1:HAL库函数HAL_UART_Transmit()输出

效果

 

实验2:重定向printf输出

原理:重定向指的是重新书写printf函数(printf其实是fputc的宏定义),使得printf函数为我们想要的输出方式

  **核心设置**:

      1. 重定向定义函数代码:这段代码是main.c函数里的,需要注意的是外部变量的引用

extern UART_HandleTypeDef usart1;   //usart.c文件里面定义的变量,需要使用外部变量调用
    #ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

PUTCHAR_PROTOTYPE
{
    /* Place your implementation of fputc here */
    /* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
    
    HAL_UART_Transmit(&usart1, (uint8_t *)&ch, 1, 0xFFFF);

    return ch;
}

      2. 刷新缓冲区:需要在printf之前调用,网上说多次调用会卡死,目前还未遇到

    setvbuf(stdin,NULL,_IONBF,0);
    setvbuf(stdout,NULL,_IONBF,0);

      3. **microLIB勾选:不勾选即使重定义printf系统也是会选择成标准库里的,网上有些人说写了重定义函数就不用勾选,明显是不对的

 

 效果

 

实验3:中断接收

  USART接收中断的流程大致是:HAL_UART_Receive_IT()→→函数串口收到中断→→USART1_IRQHandler()函数→→HAL_UART_IRQHandler()函数→→HAL_UART_RxCpltCallback()函数

  解析: 

    首先HAL_UART_Receive_IT()挂起中断,当收到接收中断时,HAL_UART_IRQHandler()会清空中断标志,然后调用HAL_UART_RxCpltCallback(),所以需要在HAL_UART_RxCpltCallback()中重新挂起标志并书写需要的功能代码

  代码书写:

    需要在stm32f1xx.it.c文件中添加函数

void USART1_IRQHandler(void)
{
  HAL_UART_IRQHandler(&huart1);
}

    然后main函数中重写HAL_UART_RxCpltCallback()函数并重新挂起中断

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    {
         //自己的代码
          } 
    HAL_UART_Receive_IT(huart,&CRX,1);  //挂起中断
}    

  主函数中主循环开始之前需要先挂起中断

//main.c

uint8_t CRX;

HAL_UART_Receive_IT(huart,&CRX,1);
while(1)
{

}

 

实现效果:

  发送单个字符时可以实现接收后发送,但callback函数中加入printf时会导致信息丢失,同样在发送多个字符时也会发生信息丢失,猜测是仿真出现的中断bug

posted on 2022-10-02 11:54  Toriyung  阅读(385)  评论(0编辑  收藏  举报