zoukankan      html  css  js  c++  java
  • 1-关于单片机通信数据传输(中断发送,大小端,IEEE754浮点型格式,共用体,空闲中断,环形队列)

    补充:

    程序优化

    为避免普通发送和中断发送造成冲突(造成死机,复位重启),printf修改为中断发送

    写这篇文章的目的呢,如题目所言,我承认自己是一个程序猿.....应该说很多很多学单片机的对于...先不说别的了,,无论是学51的还是32的,,,先问一下大家用串口发送数据的时候是怎么发的???如果发整型的数据是怎么发的??如果发浮点型的是怎么发的????再问大家串口接收数据是怎么接收的????亲们有没有想过自已用的方法是不是最好最好的方法了,反正我认为我自己现在用的方法应该是很好的了,,不说最好,因为我知道我还能在现在的基础上稍微的修改让它变为更好....只是感觉无所谓了,因为现在所用的方法对于99.9999%的项目都适用....

    好像自己在吹牛一样,,,,,其实写这篇文章呢,,,也早就想写了,因为感觉好东西应该拿出来分享一下,希望亲们能派的上用场

    先给大家源码一个51的一个32的

    链接:https://pan.baidu.com/s/1ZFcJYEWwMCXZYyRUVjknKQ 密码:i4pl

    先看第一个问题,,,,大家用串口发送数据的时候是怎么发的???

    大多数人是不是还是这样

    for(i=0;i<30;i++)
    {
    	UartSend(Data[i]);
    }
    
    void UartSend(unsigned char value) 
    {
    	ES=0;  //关闭串口中断--使用Printf 需要屏蔽 
    	TI=0;   //清发送完毕中断请求标志位   
    	SBUF=value; //发送  
    	while(TI==0); //等待发送完毕   
    	TI=0;   //清发送完毕中断请求标志位 --使用Printf 需要屏蔽   
    	ES=1;  //允许串口中断  --使用Printf 需要屏蔽 
    }
    

    上面是51的,只是打比方哈

     就是说

    for(i=0;i<30;i++)
    {
      UartSend(Data[i]);
    }

    直接说弊端,举个例子

    void main()
    {
    	InitUART(9600);
    	InitTimer0();
    	while(1)
    	{
    		for(i=0;i<130;i++)
    		{
    			UartSend(Data[i]);
    		}
    		DataConfig();//采集数据
                    DataSendMethod();//接收串口命令
    		......
             }
    }
    

    每次都要发130个数据,,是不是每次循环要等着发完上面的130个数据才执行下面的函数,,,这样话就不能及时的执行后面的函数,,,,刚想起来有没有人经常在主循环里面加延时的?????水平不高的人,,才会经常在主循环里面加延时

    那应该怎么发....用中断发

    先看最普通的,用51写的一个

    /**
    * @brief  串口发送函数中断函数
    * @param  
    * @param  None
    * @param  None
    * @retval None
    * @example 
    **/
    void UartSendTI(unsigned char *value,int DataLen) 
    {
    	UsartSendData = value;
    	UsartSendDataCnt = DataLen;
    	TI = 1;
    }
    

      

    void UARTInterrupt(void) interrupt 4
    {
    	if(TI)
    	{
    		TI = 0;
    		if(UsartSendDataCnt>0)
    		{
                      SBUF = *UsartSendData++;
    		  UsartSendDataCnt--;
                    }
    		else
    		{
                      TI = 0;
                    }
            }
    }
    

     

    发送数据的时候直接

    UartSendTI(AD0123Table,21);

    AD0123Table是一个数组

    void main()
    {
        InitUART(9600);
        InitTimer0();
        while(1)
        {
            UartSendTI(AD0123Table,21);
            
            DataConfig();//采集数据
            
           }
    } 

    这样的话这个函数

    UartSendTI(AD0123Table,21)

    只是告诉串口去发送数据,并不会占用主循环多少时间,然后就立马执行下面的函数去了,其实现在的情况就是不停的进中断发送数据

    又不停的从中断里面出来执行主循环,,,这种方式想一想是不是要比以前那样好多了???

    现在再升级一下

    大家有没有看过我这篇文章

    http://www.cnblogs.com/yangfengwu/p/6822984.html 关于环形队列的

    看到那个环形队列的程序是不是有头疼的????如果谁读起来吃力,说明底子还是不行哈,,,

    指针理解的怎么样???结构体会不会,,结构体指针呢????不会没有啥法子说明自己懒,也有可能没有遇到好老师....突然想起来一句话

    会的多的人写程序又快有好,也很轻松,因为他会复制粘贴,,,,有些人会问那不会的也能啊!!问一句,不会的,你敢复制粘贴不,

    即使复制粘贴了,你知道怎么用不????即使能用了稳定性上能得到保证不????

    其实以前我也是不会这些东西,没有人天生就会....我呢虽然笨,但是手很勤快,遇到不会的自己就会反复的敲程序测试,,所以后来

    会的多了,自学能力也很强了..别人都说我很聪明,有些人还会说我是学电气的天才....记住一句话:天才在于努力

    先说一下是如何发送数据的,环形队列又是一个什么东东

    我现在往数组里面存数据

    然后我再往里面存,,对了存数据是用的操作环形队列的函数哈  int32_t rbWrite(rb_t *rb, const void *data, size_t count)

     我又存了两个,,如果存满了还存,就会报错,,所以咱呢先取两个再存,,取数据也是用的环形队列的函数

    然后咱们再存两个吧!!

     

    具体是如何实现的就看这两个吧

    函数在32的工程里面,51享受不起.....内存堪忧

    我发送数据的时候就是直接往这个数组里面存数据,串口从这个数组里面取数据然后发出去(当然这个是在程序中设置的)

    那个数组就是一直在转圈圈......

    曾经就有一个问题就是利用环形队列解决的

    http://www.cnblogs.com/yangfengwu/p/6921832.html

    简单来说就是把接收到的数据写到Flash里面....但是呢单片机的内存有限,不能够一次性接收到所有的数据......所以我就

    利用环形队列..一边串口接收着往环形队列里面写数据,一边从环形队列里面读出数据写到Flash里面....

    现在看如何利用环形队列发送串口数据

    void rbCreate(rb_t* rb,u8 *Buff,uint32_t BuffLen)//创建或者说初始化环形缓冲区
    {
        if(NULL == rb)
        {
            printf("ERROR: input rb is NULL
    ");
            return;
        }
        rb->rbCapacity = BuffLen;//数组的大小
        rb->rbBuff = Buff;//数组的地址
        rb->rbHead = rb->rbBuff;//头指向数组首地址
        rb->rbTail = rb->rbBuff;//尾指向数组首地址
    }
    

      

    先看发送,这是在中断里面,就是如果数组里面有数据就一个一个取出来发出去

    这是串口1 的,我定义了三个 Uart1rb  Uart2rb  Uart3rb  分别操作  Usart1SendBuff    Usart2SendBuff    Usart3SendBuff  这三个数组

    if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
      {
        if(rbCanRead(&Uart1rb)>0)//如果环形队列里面的数据个数大于0
        {
          rbRead(&Uart1rb, &Usart1SendDat, 1);//读取一个数据
          USART_SendData(USART1, Usart1SendDat);//发送
        }
        else
        {
          //发送字节结束
          USART_ClearITPendingBit(USART1,USART_IT_TXE);
          USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
          USART_ITConfig(USART1, USART_IT_TC, ENABLE);
        }
      }

     现在看怎么存,应该说怎么控制串口发送数据

     
    int32_t PutData(rb_t *rb ,USART_TypeDef* USARTx, uint8_t *buf, uint32_t len)
    {
        int32_t count = 0;
        if(NULL == buf)
        {
            printf("ERROR: gizPutData buf is empty 
    ");
            return -1;
        }
        count = rbWrite(rb, buf, len);//存入数据
        if(count != len)
        {
            printf("ERROR: Failed to rbWrite 
    ");
            return -1;
        }
        USART_ITConfig(USARTx, USART_IT_TXE, ENABLE);//然后控制打开哪一个中断
        return count;
    }
    
    

    发送数据的时候直接往里面丢数据就可以了

    PutData(rb_t *rb ,USART_TypeDef* USARTx, uint8_t *buf, uint32_t len)

    再高级一点,加上DMA,用DMA就不能用环形队列了,其实下面大神介绍的用内存分配的方式,实质就是用链表,但是呢!我没想明白把数据放进

    链表然后设置一下DMA和直接用数组的方式设置一下DMA有多大区别,.....或许我还是没有明白那位大神的用意......所以我就还是用的现在

    的环形队列的方式.....

    可以看一下这位大神的介绍

    https://wenku.baidu.com/view/c2b959f0caaedd3383c4d3d7.html

     

    现在先问一下,如果让大家传输一个220.5的数据给上位机,大家如何传输??假设不加任何的标志校验什么的

    我的话

    看一下

    typedef union Resolverf//STM32为小端模式
    {
        float Data;
        char  Data_Table[4];
    }ResolverfData;
    
    typedef union ResolverLong//STM32为小端模式
    {
        long Data;
        char  Data_Table[4];
    }ResolverLongData;
    
    DATAGATHER_C_ ResolverfData ResolverData;//解析单精度浮点型数据
    DATAGATHER_C_ ResolverLongData ResolverLongDat;//解析整形

    实际上220.5 用16进制表示就是 43 5C  80  00  高位在前

    其实现在所有的仪器仪表的通信都是走的这种

    链接:https://pan.baidu.com/s/1he-dK4_FUh9hXZBa9dpKwg 密码:2f9x 

    如果是整形

    就用

    用共用体直接就可以实现两边的转换,

            ResolverData.Data_Table[0] = 0x00;
            ResolverData.Data_Table[1] = 0x80;
            ResolverData.Data_Table[2] = 0x5C;
            ResolverData.Data_Table[3] = 0x43;
            
            
            printf("%f
    ",ResolverData.Data);

     

    如果用上位机列如C#的,一个函数就搞定

    byte[] FloatDataMore = new byte[4];
    FloatDataMore[0] = 0x00;
    FloatDataMore[1] = 0x80;
    FloatDataMore[2] = 0x5C;
    FloatDataMore[3] = 0X43;
    
    float f = BitConverter.ToSingle(FloatDataMore, 0);//转换
    
    f = 220.5

    所以传输数据还是按照规范来

    说一下小端模式,其实就是在说数据存储的时候数据的低位存在了低地址,数据的高位存在了高地址

    就像上面的220.5 用stm32解析数据后存储的情况 0x00 0x80 0x5C 0x43 

    0x00是不是存在了数组的地位上哈,,,0x43存在了数组的高位上...

    看一下51 的

    大家可以用51去试一试会发现和32的正好相反

    0x43  高位存在了数组的低位上,,   0x00 存在了数组的高位上

     其实就是在说数据存储的时候数据的高位存在了低地址,数据的低位存在了高地址,,,就是大端模式

    一般我发送数据会在最后加CRC16校验   

    /**
    * @brief  计算CRC
    * @param  *modbusdata:数据指针
    * @param  length:数据长度
    * @param  
    * @retval 计算的CRC值
    * @example 
    **/
    unsigned int crc16_modbus(unsigned char *modbusdata, char length)
    {
        char i, j;
        unsigned int crc = 0xffff;//有的用ffff有的用0
        for (i = 0; i < length; i++)
        {
            crc ^= modbusdata[i];
            for (j = 0; j < 8; j++)
            {
                    if ((crc & 0x01) == 1)
                    {
                            crc = (crc >> 1) ^ 0xa001;
                    }
                    else
                    {
                            crc >>= 1;
                    }
            }
        }
        
        return crc;
    }
        crc = crc16_modbus(AD0123Table,19);
        
        AD0123Table[19] = crc&0xff;
        AD0123Table[20] = (crc>>8)&0xff;
        
      UartSendTI(AD0123Table,21);

    现在看接收.....算了明天再写吧,感觉这些够消化的了....................

    程序里面所有的函数都封装好了,关键是自己亲自去尝试

    下一篇链接  http://www.cnblogs.com/yangfengwu/p/8912072.html

  • 相关阅读:
    jmeter之正则表达式
    python_appium搭建APP自动化测试环境
    网络编程
    四种单例模式
    Pycharm快捷键
    面向对象
    阶段性总结
    模块之numpy,pandas,matplotlib
    模块之json,pickle,os,sys,logging
    模块之Time,datatime,hashlib,hmac
  • 原文地址:https://www.cnblogs.com/yangfengwu/p/8628219.html
Copyright © 2011-2022 走看看