OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4721|回复: 14

深夜叨扰,想问问串口发送一组数据的问题。

[复制链接]

56

主题

520

帖子

0

精华

高级会员

Rank: 4

积分
964
金钱
964
注册时间
2014-11-18
在线时间
160 小时
发表于 2016-3-1 22:00:25 | 显示全部楼层 |阅读模式
30金钱
因为指纹模块的需要,串口协议成了我的心头痛,发送一个字节,然后串口接收一个字节,这个是很简单的。但是发送一串数据,我完全没有这个思想。后面上网看到要
接收完成的判断,后面也试了一下,没调试出来,难道是我的思路还存在问题。希望大家各抒己见,给个这方面的学习链接也可以,小弟再次谢过了,谢谢各位大神了。
下面也给出我的代码。


最佳答案

查看完整内容[请看2#楼]

关建是你的串口协议要乍样来制定 1: 一次发送一个字节,或都一次发多个字节都没有问题 2:如果一次发送或接收的数据串大于5节个以上,我建义使用 STM32的DMA来做比较好 3:比如说, 你定议的协议为每个字节的 D7位是指令或数据标识, 1是指令,0是数据 哪么............. 这样吧, 我在做步进电机串行驱动是用过的一个485双向发送接收你可以参考一下, 不懂的地方可以问我! 如下是部份接收与发送的代码,你自已看看 /******** ...
自己选择的路,成家前走完。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

6

主题

1097

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3571
金钱
3571
注册时间
2014-12-2
在线时间
365 小时
发表于 2016-3-1 22:07:34 | 显示全部楼层
给你一个思路,串口接收到一个数据包内的字节间的时间间隔是很短的。

你可以每隔一段时间(如10ms)检查Recieve_Cnt的值,如果当前的值和上一次的值一致且不为零
则说明这10ms是没有数据的,即说明上一个数据包已经接收完毕,然后你就可以处理这个数据包了
坚决不用寄存器,拒绝重复造轮子。
回复

使用道具 举报

9

主题

507

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3347
金钱
3347
注册时间
2013-4-10
在线时间
333 小时
发表于 2016-3-1 22:00:26 | 显示全部楼层
本帖最后由 likunxue 于 2016-3-2 00:37 编辑

关建是你的串口协议要乍样来制定
1: 一次发送一个字节,或都一次发多个字节都没有问题
2:如果一次发送或接收的数据串大于5节个以上,我建义使用 STM32的DMA来做比较好
3:比如说, 你定议的协议为每个字节的 D7位是指令或数据标识, 1是指令,0是数据
哪么............. 这样吧, 我在做步进电机串行驱动是用过的一个485双向发送接收你可以参考一下,
不懂的地方可以问我!
如下是部份接收与发送的代码,你自已看看

/**************************************************************************************                 
模块名称: RS485驱动代码
文件名称: RS485.C
版    本: V1.0
**************************************************************************************/
#include "sys.h"                    
#include "rs485.h"         
#include "delay.h"
#include "MOTOR.H"                                    //步进电机配置头文件
#define TX_LED_ON()       GPIOC->BRR = 1<<12          //开工作指示灯
#define TX_LED_OFF()      GPIOC->BSRR = 1<<12         //关工作指示灯      
#define        SUA_SIZE          100u                        //串口接收,发送缓存区大小  

vu16 SUA_FWDIR;                                       //复位方向寄存器
vu16 R485_RX_BYTE;
vu16 RX_BYTE;                                         //串口收到的16位参数
vu8 SUA_MCLK;                                         //脉冲输出标志位         
vu8 RX_DAT;                                           //串口接收中的低7位数据寄存器
vu8 RX_QianZhiLing;                                      //串口接收中的低位接收标志位
vu8 RX_STR;                                           //串口接收缓存区指针
vu8 RX_Buf[SUA_SIZE];                                 //串口接收缓存数据区
vu8 TX_Buf[SUA_SIZE];                                 //串口2发送缓存区
/***************************************************************************************
函 数 名: DMA1的各通道配置
          这里的传输形式是固定的,这点要根据不同的情况来修改
          从存储器->外设模式/8位数据宽度/存储器增量模式
调    用: UART_DMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar)
参    数: DMA_CHxMA通道CHx
          cpar:外设地址
          cmar:存储器地址   
返 回 值: 无
***************************************************************************************/  
void UART_DMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar)
     {
     RCC->AHBENR |= 1<<0;                   //开启DMA1时钟
     delay_us(5);
     DMA_CHx->CPAR = cpar;                    //DMA1 外设地址
     DMA_CHx->CMAR = cmar;                    //DMA1,存储器地址         
     DMA_CHx->CCR = 0X00000000;                   //复位
     DMA_CHx->CCR |= 1<<4;                     //从存储器读
     DMA_CHx->CCR |= 0<<5;                     //普通模式
     DMA_CHx->CCR |= 0<<6;                     //外设地址非增量模式
     DMA_CHx->CCR |= 1<<7;                     //存储器增量模式
     DMA_CHx->CCR |= 0<<8;                     //外设数据宽度为8位
     DMA_CHx->CCR |= 0<<10;                    //存储器数据宽度8位
     DMA_CHx->CCR |= 1<<12;                    //中等优先级
     DMA_CHx->CCR |= 0<<14;                    //非存储器到存储器模式            
     }
/***************************************************************************************
函 数 名: 开启一次DMA传输
调    用: UART_DMA_Enable(DMA_Channel_TypeDef*DMA_CHx,u8 len)
参    数: DMA_CHxMA通道CHx
          len 需要传输的字节
返 回 值: 无
***************************************************************************************/      
void UART_DMA_Enable(DMA_Channel_TypeDef*DMA_CHx,u8 len)
     {
     DMA_CHx->CCR&=~(1<<0);              //关闭DMA传输
     DMA_CHx->CNDTR=len;                 //DMA1,传输数据量
     DMA_CHx->CCR|=1<<0;                 //开启DMA传输
     }
/*************************************************************************************
函 数 名:初始化IO 串口2
调    用:无
参    数:pclk1CLK1时钟频率(Mhz)
          bound:波特率
反 回 值: 无
**************************************************************************************/            
void RS485_Init(u32 pclk1,u32 bound)
     {           
     float temp;
     u16 i, mantissa,fraction;      
     for(i =0; i< SUA_SIZE; i++){RX_Buf = 0; TX_Buf = 0;}//接收缓存区
     RX_BYTE = 0;                     //串口收到的16位参数            
     RX_DAT = 0;                      //串口接收中的低7位数据寄存器
     RX_QianZhiLing = 0;              //串口接收中的低位接收标志位
     RX_STR = 0;                      //串口接收缓存区指针
     temp=(float)(pclk1*1000000)/(bound*16);//得到USARTDIV
     mantissa=temp;                      //得到整数部分
     fraction=(temp-mantissa)*16;     //得到小数部分         
     mantissa<<=4;
     mantissa+=fraction;
     RCC->APB2ENR |= 1<<2;            //使能PORTA口时钟  
     GPIOA->CRL &= 0XFFFF00FF;              //IO状态设置
     GPIOA->CRL |= 0X00008B00;              //IO状态设置         
     RCC->APB1ENR |= 1<<17;                //使能串口时钟
     RCC->APB1RSTR |= 1<<17;          //复位串口2
     RCC->APB1RSTR &= ~(1<<17);       //停止复位
     RCC->APB2ENR |= 1<<4;            //使能C时钟
     GPIOC->CRH &= 0xfff0ffff;
     GPIOC->CRH |= 0x00030000;        //PC12设为输出
     GPIOC->ODR |= 1<<12;             //初始化为高电平,关闭状态
     //波特率设置
     USART2->BRR = mantissa;               //波特率设置         
     USART2->CR1 |= 1<<13;            //串口2使能(UE,USATR)
     USART2->CR1 |= 1<<2;             //接收使能(RE)
     USART2->CR1 |= 1<<3;             //发送使能(TE)
     USART2->CR1 |= 1<<8;             //PE中断使能
     USART2->CR1 |= 1<<5;             //接收缓冲区非空中断使能        
     USART2->CR3 = 1<<7;                 //使能串口2的DMA发送
     //DMA1通道7,外设为串口2,存储器为TX_BUF
     UART_DMA_Config(DMA1_Channel7,(u32)&USART2->DR,(u32)TX_Buf); //初始化DMA  
     MY_NVIC_Init(0,0,USART2_IRQn,4); //抢占5,子优先级0,组4  
     MOTOR_TIM7_Init(); //初始化定时器7
     R485_RX_BYTE = 0;  
     PEND_OUT = 0;      //关电机失步报警灯
     }      
/*************************************************************************************
函 数 名: 向主机传送双字节参数(串口中断函数调用)
调    用: TX_FASONG_Byte(u16  Dat)
参    数: Dat 需要发送的参数   
反 回 值: 无
**************************************************************************************/
void TX_FASONG_Byte(u16  Dat)
     {     
     u8 S0 = Dat;
     u8 S1 = Dat >> 8;  
     TX_LED_ON();  //开工作指示灯              
     while(DMA1_Channel7->CNDTR != 0);             //等待通道7传输完成  
     TX_Buf[0] = S0 & 0x7f;                  //发送低7位   
     TX_Buf[1] = ((S0 >> 7) & 0x07)|0x88;    //初始化指令码
     TX_Buf[2] = S1 & 0x7f;                  //发送低7位   
     TX_Buf[3] = ((S1 >> 7) & 0x07)|0x98;    //结束码,并使能读取   
     UART_DMA_Enable(DMA1_Channel7,4);              //通过DMA发送出去4个字节               
     }
/*************************************************************************************
函 数 名: 多字节参数解码(串口中断函数调用)
调    用: SUA_CANSHUJIEMA(void)
参    数: 无   
反 回 值: 无
说    明: 接收6个字节,前面4个是双字节参数,第5个为功能码,第6个是指令码0x90
          高字节在后,低字节在前.   
**************************************************************************************/      
void SUA_CANSHUJIEMA(void)
     {     
     if((RX_STR ==2)&&(RX_QianZhiLing == 0xE5))
       {
       u16 Bul =(u16)(RX_Buf[1]<<8)+ RX_Buf[0];  //取得数据        
       TX_LED_ON();  //开工作指示灯        
       switch(RX_DAT)      
             {     
             case 12: MoB.XIFEN[Motor_Z] = Bul;break;   //0号机细分参数X
             case 13: MoB.XIFEN[Motor_R] = Bul;break;   //1号机细分参数Z
             case 14: MoB.XIFEN[Motor_T] = Bul;break;   //2号机细分参数R
             case 15: MoB.XIFEN[Motor_O] = Bul;break;   //3号机细分参数T
             case 22: MoB.DIANLIU[Motor_Z]= Bul;break;  //0号机电流参数X
             case 23: MoB.DIANLIU[Motor_R]= Bul;break;  //1号机电流参数Z
             case 24: MoB.DIANLIU[Motor_T]= Bul;break;  //2号机电流参数R
             case 25: MoB.DIANLIU[Motor_O]= Bul;break;  //3号机电流参数T  
             case 50: MoB.FINE = Bul;break;       //伺服细分参数               
             case 51: MoB.QDSD = Bul;break;       //伺服起动速度      
             case 52: MoB.ZGSD = Bul;break;       //伺服最高工作速度
             case 53: MoB.JiaSuShiJian = Bul;break;//伺服加速时间
             case 54: MoB.BianSuBi = Bul;break;   //伺服变速比
             case 55: MoB.SDTJ = Bul;break;       //伺服速度调节变量     
             case 70:{//重配电机参数
                     if(Bul == 0x55aa)MOTOR_CanShuZhongPei();//参数配置                 
                     }break;
             case 99:R485_RX_BYTE = Bul; break; //用于显示主机发来的测试数据     
             }               
       }
     }   
/*************************************************************************************
函 数 名: RS485中断接收函数(双字节指令模式)
调    用: 无
参    数: 无      
反 回 值: 无
自定义数据接收格式
**************************************************************************************/  
void USART2_IRQHandler(void)
     {            
     if(USART2->SR&(1<<5))       //接收到数据
       {  
       u8 K = USART2->DR;        //读取串口数据  
       USART2->SR &= ~(1<<5);    //清中断接收标志位           
       if(K < 0x80){RX_DAT = K;RX_QianZhiLing = 0xE5;}//数据低7位
       else{
           u8 Bul = K & 0xf8; //得到指令码                 
           switch(Bul)
                 {  
                 case 0x80:{//是数据字节(双字节指令码)
                           if(RX_QianZhiLing == 0XE5)
                             {
                             TX_LED_ON();              //开通信指示灯      
                             if(RX_STR >SUA_SIZE)RX_STR = 0;
                             RX_Buf[RX_STR ++] = (K <<7)|RX_DAT; //保存数据
                             }
                           }break;
                 case 0x88:{//多字节参数初始化
                           if(RX_QianZhiLing == 0XE5)
                             {
                             TX_LED_ON(); //开通信指示灯     
                             RX_STR = 0;
                             RX_Buf[RX_STR ++] = (K <<7)|RX_DAT;
                             }
                           }break;
                 case 0x90:{//收到电机方向数据(双字节指令码)
                           if(RX_QianZhiLing == 0XE5)
                             {                 
                             u8 Bii = (RX_DAT >> 4)&0x01;//得到方向标志                  
                             u8 rr1 = RX_DAT & 0x0f; //得到电机编号
                             TX_LED_ON(); //开工作指示灯      
                             switch(rr1)   //高位机地址   
                                   {//机头板                                                         
                                   }     
                             }
                           }break;
                 case 0x98:{//工作电机脉冲(双字节指令码)
                           if(RX_QianZhiLing == 0xe5)
                             {
                             u16 S0 = (((u16)(K & 0x07))<< 7 )|RX_DAT; //得到10位数据位   
                             u8 Bul = (u8)((S0>>2)&0x0f); //得到脉冲数据      
                             if(Bul != 0)
                               {        

                               }
                             }                              
                           }break;
                 case 0xB0:break;
                 case 0xB8:break;//位置坐标同步                           
                 default:{  //以下指令为快速指令码,全部为单字节  
                         switch(K)
                               {
                               case 0xE0:SUA_CANSHUJIEMA();break;//多字节参数解码(0xB0)     
                               case 0xE1:{//关闭电机
                                         TX_LED_ON(); //开通信指示灯
                                         if(TASK_MOBF() == ERR_OK)MOTOR_ENBL_OFF(); //电机在位置纠正时禁止关电机
                                         }break;
                               }
                         }break;
                 }
           RX_QianZhiLing = 0x80; //清标志位         
           }
        }
     }



回复

使用道具 举报

56

主题

520

帖子

0

精华

高级会员

Rank: 4

积分
964
金钱
964
注册时间
2014-11-18
在线时间
160 小时
 楼主| 发表于 2016-3-1 22:01:49 | 显示全部楼层
本帖最后由 aiyeba 于 2016-3-1 22:06 编辑

[mw_shl_code=c,true]void USART2_IRQHandler(void)                        
{
        
    u8 res;
         if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  
                {
                         Res=1;
                   res =USART_ReceiveData(USART2);
                          Recieve_Data[Recieve_Cnt]=res;
                    Recieve_Cnt++;
                          if(Recieve_Cnt>4)Recieve_Cnt=4;
                         LED0=~LED0;
                }

} [/mw_shl_code]
自己选择的路,成家前走完。
回复

使用道具 举报

56

主题

520

帖子

0

精华

高级会员

Rank: 4

积分
964
金钱
964
注册时间
2014-11-18
在线时间
160 小时
 楼主| 发表于 2016-3-1 22:03:14 | 显示全部楼层
本帖最后由 aiyeba 于 2016-3-1 22:05 编辑

[mw_shl_code=c,true]void Usart_Recieve_Ok(u8 *Cnt)   //判断是否接收完一串数据
{

   u8 i;
   
   Rx_Len=*Cnt;
   delay_ms(10);
   if( Rx_Len==*Cnt&&Rx_Len)   
   {
      for(i=0;i<Rx_Len;i++)
      {
             Recieve_Ok=Recieve_Data;
            Recieve_Data=0x00;
      }
     *Cnt=0;
   }

}[/mw_shl_code]
自己选择的路,成家前走完。
回复

使用道具 举报

56

主题

520

帖子

0

精华

高级会员

Rank: 4

积分
964
金钱
964
注册时间
2014-11-18
在线时间
160 小时
 楼主| 发表于 2016-3-1 22:07:18 | 显示全部楼层
        while(1)
        {
               

       
                Usart_Recieve_Ok(&Recieve_Cnt);               
               
                        if(Rx_Len>=4)
                        {
                               
                         for(i=0;i<5;i++)
                                 USART_SendData(USART2, Recieve_Ok[i]);
                        }
                       
               
                       
        }         
自己选择的路,成家前走完。
回复

使用道具 举报

56

主题

520

帖子

0

精华

高级会员

Rank: 4

积分
964
金钱
964
注册时间
2014-11-18
在线时间
160 小时
 楼主| 发表于 2016-3-1 22:12:07 | 显示全部楼层
xkwy 发表于 2016-3-1 22:07
给你一个思路,串口接收到一个数据包内的字节间的时间间隔是很短的。

你可以每隔一段时间(如10ms)检查 ...

我的思路是 Recieve_Cnt这个先保存起来,然后延时10ms,这俩个值还相等,说明没有数据过来,然后再处理数据。逻辑是没有问题,可是我的结果就是每按一次发送,只接收到几个字节,而且还是错的。
自己选择的路,成家前走完。
回复

使用道具 举报

6

主题

1097

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3571
金钱
3571
注册时间
2014-12-2
在线时间
365 小时
发表于 2016-3-1 22:26:05 | 显示全部楼层
你要注意,我的意思是每隔一段时间执行上述代码,而不是延时10ms,再检测一次,体会一下区别
坚决不用寄存器,拒绝重复造轮子。
回复

使用道具 举报

6

主题

1097

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3571
金钱
3571
注册时间
2014-12-2
在线时间
365 小时
发表于 2016-3-1 22:26:41 | 显示全部楼层
“每隔一段时间”可以通过定时器中断实现
坚决不用寄存器,拒绝重复造轮子。
回复

使用道具 举报

56

主题

520

帖子

0

精华

高级会员

Rank: 4

积分
964
金钱
964
注册时间
2014-11-18
在线时间
160 小时
 楼主| 发表于 2016-3-2 09:08:02 | 显示全部楼层
xkwy 发表于 2016-3-1 22:26
“每隔一段时间”可以通过定时器中断实现

我明白了   延时只会浪费while(1)的效率,对实时性不好。
自己选择的路,成家前走完。
回复

使用道具 举报

56

主题

520

帖子

0

精华

高级会员

Rank: 4

积分
964
金钱
964
注册时间
2014-11-18
在线时间
160 小时
 楼主| 发表于 2016-3-2 09:10:11 | 显示全部楼层
likunxue 发表于 2016-3-2 00:24
关建是你的串口协议要乍样来制定
1: 一次发送一个字节,或都一次发多个字节都没有问题
2:如果一次发送 ...

大神 ,我先搞懂串口先,你这个还加了DMA,还有电机,对我来说难于上青天。
自己选择的路,成家前走完。
回复

使用道具 举报

13

主题

276

帖子

0

精华

高级会员

Rank: 4

积分
697
金钱
697
注册时间
2014-6-20
在线时间
146 小时
发表于 2016-3-2 09:17:17 | 显示全部楼层
打开串口接收中断,将数据压入堆栈,模块发送的数据应该是有格式的,检查数据的开始标示和结束标示比较好
回复

使用道具 举报

9

主题

507

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3347
金钱
3347
注册时间
2013-4-10
在线时间
333 小时
发表于 2016-3-2 11:52:20 | 显示全部楼层
313668972 发表于 2016-3-2 09:17
打开串口接收中断,将数据压入堆栈,模块发送的数据应该是有格式的,检查数据的开始标示和结束标示比较好

串口多字节传输一定要有一个数据交换协议,否则你不管是接收也好,发送也好,你如何判断哪是开始,哪是结速呢,最少也得要知道一次发送多少字节吧! 否则,你就真的只能一个字节一个字节的发送或接收了!
回复

使用道具 举报

13

主题

276

帖子

0

精华

高级会员

Rank: 4

积分
697
金钱
697
注册时间
2014-6-20
在线时间
146 小时
发表于 2016-3-2 12:02:23 | 显示全部楼层
likunxue 发表于 2016-3-2 11:52
串口多字节传输一定要有一个数据交换协议,否则你不管是接收也好,发送也好,你如何判断哪是开始,哪是结 ...

我现在用的就是不定长数据发送和接收
回复

使用道具 举报

9

主题

507

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3347
金钱
3347
注册时间
2013-4-10
在线时间
333 小时
发表于 2016-3-2 16:11:06 | 显示全部楼层
本帖最后由 likunxue 于 2016-3-2 16:20 编辑
313668972 发表于 2016-3-2 12:02
我现在用的就是不定长数据发送和接收

不定长,没有协议更无法处理,协议没有哪么复杂, 就是你自已定义的一些格式,   比如, 0XFE 开始,  0XFF 结束, 这样在接收方,收到 0XFE后,接收指针清零, str = 0;  然后, Buf[str++] = DAT;  开始接收数据, 当收到 0XFF表示数据收完, 然后是处理收到的数据,就行了

不用 DMA也一样可以一次发送多个字节, 只是需要占用CPU 时间, 用DMA 在发送或接过程中可以不用 CPU参与.


mxi  是要发送的字节数
    for( j = 0 ; j< mxi; j ++)        
       {
      while((USART2->SR & 0x80)==0);      //等发送区空
      USART2->DR = Buf[j];//发送数据   
      }




回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-21 02:48

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表