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();
InitTimer0();
while()
{
UartSendTI(AD0123Table,); 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
{
rbRead(&Uart1rb, &Usart1SendDat, );//读取一个数据
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 = ;
if(NULL == buf)
{
printf("ERROR: gizPutData buf is empty \n");
return -;
}
count = rbWrite(rb, buf, len);//存入数据
if(count != len)
{
printf("ERROR: Failed to rbWrite \n");
return -;
}
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[];
}ResolverfData; typedef union ResolverLong//STM32为小端模式
{
long Data;
char Data_Table[];
}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[] = 0x00;
ResolverData.Data_Table[] = 0x80;
ResolverData.Data_Table[] = 0x5C;
ResolverData.Data_Table[] = 0x43; printf("%f\r\n",ResolverData.Data);
如果用上位机列如C#的,一个函数就搞定
byte[] FloatDataMore = new byte[];
FloatDataMore[] = 0x00;
FloatDataMore[] = 0x80;
FloatDataMore[] = 0x5C;
FloatDataMore[] = 0X43; float f = BitConverter.ToSingle(FloatDataMore, );//转换 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 = ; i < length; i++)
{
crc ^= modbusdata[i];
for (j = ; j < ; j++)
{
if ((crc & 0x01) == )
{
crc = (crc >> ) ^ 0xa001;
}
else
{
crc >>= ;
}
}
} return crc;
}
crc = crc16_modbus(AD0123Table,); AD0123Table[] = crc&0xff;
AD0123Table[] = (crc>>)&0xff; UartSendTI(AD0123Table,);
现在看接收.....算了明天再写吧,感觉这些够消化的了....................
程序里面所有的函数都封装好了,关键是自己亲自去尝试
下一篇链接 http://www.cnblogs.com/yangfengwu/p/8912072.html
1-关于单片机通信数据传输(中断发送,大小端,IEEE754浮点型格式,共用体,空闲中断,环形队列)的更多相关文章
- 2-关于单片机通信数据传输(中断接收,大小端,IEEE754浮点型格式,共用体,空闲中断,环形队列)
上一篇链接 http://www.cnblogs.com/yangfengwu/p/8628219.html 先说明一点这种方式,不光对于单片机类的,,对于上位机接收数据同样适用----不骗人的,自己 ...
- C++/java之间的Socket通信大小端注意事项
在一个物联往项目中,需要java云平台与一个客户端做socket定制协议的通信:然而在第一次测试时,并没有按照预想的那样完成解析.查找资料以后是因为客户端的数据读取方式为小端模式,而java默认采用大 ...
- MM32F0020 UART1中断接收和UART1中断发送
目录: 1.MM32F0020简介 2.初始化MM32F0020 UART1和NVIC中断 3.编写MM32F0020 UART1使能中断发送函数 4.编写MM32F0020 UART1中断接收和中断 ...
- MM32F0140 UART1中断接收和UART1中断发送
目录: 1.MM32F0140简介 2.初始化MM32F0140 UART1和NVIC中断 3.编写MM32F0140 UART1使能中断发送函数 4.编写MM32F0140 UART1中断接收和中断 ...
- 关于STM32空闲中断
有一次做一个东西,为了尽量不占用CPU的处理数据时间,所以就使用DMA接收串口的数据,但是呢问题来了.,,,,,怎么样才能确定接收到了一条完整的数据了,,我们都知道只要打开DMA 那家伙就不停的把接收 ...
- MM32F0140 UART1空闲中断接收
目录: 1.MM32F0140简介 2.初始化MM32F0140 UART1空闲中断和NVIC中断 3.编写MM32F0140 UART1中断接收和空闲中断函数 4.编写MM32F0140 UART1 ...
- MM32F0020 UART1空闲中断接收
目录: 1.MM32F0020简介 2.初始化MM32F0020 UART1空闲中断和NVIC中断 3.编写MM32F0020 UART1中断接收和空闲中断函数 4.编写MM32F0020 UART1 ...
- 51单片机-PC数据传输 温度 距离 监控系统设计
>_<:功能概述: 通过串口PC和单片机通信,可以询问单片机测得的温度,可以询问声呐测距的测量距离,同时把测量温度显示在数码管上. >_<:PC部分 这里com.cpp和com ...
- ucos-iii串口用信号量及环形队列中断发送,用内建消息队列中断接收
串口发送部分代码: //通过信号量的方法发送数据 void usart1SendData(CPU_INT08U ch) { OS_ERR err; CPU_INT08U isTheFirstCh; O ...
随机推荐
- Flink1.4.0连接Kafka0.10.2时遇到的问题
Flink1.4.0连接部署在Linux上的Kafka0.10.2时,报如下异常: org.apache.flink.streaming.connectors.kafka.FlinkKafkaCons ...
- java工程打jar包
下面记录一下打jar包的方法 对于含有第三方jar的工程需要写MANIFEST.MF文件,文件结构如下: Manifest-Version: 1.0 .jar .jar Main-Class: Sel ...
- JSP基本语法总结【1】(jsp工作原理,脚本元素,指令元素,动作元素)
时隔半年,回头对jsp复习整理一下,温故而知新. jsp工作原理: jsp服务器管理jsp页面分两个阶段:转换阶段(translation phase)和执行阶段(execution phase). ...
- GenyMotion the virtual device got no ip address 问题解决
不要再找答案了 升级你的virtual box到最新版本(目前是 5.0.26,已通过) 如果你是windows 10系统 必须关闭hyper-v 在管理员命令行下运行bcdedit /set hyp ...
- ie8 透明背景不能点击问题
最近开发网站,需求是三个一屏,1和3只能看见一半,2显示在中间,无箭头按钮. 因为之前写过一个有前后按钮的插件,想着怎么就在这上面改造,故把前后按钮去掉背景,定位在了1和3的位置上来实现点击前后, 发 ...
- go语言浮点数
package main import "fmt" func main() { var num,num1 float32 num = 10 num1 = 100 fmt.Print ...
- leveldb源码分析--Comparator
既然leveldb是一个按Key序组织的LSM-Tree实现,那么对于Key的比较就是非常之重要了,这个Key的比较在leveldb中是Comparator的形式出现的.我们首先来看看Comparat ...
- Jmeter中自动重定向与跟随重定向的区别
一.重定向就是通过各种方法将各种网络请求重新定个方向转到其它位置. 二.我们在网站建设中,时常会遇到需要网页重定向的情况: 1.网站调整(如改变网页目录结构): 2.网页被移到一个新地址: 3.网页扩 ...
- commons-pool 解析
首先抛出个常见的长连接问题: 1 都知道连接MySQL的应用中大多会使用框架例如 c3p0 ,dbcp proxool 等来管理数据库连接池. 数据库连接池毫无疑问都是采用长连接方式. 那么MySQ ...
- 光杆mdf文件的导入
场景,准备学习SSAS的时候,按照教程在微软下载了示例数据库AdventureWorksDW2012,下载来才发现只有一个mdf文件. 正好今天群里有位兄弟也碰到差不多的问题,客户数据库里的ldf文件 ...