gph 发表于 2023-8-4 23:25:51

学习笔记(串口接收)

本帖最后由 gph 于 2023-8-4 23:20 编辑

串口接收数据
1、串口通讯:是指外设和计算机间,通过数据、信号线、地线等,按位进行传输数据的一种通讯方式。串口是一种接口标准,它规定了接口的电气标准,没有规定接口插件电缆以及使用的协议。串口按电气标准及协议来划分,包括RS-232-C、RS-422、RS485等。2、串口通讯的数据格式一个字符一个字符地传输,每个字符一位一位地传输,并且传输一个字符时,总是以“起始位”开始,以“停止位”结束,字符之间没有固定的时间间隔要求。传输时每一位的信号宽度与波特率有关,波特率越高,宽度越小,在进行传输之前,双方一定要使用同一个波特率设置。3、通讯方式单工模式:数据传输是单向的。通信双方中,一方固定为发送端,一方则固定为接收端。信息只能沿一个方向传输,使用一根传输线。半双工模式:通信使用同一根传输线,既可以发送数据又可以接收数据,但不能同时进行发送和接收。全双工模式:通信允许数据同时在两个方向上传输。4. 偶校验与奇校验在标准ASCII码中,其最高位(b7)用作奇偶校验位 。所谓奇偶校验,是指在代码传送过程中用来检验是否出现错误的一种方法,一般分奇校验和偶校验两种。奇校验规定:正确的代码一个字节中1的个数必须是奇数,若非奇数,则在最高位b7添1;偶校验规定:正确的代码一个字节中1的个数必须是偶数,若非偶数,则在最高位b7添1。5. 停止位停止位是按长度来算的 。串行异步通信从计时开始,以单位时间为间隔(一个单位时间就是波特率的倒数),依次接受所规定的数据位和奇偶校验位,并拼装成一个字符的并行字节;此后应接收到规定长度的停止位“1”。所以说,停止位都是“1”,1.5是它的长度,即停止位的高电平保持1.5个单位时间长度。一般来讲,停止位有1,1.5,2个单位时间三种长度。6. 波特率波特率就是每秒钟传输的数据位数 。波特率的单位是每秒比特数(bps),常用的单位还有:每秒千比特数Kbps,每秒兆比特数Mbps。串口典型的传输波特率600bps,1200bps,2400bps,4800bps,9600bps,19200bps,38400bps。7、串口接收数据的几种处理方式中断方式:当串口接收数据时,CPU 会被中断并执行相应的中断服务程序,从而实现对数据的处理和存储。DMA 方式:当串口接收数据时,数据会直接被存储到内存中,而不需要 CPU 的干预。查询方式:通过轮询串口接收缓冲区来检测是否有数据到达,从而实现对数据的处理和存储。FIFO 方式:通过使用硬件或者软件实现一个缓冲区,当串口接收数据时,数据会先存储到缓冲区中,然后再由 CPU 或者 DMA 方式进行处理。8、机智云这边采用的是中断方式在main.c里gizwitsInit()中进行协议相关定时器,串口初始化,数据点初始化上图主要是协议初始化接口,继续往下看的话,我们看到了一个pRb,下面是pRb定义rb_t pRb; 是环形缓冲区结构变量static uint8_t rbBuf; 是环形缓冲区数据缓存缓冲区;那什么是环形缓冲区呢?环形缓冲区是一个先进先出(FIFO)的闭环的存储空间。比如在串口处理中,串口中断接收数据直接往环形缓冲区丢数据,而应用可以从环形缓冲区取数据进行处理,这样数据在读取和写入的时候都可以在这个缓冲区里循环进行,程序员可以根据自己需要的数据大小来决定自己使用的缓冲区大小。就是用一个指针去访问该缓冲区的最后一个内存位置的的后一位置时回到环形缓冲区的起点。可以说这是一个数组,只不过有两个指针,一个指向列队头(可读),一个指向列队尾(可写)。通过移动这两个指针即可对缓冲区的数据进行读写操作了,直到缓冲区已满(头尾相接),将数据处理完,可以释放掉数据,就可以进行存储新的数据了。rbCapacity代表缓冲区的容量 rbReadIndex 代表可读 size_trbWriteIndex 代表可写rbBuff 代表缓冲区的入口地址继续往下看,rbCreate()看字母意思就知道是创造两个字,可以跳转这个函数进去看看此函数的作用用于创建缓冲区,将缓冲区的rbReadIndex/rbWriteIndex都指向缓冲区的首地址,那么也知道rbCapacity和rbBuff在上图赋值了int8_t ICACHE_FLASH_ATTR rbDelete(rb_t* rb)为删除缓冲区函数,将结构体里面的数据全部清零。int32_t ICACHE_FLASH_ATTR rbCapacity(rb_t *rb)为获取缓冲区的总容量。rbWriteIndicator(rb_t *rb)为写入指示器rbReadIndicator(rb_t *rb)为读取指示器rbCanRead(rb_t *rb)为缓冲区有多少数据可以读取 if (rb->rbWriteIndex == rb->rbReadIndex)是rbWriteIndex和rbReadIndex都指向同一个地址,可读大小为0,返回值也为0,这种情况只会出现在缓冲区还没有数据的时候,使用之后就不会出现头尾重合的现象。rbReadIndex<rbWriteIndex是为缓冲区已经写了一部分数据,但还没到最后阶段,可读的部分为(rbWriteIndex-rbReadIndex);rbReadIndex>rbWriteIndex是为缓冲区已经写满,并且从开头处重新写了数据,可读部分为rbCapacity(rb) - (rb->rbReadIndex - rb->rbWriteIndex);;rbCanWrite(rb_t *rb)为缓冲区写入的区域大小,直接用容量减去可读的区域大小( rbCapacity(rb) - rbCanRead(rb);)rbRead(rb_t *rb, void *data, size_t count)是读数据函数,从rbReadIndex处开始读,读取count个数据,放到data地址开始的数据区域。(copySz字面上是复制数据的意思)rbReadIndex<rbWriteIndex,首先要从count和可读区域大小中选最小的那个值,避免count大于可读区域的问题出现,然后使用memcpy将rbReadIndex地址开始的最小个数的数据复制到data地址开始的数据段中,之后将rbReadIndex增加相应的个数,用于下一次Read。rbReadIndex>rbWriteIndex,且count中的数据小于从rbReadIndex到缓冲区尾部的个数,就与第一种情况一样,直接复制相应内存,之后修改rbReadIndex指针即可rbReadIndex>rbWriteIndex,且count中的数据大于从rbReadIndex到缓冲区尾部的个数,这种情况先把rbReadIndex到缓冲区尾部的数据复制到data处,再把左边不会超过Tail的值复制过去,写操作中做了限制。rbWrite(rb_t *rb, const void *data, size_t count)为写数据函数,把从data指向的地址,写到rbWriteIndex指向的地址,写count个数据,返回成功写入的个数,在这里判断了要写入的数据大小要小于可写区域大小,防止数据覆盖。rbWriteIndex>rbReadIndex,且rbWriteIndex大于count到缓冲区尾部的可写数据大小,直接复制数据,假如rbWriteIndex已经到了缓冲区尾部,直接将rbWriteIndex到缓冲区首地址。rbWriteIndex>rbReadIndex,且rbWriteIndex小于count到缓冲区尾部可写数据大小,先复制数据到尾部,之后Tail回到缓冲区首地址,将剩余的部分写入进去。rbWriteIndex<rbReadIndex,这个因为已经做了数据合法判断,所以直接复制数据就行。明白串口环形机制,那数据的话,我们得到串口中断的入口。USART2_IRQHandler该函数处理USART2全局中断。中断之后直接往环行缓冲区弄进去一个数据就好了,采用这种中断方式,提高了程序的稳定性,同时操作起数据来也很方便,如果需要的时候直接去读缓冲区里的数据。


页: [1]
查看完整版本: 学习笔记(串口接收)