项目背景:
做一个测量阻抗脉搏波的设备,设备功能其中之一是采集阻抗信号,通过一个高精度ADC转换为AD值,并将AD值输出。软件模块示例如下:
if (CS1259Ready()) //等待ADC转换完成
{
Z13Adc = ReadADC(); //转换完成后读取ADC值;
printf("%d\t\t",Z13Adc);
Z13Res = CalRes(Z13Adc); //通过ADC值计算阻抗值
printf("%d\r\n",Z13Res);
vTaskDelay(5/portTICK_RATE_MS);
}
遇到的问题:
在连续读取ADC值时,会随机的中断测量,导致测量停止,或者可能会随机的发送错误数据,经排查非串口传输的原因。且传输错误的数据通常为随机性的产生FF,例如24bit的ADC,会出现后几个bit为F的情况。现象如下:
问题分析:
用逻辑分析仪抓取 ,发现是ADC的读取程序在读取过程中,受到了中断的干扰,导致其去做其他事情了。
例如:正常读取进程时,通信帧的情况如下:
而出现上述情况时,通信帧的情况如下:
因此软件需保证读取任务进程不能被其他中断干扰。这里采用了FreeRTOS临界区的方式实现。代码示例如下图:
if (CS1259Ready()) //等待AD开始信号
{
taskENTER_CRITICAL(); //进入临界区
Z13Adc = ReadADC();
printf("%d\t\t",Z13Adc);
Z13Res = CalRes(Z13Adc);
printf("%d\r\n",Z13Res);
taskEXIT_CRITICAL(); //退出临界区
vTaskDelay(5/portTICK_RATE_MS);
}
FreeRTOS中临界区总结:
1)互斥的概念?
访问一个被多任务共享,或是被多任务和中断共享的资源时,需要采用“互斥”技术以保证数据在任何时候都保持一致性。这样做的目的是要确保任务从开始访问资源就具有排它性,直到这个资源又恢复到完整状态。
2)什么是基本临界区?
基本临界区是指宏taskENTER_CRITICAL()和taskEXIT_CRITICAL()之间的代码区间。也被称为Critical Section或者Critical Regions. 使用方法如下:
// 为了保证PORTA的访问不被中断,将访问操作放入临界区运行
taskENTER_CRITICAL(); //进入基本临界区
// 在taskENTER_CRITICAL()和taskEXIT_CRITICAL()之间不会切换到其他任务,中断可以执行,也允许嵌
//套,但只是针对优先级高于configMAX_SYSCALL_INTERRUPT_PRIORITY的任务,而且这些中断不允许访问
//FreeRTOS的API函数。
PORTA |= 0x01;
taskEXIT_CRITICAL(); //退出基本临界区
3)基本临界区的原理?
临界区是提供互斥功能的一种非常原始的实现方法。临界区的工作 仅仅是简单的把中断全部关掉,或者是关掉优先级在configMAX_SYSCALL_INTERRUPT_PRIORITY以下的中断-依赖具体使用的FreeRTOS移植。抢占式的上下文切换只可能在某中断中完成,因此调用taskENTER_CRITICAL()可以在中断关闭的时段一直保持持续运行状态直到退出临界区。
4)基本临界区使用注意事项?
临界区必须具有很短的时间,否则会反过来影响中断的响应时间。在每次使用taskENTER_CRITICAL()之后必须尽快的配套一个taskEXIT_CRITICAL()来退出临界区。
如何用挂起调度器来创建临界区?
挂起调度器也被称为锁定调度器。
vTaskSuspendAll(void); //挂起调度器创建临界区(禁止任务调度 suspend the scheduler)
/*
通过vTaskSuspendAll()来挂起调度器。挂起调度器可以停止上下文切换而不用关中断,如果某个中断在调
度器挂起过程中要求进行上下文切换,则这个要求也会被挂起,直到调度器被唤醒后才会得到执行。
*/
vTaskResumeAll(void); //(解除禁止任务调度 resuming the scheduler; 成对使用)
/*
返回值:pdTRUE:
在调度器挂起过程中,上下文切换请求也会被挂起,直到调度器唤醒才被执行,如果一个挂起的上下文切换请求在vTaskResumeAll()返回前得到执行,则返回pdTRUE.
返回值:pdFALSE:
如果是其他情况,则返回pdFALSE.
*/
// 注意1:两个函数成对使用;
// 注意2:两者之间不能调用FreeRTOS系统API.
基本临界区是保护一段代码区间不被其他任务或中断打断。而由挂起调度器实现的临界区只能保护一段代码不被其他任务打断,并不能约束中断,因为在这种方式下,中断是使能的。
调度器处于挂起状态时,不能调用FreeRTOS的API函数。