打印
[STM32F1]

STM32F103之UART上位机通信

[复制链接]
37|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
一、USART简介
1.1 UART 简介
通用同步异步收发器(USART:Universal Synchronous/Asynchronous Receiver/Transmitter)
特点:串行、全双工、同步/异步通信(USART可支持同步通信,UART只支持异步通信)

发送和接收共用的可编程波特率
可编程数据字长度(8位或9位)(常用8位)
可配置的停止位-支持1或2个停止位(常用1位停止位)
可配置奇偶校验位(一般不用)

1.2 物理拓补图
接口通过三个引脚与其他设备连接在一起。任何USART双向通信至少需要两个脚
RX:接收数据输入
TX:发送数据输出
GND:双方要共地,双方通过USART进行通信,两个设备必须要共地,因为逻辑1---3.3V电压(对于地的电压),如果两个设备不共地则对地电压的高电平(3.3V就可能不太一样)。共地的意义是让两个设备的通信电平对地电压相等



1.3 数据帧格式
一帧数据=1bit起始位[低电平]+8bit数据位+1bit奇偶校验位[可以不用]+1bit停止位[高电平]

USART总线空闲,即没有数据发生,TX线为高电平

<1>数据位:USART发送的1次数据量是1Byte=8bit;[5、6、7、8]
<2>数据字:数据位+奇偶校验位;如果不用校验位,数据字=数据位=8bit;如果使用校验位,数据字=数据位+校验位=9bit
                    奇校验:数据位中的1的个数+校验位中的1的个数为奇数
                    偶校验:数据位中的1的个数+校验位中的1的个数为偶数
<3>停止位:可以被配置为1bit(默认);2个停止位(可用于常规USART模式、单线模式以及调制解调器模式);0.5个停止位:在智能卡模式下接收数据时使用;1.5个停止位:在智能卡模式下发送和接收数据时使用
<4>发送顺序:先发送低位,再发送高位

1.4 框图



对于这个框图,最关键的是两个标志位TXE(发送为空标志位),RXNE(接收不为空标志位)
数据通过RX端口进入MCU的UART的接收移位寄存器,随后进入接收数据寄存器(RDR),进入后RXNE被置1,表明可以读
MCU通过数据总线获得数据,数据再到发送数据寄存器(TDR),若TXE=1,说明数据已经被转移到移位寄存器,表明可以发送

二、串口通信
配置UART

开启了接收中断和空闲中断

/****************************
*@brief    uasrt1端口配置函数
*@param     void
*@return    void
*@NOTE      PA10->RX PA9->TX
            PA10,PA9位于APB2上
*@time     2025-6-3
******************************/
void usart1_Init(uint32_t Baud_Rate)
{
        //1.打开引脚时钟PA
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE)      ;
       
        //2.打开usart1时钟
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE)    ;
       
        //PA9->TX推挽复用输出
        GPIO_InitTypeDef GPIO_InitStruct_TX                      ;
        GPIO_InitStruct_TX.GPIO_Mode  = GPIO_Mode_AF_PP          ;
        GPIO_InitStruct_TX.GPIO_Pin   = GPIO_Pin_9               ;
        GPIO_InitStruct_TX.GPIO_Speed = GPIO_Speed_10MHz         ;
        GPIO_Init(GPIOA,&GPIO_InitStruct_TX)        ;
       
       
        //3.配置,PA10->RX浮空输入
        GPIO_InitTypeDef GPIO_InitStruct_RX                      ;
        GPIO_InitStruct_RX.GPIO_Mode  = GPIO_Mode_IN_FLOATING    ;
        GPIO_InitStruct_RX.GPIO_Pin   = GPIO_Pin_10              ;
        GPIO_Init(GPIOA,&GPIO_InitStruct_RX)        ;
       
        //4.配置usart1,
        USART_InitTypeDef USART_InitStruct                                           ;
        USART_InitStruct.USART_Mode                = USART_Mode_Rx|USART_Mode_Tx     ;
        USART_InitStruct.USART_Parity              = USART_Parity_No                 ;
        USART_InitStruct.USART_StopBits            = USART_StopBits_1                ;
        USART_InitStruct.USART_BaudRate            = Baud_Rate                       ;       
        USART_InitStruct.USART_WordLength          = USART_WordLength_8b             ;
        USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None  ;
        USART_Init(USART1,&USART_InitStruct)                                         ;

                  
                  USART_ITConfig(USART1,USART_IT_RXNE,ENABLE)                     ;//开启接收中断 RXNEIE=1
                        USART_ITConfig(USART1,USART_IT_IDLE,ENABLE)                     ;//开启空闲中断 IDLE=1
                  //初始化NVIC
                  NVIC_InitTypeDef  NVIC_InitStruct                               ;
                  NVIC_InitStruct.NVIC_IRQChannel                   = USART1_IRQn ;//中断通道
                  NVIC_InitStruct.NVIC_IRQChannelCmd                = ENABLE      ;//中断使能
                  NVIC_InitStruct.NVIC_IRQChannelSubPriority        = 2           ;//响应先级
              NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2           ;//抢占优先级
              NVIC_Init(&NVIC_InitStruct)                                     ;
       
        //5.使能usart1
        USART_Cmd(USART1,ENABLE)                                                     ;
}  


接收发送函数

/****************************
*@brief     发送字符函数
*@param    void
*@return   void
*@note     MCU会将数据发送到DR寄存器中的TDR寄存器中,
当TXE==1时,TDR寄存器会将数据加载到移位寄存器中,可以发送
*@time    2025-6-3
******************************/
void usart1_sendbyte(uint8_t str)
{
        while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==0);
        USART_SendData(USART1,str);
}

/****************************
*@brief     发送字符串函数
*@param   uint8_t *str
*@return  uint16_t len
*@note    发送字符串本质上就是发送字符
*@time    2025-6-3
******************************/
void usart1_sendstr(char *str,int len)
{
        while(len)
   {
                 usart1_sendbyte(*str++);
                 len--;
         }
}

/****************************
*@brief    接收字符
*@param    void
*@return   void
*@note    MCU接收到数据后先放在DR寄存器的接收移位寄存器
          数据会到RDR寄存器中,当RXNE==1时,说明RDR有数据,可以读
*@time     2025-6-3
******************************/
uint16_t usart1_receivebyte()
{
        while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==0);
        return  USART_ReceiveData(USART1);
}
/****************************
*@brief    接收字符串函数(idle标志位)
*@param  void
*@return   void
*@note     void
*@time    2025-6-3
******************************/
void usart1_receivestr(char*buff)
{
        uint8_t flag;
        while(1)
   {
                 if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==1)
       {
                                 *buff=USART_ReceiveData(USART1);
                                 buff++;
                                 flag=1;
                         }
                        if(USART_GetFlagStatus(USART1,USART_FLAG_IDLE)==1&&flag)
       {
                                 *buff='\0'; //当发送时 发送结束标志
                                 //先读SR,再读DR将置IDLE位置0
                                 USART1->SR;
                                 USART1->DR;
                                 break;
                         }
         }
}


打印函数

记得勾选微库,printf函数不涉及到中断,它是将内存中的数据输出到上位机

//printf()的底层借助fputc函数进行输出
int fputc(int ch,FILE*stream)
{
        usart1_sendbyte(ch);
        return ch;
}




中断函数
MCU接收到数据时,进入接收中断函数,将数据存到rx_buff[100]数组里;当上位机不发送数据时,说明空闲状态,进入空闲中断,将 rx_buff[100]数组里面的数据发到上位机

/****************************
*@brief    usart1中断实验
*@param  void
*@return   void
*@note     void
*@time   2025-6-4
******************************/
char rx_buff[1000]={0};
char cnt=0;
void USART1_IRQHandler(void)
{
   //当RXNE=1时,有数据接收
        if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)
   {
                 rx_buff[cnt++]=USART_ReceiveData(USART1);
                 USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除标志位
         }
         
         //当IDLE=1时,
        if(USART_GetITStatus(USART1,USART_IT_IDLE)==SET)
   {
                 usart1_sendstr(rx_buff,strlen(rx_buff));
                 cnt=0;
                 USART1->SR;
                 USART1->DR;                                  //清除标志位
         }
}


————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/DK3314219995/article/details/149025676

使用特权

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

本版积分规则

60

主题

180

帖子

0

粉丝