补充:

程序优化

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

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

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

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

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

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

大多数人是不是还是这样

1

2

3

4

5

6

7

8

9

10

11

12

13

14

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]);
}

直接说弊端,举个例子

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

void main()

{

    InitUART(9600);

    InitTimer0();

    while(1)

    {

        for(i=0;i<130;i++)

        {

            UartSend(Data[i]);

        }

        DataConfig();//采集数据

                DataSendMethod();//接收串口命令

        ......

         }

}

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

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

/**

* @brief  串口发送函数中断函数

* @param 

* @param  None

* @param  None

* @retval None

* @example

**/

void UartSendTI(unsigned char *value,int DataLen)

{

    UsartSendData = value;

    UsartSendDataCnt = DataLen;

    TI = 1;

}

  

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

void UARTInterrupt(void) interrupt 4

{

    if(TI)

    {

        TI = 0;

        if(UsartSendDataCnt>0)

        {

                  SBUF = *UsartSendData++;

          UsartSendDataCnt--;

                }

        else

        {

                  TI = 0;

                }

        }

}

 

发送数据的时候直接

UartSendTI(AD0123Table,21);

AD0123Table是一个数组

1

2

3

4

5

6

7

8

9

10

11

12

void main()

{

    InitUART(9600);

    InitTimer0();

    while(1)

    {

        UartSendTI(AD0123Table,21);

        

        DataConfig();//采集数据

        

       }

}

  

这样的话这个函数

1

<span style="font-size: 18px;">UartSendTI(AD0123Table,21);<br>只是告诉串口去发送数据,并不会占用主循环多少时间,然后就立马执行下面的函数去了,其实现在的情况就是不停的进中断发送数据<br>又不停的从中断里面出来执行主循环,,,这种方式想一想是不是要比以前那样好多了???<br><br>现在再升级一下<br>大家有没有看过我这篇文章<br><a href="http://www.cnblogs.com/yangfengwu/p/6822984.html" target="_blank">http://www.cnblogs.com/yangfengwu/p/6822984.html</a>  关于环形队列的<br><br>看到那个环形队列的程序是不是有头疼的????如果谁读起来吃力,说明底子还是不行哈,,,<br>指针理解的怎么样???结构体会不会,,结构体指针呢????不会没有啥法子说明自己懒,也有可能没有遇到好老师....突然想起来一句话<br>会的多的人写程序又快有好,也很轻松,因为他会复制粘贴,,,,有些人会问那不会的也能啊!!问一句,不会的,你敢复制粘贴不,<br>即使复制粘贴了,</span><span style="font-size: 18px;">你知道怎么用不????即使能用了稳定性上能得到保证不????</span>

1

<span style="font-size: 18px;">其实以前我也是不会这些东西,没有人天生就会....我呢虽然笨,但是手很勤快,遇到不会的自己就会反复的敲程序测试,,所以后来<br>会的多了,自学能力也很强了..别人都说我很聪明,有些人还会说我是学电气的天才....记住一句话:天才在于努力<br><br>先说一下是如何发送数据的,环形队列又是一个什么东东<br><img src="https://images2018.cnblogs.com/blog/819239/201803/819239-20180323040015001-849462310.png" alt=""></span>

我现在往数组里面存数据

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

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

然后咱们再存两个吧!!

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

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

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

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

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

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

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

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

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

1

2

3

4

5

6

7

8

9

10

11

12

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);}}

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

1

<span style="font-size: 18px;"> </span>

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;
}

1

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

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

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

1

<span style="font-size: 18px;">说一下小端模式,其实就是在说数据存储的时候数据的低位存在了低地址,数据的高位存在了高地址<br><br>就像上面的220.5  用stm32解析数据后存储的情况 0x00 0x80  0x5C  0x43 <br>0x00是不是存在了数组的地位上哈,,,0x43存在了数组的高位上...<br><br>看一下51 的<br><br><img src="https://images2018.cnblogs.com/blog/819239/201803/819239-20180323062311161-1116961849.png" alt=""></span>

大家可以用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有的用0for (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

1-关于单片机通信数据传输(中断发送,大小端,IEEE754浮点型格式,共用体,空闲中断,环形队列)相关推荐

  1. openmv识别物体并与单片机通信(STM32)

    openmv识别物体并与单片机通信(STM32) OPENMV端 物体识别 感光元件 寻找色块 画图 距离判断 距离 实际距离 串口发送 STM32段 串口接收 串口2配置 数据处理 数据显示 Ope ...

  2. STM32单片机串口空闲中断接收不定长数据

    在使用单片机的串口通信功能时,常用的接收数据方法是通过固定的字节数来判断一帧数是否发送完成,或者是通过固定的结束标志位来表示一帧数据发送完成.但是有时候会遇到发送的数据长度不固定,也没有固定的结束标志 ...

  3. 关于STM32空闲中断

    有一次做一个东西,为了尽量不占用CPU的处理数据时间,所以就使用DMA接收串口的数据,但是呢问题来了.,,,,,怎么样才能确定接收到了一条完整的数据了,,我们都知道只要打开DMA 那家伙就不停的把接收 ...

  4. STM32 hal库串口空闲中断最新用法

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.串口空闲中断是什么? 二.使用步骤 1.hal库空闲中断接收函数 2.使用方法 3.最终效果 实际效果 总结 前言 ...

  5. 【无标题】STM32F767串口空闲中断+DMA实现不定帧长度的接收

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.配置串口与DMA 二.空闲中断服务函数 二.串口+DMA发送 三.主函数 总结 前言 提示:这里可以添加本文要记录 ...

  6. STM32-HAL库串口DMA空闲中断的正确使用方式+解析SBUS信号

    STM32-HAL库串口DMA空闲中断的正确使用方式+解析SBUS信号 一. 问题描述 二. 方法一--使用HAL_UART_Receive_DMA 三. 方法二--使用HAL_UARTEx_Rece ...

  7. stm32: 串口空闲中断的实现(HAL库)

    STM32利用串口空闲中断来分包(HAL库) 文章目录 STM32利用串口空闲中断来分包(HAL库) 1. 开发环境 2. 串口中断接收的问题和解决办法 3. 串口空闲中断分包的原理 4. STM32 ...

  8. GD32F4xx串口收发,DMA+空闲中断

    GD32F4xx系列的串口收发DMA+空闲中断基础配置:(这里主要是以DMA+空闲中断为例) 原理就不赘述了,网上资料很多,这里直接进行配置和测试. 1,首先添加GD32F4xx的dma外设库函数文件 ...

  9. 串口的空闲中断+DMA接收(附F4代码)

    传统串口接受与发送: 串口接受一个很长的帧,接受帧时依靠串口中断每次只能传输8位,传一个帧要进入好多次中断,每次进入中断都要判断是否接收完毕. DMA串口接收与发送: 1,电脑通过串口1给STM32F ...

最新文章

  1. CSS3弹性盒模型之box-orient box-direction
  2. errors_impl.InvalidArgumentError: Input to reshape is a tensor
  3. Zabbix客户端配置环境检查
  4. 用python写九九乘法口诀表左上角_python打出九九乘法口诀表
  5. 动态规划编程面试_面试的前25大动态编程问题
  6. 转载:【微信小程序】 wx:if 与 hidden(隐藏元素)区别
  7. 用ghost备份和还原Linux系统
  8. 我们是否应该保留同时多线程?
  9. RabbitMQ入门-Topic模式
  10. 正则表达式之非捕获组
  11. svn下载项目到指定文件夹,以及更新提交
  12. axure动态面板的简单介绍
  13. 尚硅谷Javase项目一(家庭收支记账软件)
  14. 计算机控制技术ipc是什么意思啊,CPU的IPC性能是什么意思?通俗易懂科普处理器IPC性能含义...
  15. Qt 读取Excel数据
  16. 等待是一个过程,每一种坚守都是幸福
  17. php aes 256 加解密,PHP完整的AES加解密算法使用及例子(256位)
  18. html hr线条粗细,html中hr怎么设置粗细
  19. 博途PLC和CODESYS平台下FB编程应用(如何实例化多个FB)
  20. c语言规定 标识符由,【填空题】C语言的标识符命名规则规定标识符可以由字母数字和下划线组成,首字符不能是 。 (4.0分)...

热门文章

  1. 【计算机毕业设计】018实习记录
  2. vscode ( Visual Studio Code )设置中文、字体和字号
  3. android修改输出分辨率,编译Android8.1修改默认分辨率和屏幕密度
  4. H264编码器5( x264源代码简单分析:x264_slice_write() 与H264 编码简介)
  5. pytorch中的数据加载(dataset基类,以及pytorch自带数据集)
  6. 超好用的清理软件Wise Disk Cleaner X
  7. 社会工程:安全体系中的人性漏洞pdf
  8. 2022.02.23_HTML+CSS学习总结_CSS初识、选择器与标签的分类
  9. 三:Job 和 JobDetail 详细介绍
  10. HandlerMethodReturnValueHandler处理返回值问题,aop