单片机模块化程序: 数据缓存封装-环形队列实现

------------恢复内容开始------------

前言

  在上一节只是稍微说了下数据缓存

  https://www.cnblogs.com/yangfengwu/p/11769059.html

  这节为了可以让大家直接应用起来,我封装了下.

  咱们平时发送数据的时候最希望的是可以有个缓存,每次把要发送的数据存到缓存里面

  需要发送的时候咱就去缓存里面去取

  而且咱希望咱的缓存可以存储多条数据

  发送的时候按照先进先出的原则把每条数据提取出来发出去

前要

  一,作为一个资深的C语言开发者,应该知道的东西

         假设是一个32位的单片机,定义了一个32位的变量int a;

      假设变量a的地址是:0x20000000-0x20000003

        在32位的单片机中,int型占四字节

    然后说一下 memcpy

    假设 a = 256   

    试问调用以下程序,可不可以把a的值拷贝到b里面??

    memcpy(&b,&a,1)

    首先大家需要明确,memcpy默认拷贝一个字节

    所以,并不能把a的值复制给b

    如果想把a的值复制给b

    memcpy(&b,&a,4)

缓存源码使用

  一,注意,这一节是对上一节的封装,请先看上一节

    这节增加了 BufferManage 文件

      

  二,定义一个管理变量

    buff_manage_struct buff_manage_struct1;

    

  三,定义两个数组

    一个用于交给环形队列用于缓存数据:u8  buff[1024];

    请必须定义成u8 / uint8_t  /char   等等1字节类型的

    请必须定义成u8 / uint8_t  /char   等等1字节类型的

    请必须定义成u8 / uint8_t  / char  等等1字节类型的

    数组大小是1024字节,最大缓存1024字节数据

    另一个交给环形队列用于记录每次缓存数据的个数:u32 Managebuff[10];   

    请必须定义为 u32 / uint32_t  / int32_t  等等4字节类型的

    请必须定义为 u32 / uint32_t  / int32_t  等等4字节类型的

    请必须定义为 u32 / uint32_t  / int32_t  等等4字节类型的

 

    数组个数是10个,说明最多可以管理10条数据

    u32 类型,说明记录的每条数据个数最大长度是 2^32  个

    

  四, 调用 BufferManageCreate函数

    

    BufferManageCreate(&buff_manage_struct1, buff, 1024, Managebuff, 10*4);

    注:

    &buff_manage_struct1  :数据缓存管理变量

    buff :  传入u8  buff[1024] 数组的首地址

    1024 : u8  buff[1024] 数组的长度

    Managebuff : 传入u32 Managebuff[10] 数组的首地址

    10*4 :u32 Managebuff[10] 的数组长度*空间所占字节数

    注:请大家一致定义成 u32类型,最后的参数统一填写: 数组长度*4

    本人封装的BufferManage程序默认内部按照u32类型处理

    

  五,定义一个数组,用于提取缓存的数据

    注意:提取先前存储的一条数据,然后缓存到一个数组里面:u8  SendBuff[1024];

    最后操作这个数组

    

测试1

  说明:我定义了一些通用变量,用户可随意使用

  

  二,每隔3S插入17字节的数据

  主循环只要判断插入了数据就取出来用串口发送出去

    

    插入数据

    BufferManageWrite(&buff_manage_struct1,temp,17);

    提取数据

    buff_manage_struct1.SendLen = BufferManageRead(&buff_manage_struct1,SendBuff);

  三,测试

    

#include "include.h"
#include "BufferManage.h"

buff_manage_struct buff_manage_struct1;


u8  buff[1024];//»º´æÊý¾ÝµÄÊý×é
u32 Managebuff[10];//¼Ç¼ÿ´Î»º´æ¶àÉÙÊý¾ÝµÄÊý×é


u8  SendBuff[1024];//ÌáÈ¡Ò»ÌõÊý¾ÝÖ®ºó´æ´¢µÄÊý×é


char temp[17]="111111111111111
";

int main(void)
{
  NVIC_Configuration();
    uart_init(115200);     //´®¿Ú³õʼ»¯Îª115200
    GpioInit();
    DelayInit();
    

    BufferManageCreate(&buff_manage_struct1, buff, 1024, Managebuff, 10*4);
    while(1)
    {    
        if(SysTickCntMs>=3000)
        {
            BufferManageWrite(&buff_manage_struct1,temp,17);//²åÈëÊý¾Ý
            SysTickCntMs=0;
        }

        buff_manage_struct1.SendLen = BufferManageRead(&buff_manage_struct1,SendBuff);//ÌáÈ¡Êý¾Ýµ½SendBuffÊý×é
        if(buff_manage_struct1.SendLen>0)//ÓÐÊý¾ÝÐèÒª·¢ËÍ
        {
            UsartOutStr(SendBuff,buff_manage_struct1.SendLen);
        }
        
        
//    if(Usart1ReadFlage)//´®¿Ú½ÓÊÕÍêÒ»ÌõÍêÕûµÄÊý¾Ý
//        {
//          Usart1ReadFlage=0;
//            memset(Usart1ReadBuff,NULL, sizeof(Usart1ReadBuff));//ÇåÁã
//        }
    }
}

测试2

  每隔3S插入两份数据:用来模拟不定期插入多份数据

  

  

  注:提取数据是一条一条的提取

  咱可以加点延时让每条数据之间加点延时

  注:延时1000ms是控制每隔1000ms从里面提取一条数据

  

  

  实际应用中直接加延时其实是不可取的.但是咱们又想让每条数据

  之间有延时,否则就会当做一条数据

以上测试说明

  其实简而言之

  在需要发送数据的地方

  只需要调用

  BufferManageWrite(&buff_manage_struct1,temp,17);//插入数据

  在需要提取发送的地方调用一下函数

  Len = BufferManageRead(&buff_manage_struct1,SendBuff);//提取数据到SendBuff数组
  if(Len>0)
  {
    操作发送的数据:SendBuff   数据长度:Len
  }

  这样便会一条数据一条数据的提取出来,然后最终如何操作如何应用自行发挥.

扩展:使用串口中断发送缓存的数据

  一,首先先说明处理思路

    如果缓存区没有数据,则每隔1ms查询一次

    如果查询到了有数据,则提取出来,然后交由中断处理

    然后查询间隔变为10ms (该间隔可调节)

    10ms便是发送每一条数据之间的时间间隔

  二,1Ms定时器增加以下程序

    

    

    if(Usart1ManageSendDatLen == 0)//没有在发送数据
    {
        Usart1ManageSendDelayCnt++;
        if(Usart1ManageSendDelayCnt>=1000)//延迟10Ms
        {
            Usart1ManageSendDelayCnt = 0;
            if(rbCanRead(&Uart1rbManage)>0)//是不是有需要发送的数据
            {
                //取出这次要发送的数据个数
                rbRead(&Uart1rbManage, &Usart1ManageSendDatLen, 1);
                USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//打开发送中断
            }
            else
            {
                //没有数据发送的时候1Ms判断一次,延迟1Ms
              Usart1ManageSendDelayCnt=10;
            }
        }
    }

   三,串口中断里面

    

  

    if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
    {        
        if(buff_manage_struct1.SendLen>0)//发送的数据个数大于0
        {
            USART_SendData(USART1, Usart1SendBuff[buff_manage_struct1.Count]);//发送
            buff_manage_struct1.Count++;
            buff_manage_struct1.SendLen -- ;//发送的数据个数减一
        }
        else//发送字节结束
        { 
            USART_ClearITPendingBit(USART1,USART_IT_TXE);
            USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
            USART_ITConfig(USART1, USART_IT_TC, ENABLE);
        }
    }
  //发送完成
  if (USART_GetITStatus(USART1, USART_IT_TC) != RESET)
  {
    USART_ClearITPendingBit(USART1,USART_IT_TC);
    USART_ITConfig(USART1, USART_IT_TC, DISABLE);
  }

   四:测试

  主函数还是每隔3S插入两条数据

  

  

 

扩展:使用DMA串口发送缓存的数据

一,我先留给大家去完善,我给大家一些提示

  简而言之:如果有数据需要发送,就设置下数据地址和数据个数,然后启动DMA

  详细方案1:

  每次存入数据以后,判断下DMA是否传输完成,如果传输完成,则重新赋值以后启动

  在DMA发送完成中断里面判断是否有数据需要发送

  如果有,则重新赋值以后启动

  详细方案2:

  上述方案有个问题在于每条数据之间没有了固定的时间间隔

  有可能造成粘包.

  咱还是利用定时器,把以下红框改为:

  重新赋值DMA以后启动DMA.

  别忘了在DMA发送完成中断里面:  buff_manage_struct1.SendLen = 0;

  

提醒:

  大家使用缓存的时候需要多考虑一件事情:中断打断

  为避免存和取之间由于中断打断而造成以外情况,设置了中断保护

  

  

 

原文地址:https://www.cnblogs.com/yangfengwu/p/12228402.html