打印
[应用相关]

STM32串口驱动(拼音检索测试通过)(环形队列+内存动态分配+DMA)

[复制链接]
楼主: 狗啃模拟
手机看帖
扫描二维码
随时随地手机跟帖
21
狗啃模拟|  楼主 | 2021-8-5 22:28 | 只看该作者 |只看大图 回帖奖励 |倒序浏览
(1)产生内存碎片的问题。
    在运行的过程中,各个任务频繁的调用内存分配和释放,会导致原本一整块空间地址连续的区域分散成一堆物理地址上相互独立的区域,这样有可能导致一个程序需要一个较大的内存,空余的内存块没有一个连续的地址,无法分配给任务。久而久之,最后系统可能连一个很小的物理地址都分配不到,最后导致系统的崩溃。

使用特权

评论回复
22
狗啃模拟|  楼主 | 2021-8-5 22:29 | 只看该作者

使用特权

评论回复
23
狗啃模拟|  楼主 | 2021-8-5 22:31 | 只看该作者
在上图中可以看到,虽然起始地址为20000的内存区有16个空白的字节,但是仍然无法为任务分配到四个字节的物理内存。
    (2)运行的时间不确定的问题
    在free()函数中,存在着一些内存合并等功能,例如将释放完成以后,将空间上相近的两个空白区域合并为同一个,将存在内存碎片的区域重新整合,甚至可能使用了二叉树等非线性数据结构,等等操作。
    而这些函数所耗费的时间是无法确定的,在实际的应用中,对于内存这种全局变量,多个任务都要用到,为避免会存在可重入性的问题,必须采用信号同步的方法,或者暂时关闭中断的方法,来同步对各个任务对共享资源的使用。这样,导致了系统死区时间的增加,响应速度的变慢,不确定性增加。
    因此,在大多数嵌入式系统中,通常采用静态内存块池的方法。将系统空余的内存统一管理,生成一系列的大小固定的内存块池,在实际的操作中,以这一整个内存块进行操作。

使用特权

评论回复
24
狗啃模拟|  楼主 | 2021-8-5 22:32 | 只看该作者
实现过程
    首先定义内存管理块的结构体
         typedef struct OSMEMTCB{
                void                 *OSMemFreeList;//用于指向该管理区中的空白的内存块
                u8                         OSMemBlkSize;//用于该管理区中的每个内存块的字节数
                u8                         OSMemNBlks;//用于该管理区中的分为多少个内存块
                u8                         OSMemFreeNBlks;//用于该管理区还剩多少空白内存块

         }OSMEMTcb;

使用特权

评论回复
25
狗啃模拟|  楼主 | 2021-8-5 22:35 | 只看该作者
  将一个静态的存储区分配给内存配置函数,内存管理块的各个列表的含义如下图所示:

使用特权

评论回复
26
狗啃模拟|  楼主 | 2021-8-5 22:42 | 只看该作者
内存分配3 (原文件名:内存分配3.JPG)
    每个内存块的头四个字节用于存储下一个内存块的指针地址,直到倒数第一个为止,最后一个指针指向一个空的指针,表明已经到达内存区的的末端。
    在实际运用过程中,OSMemFreeList是指向空白的内存块的指针,通过它来申请内存,当申请到内存块以后,OSMemFreeList指向当前数据块的下一个内存块节点地址(内存管理函数已经自动将所有内存块通过指针链接成一个单向链表),当释放内存块的时候,将OSMemFreeList指向当前释放的内存块,将当前内存块的下一个内存块指针指向先前的OSMemFreeList。OSMemFreeNBlks保存着该内存区空白块的数量,若内存块已满,返回错误代码,OSMemBlkSize指的是每个内存块内字节数量,它的大小可以根据需要指定,理论上是它越小,内块的利用率就越高,例如保存一个101个字节的数据,若一个内存块的大小是10个字节,则需要11个内存块,若一个内存块的大小是100个字节,则需要2个内存块,最后一个内存块仅仅使用了一个字节。但并非内存块的越小越好,因为保存下个内存块节点的地址需要4个地址位,内存块越小,保存地址的数据所占比例越高。在实际操作32字节过程中,可以定义大小不同的内存块,灵活运用。

使用特权

评论回复
27
狗啃模拟|  楼主 | 2021-8-5 22:43 | 只看该作者

使用特权

评论回复
28
狗啃模拟|  楼主 | 2021-8-5 22:44 | 只看该作者
内存分配4 (原文件名:内存分配4.JPG)
   内存配置函数的核心代码:OSMemCreate(......)

使用特权

评论回复
29
狗啃模拟|  楼主 | 2021-8-5 22:45 | 只看该作者
内存分配5 (原文件名:内存分配5.JPG)


    for(i=0;i<nblks-1;i++)                              
    {
        plink=(void **)(link);                        //将二维指针定位到框的首位
        *plink=(void *)(link+blksize);        //该内存块的地址存放的
        //是第二片内存区的首地址
        link+=blksize;                                        //一维指针重新定位
    }
    //最后一个二维指针指向一个空指针

    获取内存块的核心代码:OSMemGet(......)
     tcb=(*ptr).OSMemFreeList;
    if((*ptr).OSMemFreeNBlks==0){return (void *)0;}//如果空白内存块的数量为                                                                            //返回,若正确返回,收到的数据应该是0
    (*ptr).OSMemFreeNBlks--;                        //空白内存块块数量减一
    //空白内存块指针指向下一个内存区
     //tcb指向的是内存块节点指针,不能直接使用,加上偏移值4个字节
    index=(u8 *)tcb;
    index+=4;
    //返回内存块指针
    return index;
      
    释放内存块的核心代码:OSMemDelete(......)
    (void **)tcb=(*ptr).OSMemFreeList;        //将OSMemFreeList重新指向这个已经变成空白了的指针
    (*ptr).OSMemFreeList=tcb;        //将这个空白的指针的下个指针指向原先的空白区指针
    (*ptr).OSMemFreeNBlks++;        //空白内存块数量加1

值得说明的是,工程文件中的OSQMem.h文件中
OS_MEM_MAX                    //最多允许的内存块管理区
OS_MEM_USART1_MAX     1024    //发送缓冲区的内存大小
OS_MEM_USART1_BLK     32      //每一个块的长度
      
而 USART.h文件中
DMA_MODE     //定义是采用DMA模式,还是普通的中断模式      
推荐是用DMA模式

使用特权

评论回复
30
狗啃模拟|  楼主 | 2021-8-5 22:46 | 只看该作者
再就是很多朋友可能觉得奇怪的是为什么一个是
USART1.c
USART1Cinfig.c
USART1.c是上层文件,与硬件无关,
USART1Cinfig.c是底层文件,与硬件相关,为了方便移植,只需改变USART1Cinfig.c的内容就可以,我只有STM32的板子,
Mega16的板子,和340的板子,都是我自己做的,这个程序经过移植到上述三个板子以后已经用在项目中了,在下是个菜鸟,
希望朋友们多多指教。
一直在这里学习到了很多东西,本人比较懒,老是索取而没有回报,希望能对初学的朋友们有用。
我的邮箱是linquan315@gmail.com欢迎朋友们多多交流。

使用特权

评论回复
31
狗啃模拟|  楼主 | 2021-8-5 22:48 | 只看该作者
2011年1月16日加上:
在补上几句话,告诉兄弟们怎么使用,
把工程文档的‘驱动’这个文件夹的内容加到你们的工程中就可以了,如果要使用USART2,3,直到5,
只需将USART1.c,和USART1Config.c中的‘USART1’直接全部替换成‘USART2’,等等就可以了。
再就是如果你们用2.0的库的话,你们中断向量地址UsageFault_Handler默认是在stm32f10x_it.c 文件里的,你要将它们注释掉,否则会出现重复定义的问题,3.0就没有这个问题了。
另外接收的部分我没有说,其实接收的可以在调用USART1RecvData(count,flag)函数来配置,count用于定义一帧接收字节数,flag用于是否开启接收超时中断,里面的USART1_RECV_MAX_Q用于定义接收缓冲区的最大字节,我以前是爱用收到多少字节后进入接收中断的,发觉其实没有必要,我们在接收时开启定时中断,每个接收字节的ISR里面更新TIM的时间,最后一个字节结束了以后,TIM没有更新了,就会触发超时中断,在超时中断里面处理接收到的命令就可以了。所以这个count事实上没有作用。flag用于是否开启超时中断,若朋友们是手动调试,请关闭flag,若正常工作了,打开flag就是了。      

使用特权

评论回复
32
狗啃模拟|  楼主 | 2021-8-5 22:50 | 只看该作者
注意:
USART1接收超时中断使用了TIM2
USART1接收超时中断使用了TIM3
USART1接收超时中断使用了TIM4
USART1接收超时中断使用了TIM6
USART1接收超时中断使用了TIM7
STM的定时器很多,随便用,在AVR里面,我用的是Timer2

使用特权

评论回复
33
狗啃模拟|  楼主 | 2021-8-5 22:51 | 只看该作者
340里面也有很多,AVR,340的工程以后再挂上。

多谢兄弟们捧场,刚刚把中文输入法加上去了,有需要加入中文输入法的朋友们可以用。

拼音输入法 (原文件名:拼音输入法.JPG)
串口发送模板(加入拼音检索)**_611446EN129F.rar(文件大小:816K) (原文件名:串口发送模板(第三版).rar)

使用特权

评论回复
34
shengyanbo| | 2021-8-13 10:06 | 只看该作者
顶一下

使用特权

评论回复
35
磨砂| | 2021-9-7 10:21 | 只看该作者
环形队列是那种没有队头队尾的吗

使用特权

评论回复
36
八层楼| | 2021-9-7 10:51 | 只看该作者
链接好像是失效了

使用特权

评论回复
37
sheflynn| | 2021-9-8 13:56 | 只看该作者
环形队列怎么样  

使用特权

评论回复
38
ccook11| | 2021-9-8 13:56 | 只看该作者
用的是ringbuffer吗?   

使用特权

评论回复
39
qiufengsd| | 2021-9-8 13:56 | 只看该作者
DMA的效果怎么样   

使用特权

评论回复
40
kmzuaz| | 2021-9-8 13:56 | 只看该作者
可以使用stm32cubemx配置的   

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则