zoukankan      html  css  js  c++  java
  • 单片机模块化程序: 数据缓存封装-环形队列实现

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

    前言

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

      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;

      

    提醒:

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

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

      

      

     

  • 相关阅读:
    hdu4521(线段树+dp)
    【Leetcode】Container With Most Water
    【Leetcode】Trapping Rain Water
    【Leetcode】Valid Sudoku
    【Leetcode】Combinations
    【Leetcode】Permutations II
    【Leetcode】Permutations
    【Leetcode】Permutation Sequence
    【Leetcode】Next Permutation
    【Leetcode】Remove Element
  • 原文地址:https://www.cnblogs.com/yangfengwu/p/12228402.html
Copyright © 2011-2022 走看看