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\n");
        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 \n");
        return -1;
    }
    count = rbWrite(rb, buf, len);//存入數據
    if(count != len)
    {
        printf("ERROR: Failed to rbWrite \n");
        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\r\n",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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM