STM32F103主从轮询发送和接收数据时的上电时序匹配怎么实现

STM32F103主从轮询发送和接收数据时的上电时序匹配怎么实现

  • 你可以看下这个问题的回答https://ask.csdn.net/questions/7425642
  • 这篇博客你也可以参考下:STM32F103程序设计-5-控制引脚高低电平的实现
  • 除此之外, 这篇博客: STM32f103串口寄存器,简单库函数配置中的 STM32f103串口寄存器,简单库函数配置 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • USART寄存器描述
    在这里插入图片描述
    串口相关控制寄存器
    状态寄存器(USART_SR)
    地址偏移:0x00
    复位值:0x00C0
    在这里插入图片描述
    状态寄存器适用于检测串口此时所处的状态。
    它能够检测到的状态有:发送寄存器空位、发送完成位、读数据寄存器非空位、检测到主线空闲位、过载错误为等等。
    主要关注两个位:RXNE和TC(第5、6两位)。
    RXNE(读数据寄存器非空):当该位被置1的时候,就是提示已经有数据被接收到了,并且可以读出来了(即RDR移位寄存器中的数据被转移到USART_DR寄存器中)。这时候要做的就是尽快读取USART_DR,从而将该位清零,也可以向该位写0,直接清除。
    TC(发送完成):当该位被置1的时候,表示USART_DR内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式:读USART_SR,写USART_DR;直接向该位写0。

    数据寄存器(USART_DR)
    在这里插入图片描述
    在这里插入图片描述
    USART_DR实际是包含了两个寄存器,一个专门用于发送的TDR,一个专门用于接收的RDR。进行发送数据操作时,往USART_DR写入数据会自动存储在TDR内;当进行读取数据操作时,向USART_DR读取数据会自动提取RDR数据。
    串行通信时一位一位传输的,所以TDR和RDR寄存器都是介于系统总线和移位寄存器间的;发送数据时把TDR内容转移到发送移位寄存器上,接收数据时则是把接收到的每一位顺序保存在接收移位寄存器内进而转移到RDR。

    波特比率寄存器(USART_BRR)
    在这里插入图片描述
    控制寄存器 1(USART_CR1)
    在这里插入图片描述
    控制寄存器 2(USART_CR2)
    地址偏移:0x10
    复位值:0x0000
    在这里插入图片描述

    控制寄存器 3(USART_CR3)
    地址偏移:0x14
    复位值:0x0000

    在这里插入图片描述

    控制寄存器主要是设置USART使能、检验控制使能、校验选择(奇校验偶校验)、PE中断使能、发送缓冲区空中断使能、发送完成中断使能、接收缓冲区非空使能、发送使能、接受使能、字长等。

    USART外设引脚复用
    在这里插入图片描述
    分数波特率的产生
    在这里插入图片描述

    USARTDIV是一个无符号的定点数。这12位的值设置在USART_BRR寄存器。
    注: 在写入 USART_BRR 之后,波特率计数器会被波特率寄存器的新值替换。因此,不要在通信进行中改变波特率寄存器的数值。

    串口设置的一般步骤可以总结为如下几个步骤:

    1. 串口时钟使能,GPIO 时钟使能
    2. 串口复位
    3. GPIO 端口模式设置
    4. 串口参数初始化
    5. 开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)
    6. 使能串口
    7. 编写中断处理函数
      下面,我们就简单介绍下这几个与串口基本配置直接相关的几个固件库函数。这些函数和定义主要分布在 stm32f10x_usart.h 和 stm32f10x_usart.c 文件中。
    1. 串口时钟使能。 串口是挂载在 APB2 下面的外设,所以使能函数为:
    `RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1)
    1. **串口复位。**当外设出现异常的时候可以通过复位设置,实现该外设的复位,然后重新配置这个外设达到让其重新工作的目的。一般在系统刚开始配置外设的时候,都会先执行复位该外设的操作。复位的是在函数 USART_DeInit()中完成:
     void USART_DeInit(USART_TypeDef* USARTx);//串口复位
    
    1. **串口参数初始化。**串口初始化是通过 USART_Init()函数实现的:
    void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)

    这个函数的第一个入口参数是指定初始化的串口标号,这里选择 USART1。第二个入口参数是一个 USART_InitTypeDef 类型的结构体指针,这个结构体指针的成员变量用来设置串口的一些参数。一般的实现格式为:

    USART_InitStructure.USART_BaudRate = bound; //波特率设置;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为 8 位数据格式
    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(USART1, &USART_InitStructure); //初始化串口
    
    

    从上面的初始化格式可以看出初始化需要设置的参数为:波特率,字长,停止位,奇偶校验位,硬件数据流控制,模式(收,发)。我们可以根据需要设置这些参数。
    4. 数据发送与接收。STM32 的发送与接收是通过数据寄存器 USART_DR 来实现的,这是
    一个双寄存器,包含了 TDR 和 RDR。当向该寄存器写数据的时候,串口就会自动发送,当收到数据的时候,也是存在该寄存器内。
    STM32 库函数操作 USART_DR 寄存器发送数据的函数是:

    void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
    

    通过该函数向串口寄存器 USART_DR 写入一个数据。
    STM32 库函数操作 USART_DR 寄存器读取串口接收到的数据的函数是:

    uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
    
    

    通过该函数可以读取串口接受到的数据。

    `
    5. 串口状态。串口的状态可以通过状态寄存器 USART_SR 读取。

    在我们固件库函数里面,读取串口状态的函数是:

    FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG)

    这个函数的第二个入口参数非常关键,它是标示我们要查看串口的哪种状态,比如上面讲解的RXNE(读数据寄存器非空)以及 TC(发送完成)。例如我们要判断读寄存器是否非空(RXNE),操作库函数的方法是:、

    USART_GetFlagStatus(USART1, USART_FLAG_RXNE);
    

    我们要判断发送是否完成(TC),操作库函数的方法是:

    USART_GetFlagStatus(USART1, USART_FLAG_TC);
    这些标识号在 MDK 里面是通过宏定义定义的:
    #define USART_IT_PE ((uint16_t)0x0028)
    #define USART_IT_TXE ((uint16_t)0x0727)
    #define USART_IT_TC ((uint16_t)0x0626)
    #define USART_IT_RXNE ((uint16_t)0x0525)
    #define USART_IT_IDLE ((uint16_t)0x0424)
    #define USART_IT_LBD ((uint16_t)0x0846)
    #define USART_IT_CTS ((uint16_t)0x096A)
    #define USART_IT_ERR ((uint16_t)0x0060)
    #define USART_IT_ORE ((uint16_t)0x0360)
    #define USART_IT_NE ((uint16_t)0x0260)
    #define USART_IT_FE ((uint16_t)0x0160)
    
    1. 串口使能。 串口使能是通过函数 USART_Cmd()来实现的,这个很容易理解,使用方法是:
    USART_Cmd(USART1, ENABLE); //使能串口
    

    7 **开启串口响应中断。**有些时候当我们还需要开启串口中断,那么我们还需要使能串口中断,使能串口中断的函数是:

    void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT,
    FunctionalState NewState)
    

    开启中断的方法是:

    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断,接收到数据中断
    

    我们在发送数据结束的时候(TC,发送完成)要产生中断,那么方法是:

    USART_ITConfig(USART1,USART_IT_TC,ENABLE);
    
    1. **获取相应中断状态。**当我们使能了某个中断的时候,当该中断发生了,就会设置状态寄存器中的某个标志位。经常我们在中断处理函数中,要判断该中断是哪种中断,使用的函数是:
    ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
    

    具体程序如下:

    #include"stm32f10x.h" 
    void my_USART_Init()	
    {	
     GPIO_InitTypeDef GPIO_InitStruct;		
     USART_InitTypeDef USART_InitStruct;		
     NVIC_InitTypeDef  NVIC_InitStruct;	
      
      //1.时钟使能		
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//ENABLE THE GPIOA		
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE)//ENABLETH USART1				
      //2.GPIOA9 init				 
     GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;//复位推挽输出		    
     GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9 ;	   	
     GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;		
     GPIO_Init(GPIOA,&GPIO_InitStruct);		//2.GPIOA10 init		
     GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;//浮空输入		
     GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;		  
     GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;		
     GPIO_Init(GPIOA,&GPIO_InitStruct);		//3.usart init				
     USART_InitStruct.USART_BaudRate=115200;//设置波特率		             
    USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//      
       设置硬件流设置		 
     USART_InitStruct.USART_Mode= USART_Mode_Rx | USART_Mode_Tx;//设置模式		 
     USART_InitStruct.USART_Parity=USART_Parity_No;//不采用奇偶校验		
     USART_InitStruct.USART_StopBits=USART_StopBits_1;//1位停止位		
     USART_InitStruct.USART_WordLength=USART_WordLength_8b;//8位数据位				
     USART_Init(USART1,&USART_InitStruct);   //初始化某串口		 
     USART_Cmd(USART1,ENABLE);  //串口使能				
     USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//实现中断		
     //中断优先级				
     NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn;		  
     NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;		
     NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;		
     NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;		
     NVIC_Init(&NVIC_InitStruct);	
    }	
    //中断处理函数	
    void USART1_IRQHandler(void)	
    {     u8 res;					                                             
     if(USART_GetITStatus(USART1,USART_IT_RXNE))				
     {
       res= USART_ReceiveData(USART1);						   
    USART_SendData(USART1,res);				
    }
    }	
    int main()
    {	
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级	
    my_USART_Init();
    while(1);