单线半双工实现两块STM32的通信(模拟与AX12舵机的通信过程)

实验内容

用32最小系统板模拟一个AX12舵机,用32精英板作为控制器。简单模拟控制器与AX12的通信过程。

最小系统板部分:
①通过单线(实验过程中选择PA2)与精英板通信,接收指令包。
②收到指令包后,发送反馈的状态包给精英板。

精英板内容:
①用串口1(PA9,PA10)与电脑通信。
②用单线(实验过程中选择PA2)与最小系统板通信。
具体实现:
按下精英版的KEY0,发送改变舵机ID的指令包给最小板,最小板收到指令包后,如果校验码没有错误,灯亮五秒后发送数据(符合要求格式)给精英板。精英板接收到数据后,把校验码发给电脑串口。(因为重点研究通信问题,故舵机多种功能不加以模拟)

相关理论内容

1.指令包与状态包的格式
在这里插入图片描述
需要关注INSTRUCTION内容:
在这里插入图片描述
需要关注的PARAMETER参数:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

重点代码

精英板部分:
1.半双工引脚的配置
关于简单实现半双工通信可以参考我的另一篇博客
https://blog.csdn.net/npuqiyi/article/details/105301081#comments

void uart2_init(u32 bound){
  //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
 USART_InitTypeDef USART_InitStructure;
 NVIC_InitTypeDef NVIC_InitStructure; 
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能USART2,GPIOA时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //使能USART2,GPIOA时钟 
 //USART2_TX   GPIOA.2
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA.2
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.2
  //Usart2 NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级2
 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //子优先级3
 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;   //IRQ通道使能
 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器  
   //USART 初始化设置
 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(USART2, &USART_InitStructure); //初始化串口1
  USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启串口接受中断
  USART_Cmd(USART2, ENABLE);                    //使能串口1  
 USART2->CR2&=~(1<<11);
  USART2->CR2&=~(1<<14);
  USART2->CR3|=1<<3; 
  USART2->CR3&=~(1<<1);  
  USART2->CR3&=~(1<<5);
}

2.向舵机发送指令包
因为半双工模式下,PA2和PA3是连接在一起的,所以串口发送数据也会触发中断,于是我软件设置模式选择,平时处于接收模式,只有需要时才出于发送模式。在中断服务函数中,只有接收模式的数据是有效的。

void Rudder_ID_Change(u8 IDO,u8 IDN)
{
 COMMAND[0]=0XFF;
 COMMAND[1]=0XFF;
 COMMAND[2]=IDO;
 COMMAND[3]=0X04;
 COMMAND[4]=0X03;
 COMMAND[5]=0X03;
 COMMAND[6]=IDN;
 COMMAND[7]=~(COMMAND[2]+COMMAND[3]+COMMAND[4]+COMMAND[5]+COMMAND[6]);
  LEN=8;                                      //生成指令
 MODE=MODE_TXD;
 Usart2_Send_Data(COMMAND,LEN);               //发送指令
 printf("ID-CHANGE");
 MODE=MODE_RXD;
 USART2_RX_STA=0;                              //接收模式配置
 memset(USART2_RX_BUF,0,sizeof(USART2_RX_BUF));
}

3.串口服务函数

void USART2_IRQHandler(void)                 //串口2中断服务程序
{
 u8 Res;
 static unsigned char sum=0;
 u8 i;
#if SYSTEM_SUPPORT_OS   //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
 OSIntEnter();    
#endif
 if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  
  {
   Res =USART_ReceiveData(USART2); //读取接收到的数据
     if(MODE==MODE_RXD)//必须为接收模式数据才有效
   {
    USART2_RX_BUF[USART2_RX_STA&0X3FFF]=Res ;
    USART2_RX_STA++;
     if(USART2_RX_BUF[0]!=0xff) USART2_RX_STA--;
    if((USART2_RX_STA&0X3FFF)>3)//收到长度
    {
     if((USART2_RX_BUF[0]==0xff)&&(USART2_RX_BUF[1]==0xff))//检验包头
     { 
      if((USART2_RX_STA&0X3FFF)==USART2_RX_BUF[3]+4)//接收完成
      {
       
       for(i=2;i<(USART2_RX_STA&0X3FFF)-1;i++)
       {
        sum+=USART2_RX_BUF[i];
       }
       sum=~sum;
       printf("   sum: %d  ",sum);
       if(sum==USART2_RX_BUF[(USART2_RX_STA&0X3FFF)-1])//满足校验
       {
        USART2_RX_STA|=0x8000;//接收终止位置1
       }
      }
     }
     }
    }     
     } 
#if SYSTEM_SUPPORT_OS  //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
 OSIntExit();              
#endif
} 

Res =USART_ReceiveData(USART2);这一句一定要保留,不然会出现程序卡死。
**if(USART2_RX_BUF[0]!=0xff) USART2_RX_STA–;**这一句也很重要,因为串口发送数据时,往往会先发送一个无效数据,这样可以通过包头的判断清除无效数据。

4.主函数

 int main(void)
 {  
  u8 key; 
 u16 times=0;
 delay_init();       //延时函数初始化   
 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
 uart_init(115200);  //串口初始化为115200
 uart2_init(115200);  //串口初始化为115200
  LED_Init();        //LED端口初始化
 KEY_Init();          //初始化与按键连接的硬件接口
 BEEP_Init();
 USART_SendData(USART2, 0);
 while(USART_GetFlagStatus(USART2,USART_FLAG_TC)!=SET);//防止有效数据丢失
  printf("CHUANKOU");
  while(1)
 {
  key=KEY_Scan(0);//先用电脑发给单片机,再按KEY0发回来
  if(key==KEY0_PRES)   //接收模式
  {
   printf("KEY0-PRES");
   Rudder_ID_Change(1,0);
  
   }
  else if(key==KEY1_PRES)
  {
   printf("%s",USART2_RX_BUF);
  }
  else
  {
   times++; 
   if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
   delay_ms(10);   
  }
 }  
 }

最小系统板部分
最小系统板部分我直接以精英部分代码为模板,内容基本一样。

 int main(void)
 {  
  u8 key; 
 u16 times=0;
 delay_init();       //延时函数初始化   
 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
 uart_init(115200);  //串口初始化为115200
 uart2_init(115200);  //串口初始化为115200
  LED_Init();        //LED端口初始化
 USART_SendData(USART2, 0);//第一次发的第一个数据(空闲帧)会丢失,这样就可以了
 while(USART_GetFlagStatus(USART2,USART_FLAG_TC)!=SET);
  printf("CHUANKOU");
  MODE=MODE_RXD;
  while(1)
 {
  if(USART2_RX_STA&0x8000)
  {
   LED0=0;
   delay_ms(1000);
   delay_ms(1000);
   delay_ms(1000);
   delay_ms(1000);
   delay_ms(1000);
   Rudder_ID_Change(1,0);
  }
  else
  {
   times++; 
   if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
   delay_ms(10);   
  }
 }  
 }

相关代码资源,不需要积分,感兴趣的人可以看一看。
https://download.csdn.net/download/npuqiyi/12302305

发布了5 篇原创文章 · 获赞 4 · 访问量 2669

猜你喜欢

转载自blog.csdn.net/npuqiyi/article/details/105249717