【小黑嵌入式系统第十六课】PSoC 5LP第三个实验——μC OS-III 综合实验

当系统响应时间很重要时,要使用抢占式内核。因此绝大多数商业上销售的实时内核都是抢占式内核。最高优先级的任务一旦就绪,总能得到CPU的控制权。当一个运行着的任务使一个比它优先级高的任务进入了就绪状态,当前任务的CPU使用权就被剥夺了,或者说被挂起了,那个高优先级的任务立刻得到了CPU的控制权。
如果是中断服务程序使一个高优先级的任务进入就绪态,中断完成时,中断了的任务将被挂起,优先级高的那个任务开始运行。
11. 中断
中断是一种硬件机制,用于通知CPU有个异步事件发生了。中断一旦被识别,CPU保存部分(或全部)上下文即部分或全部寄存器的值,跳转到专门的子程序,称为中断服务程序(ISR)。中断服务程序做事件处理,处理完成后,程序回到:
在这里插入图片描述1.在前后台系统中,程序回到后台程序;
2.对非抢占式内核而言,程序回到被中断了的任务;
3.对抢占式内核而言,让进入就绪态的优先级最高的任务开始运行。
12. 时钟节拍
时钟节拍是特定的周期性中断。这个中断可以看作是系统心脏的脉动。中断之间的时间间隔取决于不同应用,一般在10ms到200ms之间。时钟的节拍式中断使得内核可以将任务延时若干个整数时钟节拍,以及当任务在等待事件发生时,提供等待超时的依据。
时钟节拍率越快,系统的额外开销就越大。

5 硬件设计

在这里插入图片描述

Vin_Pot 输入电压同时送入上下两路 SAR ADC。SAR ADC 0 设为 12 位分辨率,2.048V 的固定 量程,将它作为 SAR ADC 1 的测量结果准确性对照。SAR ADC 1 设为 8 位分辨率,输入范围设为 0~2.048V。
为了尽量实现准确的测量,应让 Vin_Pot 的变化范围与 SAR ADC 1 的输入范围尽量匹配,为此 使用了两个可编程增益放大器 PGA_1 和 PGA_2 对小输入电压进行放大。针对 Vin_Pot 的不同变化 范围(3 个量程),两个 PGA 的增益设置如上图所示,2000mV 量程时总增益为 1,200mV 量程时总增益为 8,20mV 量程时总增益为 96。
200mV 量程时这里将总增益安排为 8 而不是 10(因为 PGA 并没有 1x10 或者 2x5 的增益配置可选),此时Vin_Pot范围在0~256mV时与SAR ADC 1的输入范围刚好匹配,但程序只处理0~200mV的范围,即使用了 SAR ADC 1 的 80% F.S.。
20mV 量程时将总增益安排为 96 而不是 100,此时 Vin_Pot 范围在 0~21.3mV 时与 SAR ADC 1的输入范围刚好匹配,但程序只处理 0~20mV 的范围,即使用了 SAR ADC 1 的 94% F.S.。这样可以让 20mV 和 200mV 量程之间有一个重叠(20~21.3mV),当使用自动量程测量时,避免被测电压出现在相邻量程的交界处时显示结果跳动频繁。如上所述 200mV 和 2000mV 量程之间也有重叠(200~256mV)。
按键状态通过 D 触发器,每隔 20ms(1/50Hz)采样一次,q 为硬件消抖后的输出,提供给中断。中断的触发类型设为上升沿触发。按键的每次按下时刻,触发一次中断。

5.1 ADC

在这里插入图片描述

5.2 时钟

在这里插入图片描述

5.3 PGA

在这里插入图片描述

6 软件设计

6.1 总体设计

app.c 中定义了:#define DISPHOLD_STATUS() (LED4_Read()==1) 通过 DISPHOLD_STATUS()可知当前的显示保持状态,当值为 0(DEF_FALSE)时, 任务正常 进行测量和结果显示;当值为 1(DEF_TRUE)时,不进行测量和结果显示。
App_TaskADC12()是第一个创建的用户任务,负责通过 SAR ADC 0(12 位分辨率)测量输 入电压及结果显示,以及 SW2 显示保持按键事件的处理。当前的显示保持状态由 LED4 指示。在非显示保持状态时,进行测量电压、过量程判断、LCD 显示工作。
App_TaskMyADC8()是由 App_TaskADC12()创建的第二个用户任务,负责通过 SAR ADC 1 测量输入电压及结果显示,以及 SW3 量程选择按键事件的处理。在非显示保持状态时,若当前量程选择为非自动,则按指定量程测量电压, 再显示测量值;若当前量程选择为自动,则先以最大量程粗测一次,从而确定被测电压落在 哪个量程最佳,然后以最佳量程再测一次,再显示后者测量值。
SW2、SW3 按键按下时刻分别会触发 isr_sw2isr_sw3 中断,在中断服务函数中,分别给任务 App_TaskADC12()App_TaskMyADC8() 发信号量。 下列代码以 isr_sw2 中断和任务App_TaskADC12()为例,任务中等待信号量, 不管当前是否收到信号量立即返回,根据返回的错误代码 os_err 值判断当前是否收到信号量,若收到则更改显示保持状态指示。

6.2 详细设计
App.c
/\*INCLUDE FILES\*/ 
#include <includes.h> 
#include <device.h> 
/\* LOCAL DEFINES\*/ 
#define MAX\_SAMPLE 8 
/\* 偏移误差值 \*/ 
#define DC\_OFFSET\_MV\_2000MV 2 
#define DC\_OFFSET\_100uV\_200MV 10 
#define DC\_OFFSET\_10uV\_20MV 0 

/\* 增益误差值 \*/ 
#define DC\_GAIN\_CAL\_FACTOR\_2000MV 1.00 
#define DC\_GAIN\_CAL\_FACTOR\_200MV 0.99 
#define DC\_GAIN\_CAL\_FACTOR\_20MV 0.95 

/\* 测量范围 \*/ 
#define RANGE\_2000MV 0 
#define RANGE\_200MV 1 
#define RANGE\_20MV 2 
#define RANGE\_AUTO 36 
#define RANGE\_NUM\_MAX 4 

/\* 增益\*/ 
#define Vin\_GAIN\_2000MV 1 
#define Vin\_GAIN\_200MV 8 
#define Vin\_GAIN\_20MV 96 
#define DISPHOLD\_STATUS() (LED4\_Read()==1) 

/\*GLOBAL VARIABLES\*/ 
OS_SEM sem_sw2, sem_sw3; 
/\*LOCAL VARIABLES\*/ 
static OS_TCB App_TaskADC12TCB; 
static CPU_STK App_TaskADC12Stk[APP_CFG_TASK_ADC12_STK_SIZE]; 
static OS_TCB App_TaskMyADC8TCB; 
static CPU_STK App_TaskMyADC8Stk[APP_CFG_TASK_MyADC8_STK_SIZE]; 
static OS_MUTEX mutex_LCD; 

/\*FUNCTION PROTOTYPES\*/ 
static void App\_TaskADC12 (void \*p_arg); 
static void App\_TaskMyADC8 (void \*p_arg); 
static void App\_TaskCreate (void); 
static void App\_ObjCreate (void); 
static CPU_INT16U SAR\_ADC12\_GetResult (void); 
static CPU_INT16U My\_SAR\_ADC\_GetResult (CPU_INT08U range); 
/\*Main函数\*/ 
int main (void) 
{ 
OS_ERR os_err;7 
BSP\_PreInit(); 
CPU\_Init(); 
OSInit(&os_err); 
OSTaskCreate((OS_TCB \*)&App_TaskADC12TCB, 
(CPU_CHAR \*)"ADC12", 
(OS_TASK_PTR )App_TaskADC12, 
(void \*)0, 
(OS_PRIO)APP_CFG_TASK_ADC12_PRIO, 
(CPU_STK \*)&App_TaskADC12Stk[0], 
(CPU_STK_SIZE )APP_CFG_TASK_ADC12_STK_SIZE_LIMIT, 
(CPU_STK_SIZE )APP_CFG_TASK_ADC12_STK_SIZE, 
(OS_MSG_QTY )0, 
(OS_TICK )0, 
(void \*)0, 
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), 
(OS_ERR \*)&os_err); 
OSStart(&os_err); 

static void App\_TaskADC12 (void \*p_arg) 
{ 
OS_ERR os_err; 
CPU_TS ts; 
CPU_INT16U mVolts; 
CPU_CHAR str[20]; 

(void)p_arg; 
BSP\_PostInit(); 
BSP\_CPU\_TickInit(); 
#if (OS\_CFG\_STAT\_TASK\_EN > 0) 
OSStatTaskCPUUsageInit(&err); 
#endif 
#ifdef CPU\_CFG\_INT\_DIS\_MEAS\_EN 
CPU\_IntDisMeasMaxCurReset(); 
#endif 

App\_TaskCreate(); 
App\_ObjCreate(); 


/\* Print the initial LCD and LED display \*/ 
LCD\_Position(0, 0); 
LCD\_PrintString("2000m"); 
LCD\_Position(1, 0); 
LCD\_PrintString("ADC12:");LED4\_Write(0); 
ADC\_SAR\_0\_StartConvert(); 
 
while (DEF_TRUE) 
{ 
OSSemPend(&sem_sw2, 0, OS_OPT_PEND_NON_BLOCKING, &ts, &os_err); 

if(os_err == OS_ERR_NONE) 
{ 
/\* 按下SW2,切换并指示显示保持状态 \*/ 
LED4\_Write(~LED4\_Read()); 
OSMutexPend(&mutex_LCD, 0, OS_OPT_PEND_BLOCKING, &ts, &os_err);
if(os_err == OS_ERR_NONE) 
{ 
LCD\_Position(0, 6); 
if(DISPHOLD\_STATUS() == DEF_TRUE) 
{ 
LCD\_PutChar('H'); 
} 
else 
{ 
LCD\_PutChar(' '); 
} 
OSMutexPost(&mutex_LCD, OS_OPT_POST_NONE, &os_err); 
} 
} 
if(DISPHOLD\_STATUS() == DEF_FALSE) 
{ 
mVolts = SAR\_ADC12\_GetResult(); 
/\* Print the measured voltage value \*/ 
sprintf(str,"%4d mV", mVolts); 
OSMutexPend(&mutex_LCD, 0, OS_OPT_PEND_BLOCKING, &ts, &os_err); 

if(os_err == OS_ERR_NONE) 
{ 
LCD\_Position(1,9); 
LCD\_PrintString(str); 
LCD\_Position(1, 7); 
if(mVolts >= 2048) 
{ 
LCD\_PutChar('^'); 
} 
else 
{ 
LCD\_PutChar(' '); 
} 
OSMutexPost(&mutex_LCD, OS_OPT_POST_NONE, &os_err); 
// Release LCD Mutex 
} 
} 
else 
{ 
} 
OSTimeDlyHMSM(0, 0, 0, 250, OS_OPT_TIME_HMSM_STRICT, &os_err); 
// Wait 250ms, give CPU to other tasks 
} 
} 
static void App\_TaskMyADC8 (void \*p_arg) 
{ 
OS_ERR os_err; 
CPU_TS ts; 
CPU_INT08U volt_range = RANGE_2000MV; 
CPU_INT08U volt_range_auto = RANGE_2000MV; 
CPU_INT16U voltDigs; 
/\* 在中保留有效数字(删除小数点)毫伏、一百微伏或十微伏 \*/ 
CPU_CHAR str[20]; 
(void)p_arg; 
while (DEF_TRUE) 
{
OSSemPend(&sem_sw3, 0, OS_OPT_PEND_NON_BLOCKING, &ts, &os_err); 
if(os_err == OS_ERR_NONE) 
{ /\* 如果按下SW3,则切换到下一个电压范围 \*/ 
volt_range = (volt_range +1) % RANGE_NUM_MAX; 
} 
if(DISPHOLD\_STATUS() == DEF_FALSE) 
{ 
/\* My SAR ADC8:获取八次连续转换的平均值 \*/ 
if(volt_range != RANGE_AUTO) 
{ 
/\* Perform only one measurement with a fixed range \*/ 
voltDigs = My\_SAR\_ADC\_GetResult(volt_range); 
} 
else 
{ 
/\* Perform a rough measurement \*/ 
volt_range_auto = RANGE_2000MV; 
voltDigs = My\_SAR\_ADC\_GetResult(volt_range_auto); 
/\* Determine the appropriate range and measure once again\*/ 
if(voltDigs >= 200) 
// >=200 mV 
{ 
} 
else if(voltDigs >= 20) // 199~20 mV 
{ 
volt_range_auto = RANGE_200MV; 
voltDigs = My\_SAR\_ADC\_GetResult(volt_range_auto); 
} 
else 
// 19~0 mV 
{ 
volt_range_auto = RANGE_20MV; 
voltDigs = My\_SAR\_ADC\_GetResult(volt_range_auto); 
} 
} 
OSMutexPend(&mutex_LCD, 0, OS_OPT_PEND_BLOCKING, &ts, 
&os_err); // Wait for LCD Mutex 
if(os_err == OS_ERR_NONE) 
{ 
/\* 确定过电压状态并标记 \*/ 
LCD\_Position(0, 7); 
str[0] = ' '; 
if(voltDigs > 2000 && ((volt_range == RANGE_2000MV)|| \(volt_range == RANGE_AUTO && volt_range_auto 		== RANGE_2000MV))) 
{ 
voltDigs = 2000; 
// 避免显示超过测量范围的电压值 
str[0] = '^'; 
// 记录过电压标记 
} 
if(voltDigs\*Vin_GAIN_200MV\*10 > 2000 && volt_range == RANGE_200MV) 
{ 
voltDigs = 200/Vin_GAIN_200MV ; 
str[0] = '^'; 
} 
if((voltDigs+1)\*Vin_GAIN_20MV\*100 > 2000 && volt_range == 
RANGE_20MV) 
{ 
voltDigs = 20; 
str[0] = '^'; 
} 
LCD\_PutChar(str[0]); 
/\* Print the measured voltage value \*/ 
LCD\_Position(0, 0); 
switch(volt_range) 
{ 
case RANGE_2000MV: 
LCD\_PrintString("2000m"); 
sprintf(str, " %4d mV", voltDigs); 
break; 
case RANGE_200MV: 
LCD\_PrintString("200m "); 
sprintf(str, "%3d.%01d mV", 
voltDigs\*Vin_GAIN_200MV, voltDigs%Vin_GAIN_200MV); 
break; 
case RANGE_20MV: 
LCD\_PrintString("20m "); 
if(voltDigs>=20) 
sprintf(str, "20.%02d mV", 
voltDigs%Vin_GAIN_20MV); 
else 
sprintf(str, "%2d.%02d mV", voltDigs\*98, 
voltDigs%Vin_GAIN_20MV); 
break; 
case RANGE_AUTO: 
LCD\_PrintString("AUTO"); 
switch(volt_range_auto)13 
{ 
case RANGE_2000MV: 
LCD\_PutChar('H'); 
sprintf(str, " %4d mV", voltDigs); 
break; 
case RANGE_200MV: 
LCD\_PutChar('M'); 
sprintf(str, "%3d.%01d mV", 
voltDigs\*Vin_GAIN_200MV, 	voltDigs%Vin_GAIN_200MV); 
break; 
case RANGE_20MV: 
LCD\_PutChar('L'); 
sprintf(str, "f.%02d mV", 
voltDigs\*Vin_GAIN_20MV, 	voltDigs%Vin_GAIN_20MV); 
break; 
default: 
break; 
} 
break; 
default: 
break; 
} 
LCD\_Position(0, 8); 
LCD\_PrintString(str); 
OSMutexPost(&mutex_LCD, OS_OPT_POST_NONE, &os_err); 
// Release LCD Mutex 
} 
} 
else 
{ 
} 
OSTimeDlyHMSM(0, 0, 0, 250, OS_OPT_TIME_HMSM_STRICT,&os_err); 
// Wait 250ms, give CPU to other tasks 
} 
} 
}

static void App\_TaskCreate (void) 
{ 
OS_ERR os_err; 
OSTaskCreate((OS_TCB \*)&App_TaskMyADC8TCB, 
(CPU_CHAR \*)"MyADC8",
(OS_TASK_PTR )App_TaskMyADC8, 
(void \*)0, 
(OS_PRIO )APP_CFG_TASK_MyADC8_PRIO, 
(CPU_STK \*)&App_TaskMyADC8Stk[0], 
(CPU_STK_SIZE )APP_CFG_TASK_MyADC8_STK_SIZE_LIMIT, 
(CPU_STK_SIZE )APP_CFG_TASK_MyADC8_STK_SIZE, 
(OS_MSG_QTY )0, 
(OS_TICK )0, 
(void \*)0, 
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), 
(OS_ERR 
\*)&os_err); 
} 

static void App\_ObjCreate (void) 
{ 
OS_ERR err; 

OSSemCreate(&sem_sw2,"sem\_sw2",0,&err); 
OSSemCreate(&sem_sw3,"sem\_sw3",0,&err); 
OSMutexCreate(&mutex_LCD, "mutex\_LCD", &err); 
} 

static CPU_INT16U SAR\_ADC12\_GetResult (void) 
{ 
CPU_INT16U adcCount; 
// 保持ADC计数 
CPU_INT16U mVolts; 
// 保持电压值 
CPU_INT08U sampleCount; // 统计从ADC采集的样本数 
CPU_INT32U voltSamples; // 保存累积样本 
/\* Perform eight consecutive conversions and get the average value 
\*/ 
voltSamples = 0; 
for(sampleCount=0; sampleCount<MAX_SAMPLE; sampleCount++) 
{ 
/\* Read ADC count and convert to milli volts \*/ 
ADC\_SAR\_0\_IsEndConversion(ADC_SAR_0_WAIT_FOR_RESULT); 
adcCount = ADC\_SAR\_0\_GetResult16(); 
mVolts = ADC\_SAR\_0\_CountsTo\_mVolts(adcCount);
/\* Add the current ADC reading to the cumulated samples \*/ 
voltSamples += mVolts; 
} 
mVolts = voltSamples/MAX_SAMPLE; 

return mVolts; 
} 

static CPU_INT16U My\_SAR\_ADC\_GetResult (CPU_INT08U range) 
{ 
CPU_INT16U adcCount; 
// 保持ADC计数 
CPU_INT16S voltDigs[2];
 /\* 中保留有效数字 \*/ 
char str[20]; 
CPU_INT08U i; 
CPU_INT08U DAC8_val; 
// VDAC8 的值 
CPU_INT08U comp_1_out; 
// 比较器输出 
CPU_INT16U voltCount; 
// 保持ADC的计数 
CPU_INT16U mVolts; 
//将ADC计数转换的结果保留为毫伏 
CPU_INT08U sampleCount; 
// 统计从ADC采集的样本数 
CPU_INT32U voltSamples; 
// 保持逐次样本 
CPU_INT08U chan; 
CPU_INT16S dc_offset; 
// 偏移误差 
CPU_FP32 
dc_gain_cal_factor; // 增益误差 
Sample\_Hold\_1\_Start(); 
Clock\_1\_Start(); 
VDAC8\_1\_Start(); 
PGA\_1\_Start(); 
PGA\_2\_Start(); 
Comp\_1\_Start(); 
switch(range) 
{ 
default: 
case RANGE_2000MV: 
PGA\_1\_SetGain(PGA_1_GAIN_01); 
PGA\_2\_SetGain(PGA_2_GAIN_01); 
dc_offset = DC_OFFSET_MV_2000MV; 
dc_gain_cal_factor = DC_GAIN_CAL_FACTOR_2000MV; 
break;
case RANGE_200MV: 
PGA\_1\_SetGain(PGA_1_GAIN_04); 
PGA\_2\_SetGain(PGA_2_GAIN_02); 
dc_offset = DC_OFFSET_100uV_200MV \*Vin_GAIN_200MV/10; 
dc_gain_cal_factor = DC_GAIN_CAL_FACTOR_200MV; 
break; 
case RANGE_20MV: 
PGA\_1\_SetGain(PGA_1_GAIN_48); 
PGA\_2\_SetGain(PGA_2_GAIN_02); 
dc_offset = DC_OFFSET_10uV_20MV \*Vin_GAIN_20MV/100; 
dc_gain_cal_factor = DC_GAIN_CAL_FACTOR_20MV; 
break; 
} 
/\* Perform eight consecutive conversions and get the average value \*/ 	
voltSamples = 0;//逐次样本数 为0 
for(sampleCount=0; sampleCount<MAX_SAMPLE; sampleCount++)
//采样次数为8 
{ 
/\* 开始新的采样\*/ 
SH\_Control\_Write(1); 
CyDelayUs(1); 
// 需要时间1us 
/\* 在保持期间完成一个转换\*/ 
voltCount = 0x00;//保持ADC的计数=0 
for(i=0; i<8; i++)//8次逼近转化值 
{ 
DAC8_val = voltCount + (0x80 >> i);
//VDAC8 的值 = 采样值+(10000000>>i) 
VDAC8\_1\_SetValue(DAC8_val);
//设置采样比较值 
CyDelayUs(1); 
## 最后

**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

**深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**

**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

![img](https://img-blog.csdnimg.cn/img_convert/8982c0815ee5e5f6f564f380c7fd107c.png)

![img](https://img-blog.csdnimg.cn/img_convert/f092057ce86f505535aaf39f0cf69a22.jpeg)

![img](https://img-blog.csdnimg.cn/img_convert/a307887fcc1e319623cc6c78a58a2324.png)

 ![img](https://img-blog.csdnimg.cn/img_convert/4d90ce0f3e953dc76c7870332eacc68c.png)

![img](https://img-blog.csdnimg.cn/img_convert/f9a820eed8f0e50e31950a9660a6be93.png)

![img](https://img-blog.csdnimg.cn/img_convert/1451ee1f33d4f9aeedd34c52fa9e76eb.png)

![](https://img-blog.csdnimg.cn/img_convert/9ba031ee82f6c9771bead1b263102cb5.png)

 

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!


.(img-S4GzYfcY-1715626722385)]

[外链图片转存中...(img-S0h4jp8b-1715626722386)]

 [外链图片转存中...(img-B38HzgGB-1715626722386)]

[外链图片转存中...(img-vZwwkRKB-1715626722387)]

[外链图片转存中...(img-MCb3VgL6-1715626722388)]

[外链图片转存中...(img-IAD3zU7h-1715626722388)]

 

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值