一、基础知识
MLX90614ESF是非接触式红外测温传感器整个系列名称,其中此类型传感器又有多个型号比如AXX、BXX、DXX。不同型号的传感器,所工作的额定电压不同,所测的温度范围不同。而本次实验所使用的为BXX型号。下图所示为参考资料给出的应用电路图。
其中R1和R2是大小为10K的上拉电阻 ,因此传感器的接口为SMBus协议,而SMBus协议与IIC协议类似,所以用了两个电阻对SDA和SCL上拉到3.3V高电平。
二、实验部分
本次实验任务的内容如下
① 创建定时器任务,通过定时器回调函数发送信号量至任务A。
② 任务A接收信号量之后驱动传感器采集100次温度数据,取平均值算出数据。
③ 温度是否高于阈值,若高于设定阈值,就向任务B发送内建消息,将当前温度数据传递给任务B,并打印至XCOM。
本次实验使用了UCOSIII的 软件定时器、任务的内建信号量、任务的内建消息队列
三、实验过程
① 在开始任务内创建三个任务
//开始任务函数
void start_task(void *p_arg)
{
OS_ERR err;
CPU_SR_ALLOC();
p_arg = p_arg;
CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); //统计任务
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN //如果使能了测量中断关闭时间
CPU_IntDisMeasMaxCurReset();
#endif
#if OS_CFG_SCHED_ROUND_ROBIN_EN //当使用时间片轮转的时候
//使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);
#endif
//创建定时器1
OSTmrCreate((OS_TMR *)&tmr1, //定时器1
(CPU_CHAR *)"tmr1", //定时器名字
(OS_TICK )0, //20*10=200ms
(OS_TICK )100, //100*10=1000ms
(OS_OPT )OS_OPT_TMR_PERIODIC, //周期模式
(OS_TMR_CALLBACK_PTR)tmr1_callback,//定时器1回调函数
(void *)0, //参数为0
(OS_ERR *)&err); //返回的错误码
OS_CRITICAL_ENTER(); //进入临界区
//创建测温任务
OSTaskCreate((OS_TCB * )&MlxTaskTCB, //任务控制块
(CPU_CHAR * )"mlx_task", //任务名字
(OS_TASK_PTR )mlx_task, //任务函数
(void * )0, //传递给任务函数的参数
(OS_PRIO )MLX_TASK_PRIO, //任务优先级
(CPU_STK * )&MLX_TASK_STK[0], //任务堆栈基地址
(CPU_STK_SIZE)MLX_STK_SIZE/10, //任务堆栈深度限位
(CPU_STK_SIZE)MLX_STK_SIZE, //任务堆栈大小
(OS_MSG_QTY )0, //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
(OS_TICK )0, //当使能时间片轮转时的时间片长度,为0时为默认长度,
(void * )0, //用户补充的存储区
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
(OS_ERR * )&err); //存放该函数错误时的返回值
//创建消息接收任务
OSTaskCreate((OS_TCB * )&MSGTaskTCB, //任务控制块
(CPU_CHAR * )"receive_msg_task", //任务名字
(OS_TASK_PTR )receive_msg_task, //任务函数
(void * )0, //传递给任务函数的参数
(OS_PRIO )MSG_TASK_PRIO, //任务优先级
(CPU_STK * )&MSG_TASK_STK[0], //任务堆栈基地址
(CPU_STK_SIZE)MSG_STK_SIZE/10, //任务堆栈深度限位
(CPU_STK_SIZE)MSG_STK_SIZE, //任务堆栈大小
(OS_MSG_QTY )TASK_Q_NUM, //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
(OS_TICK )0, //当使能时间片轮转时的时间片长度,为0时为默认长度,
(void * )0, //用户补充的存储区
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
(OS_ERR * )&err); //存放该函数错误时的返回值
OS_TaskSuspend((OS_TCB*)&StartTaskTCB,&err); //挂起开始任务
OS_CRITICAL_EXIT(); //进入临界区
}
② 测温任务
void mlx_task(void *p_arg)
{
u8 *pbuf;
double mxl_val_mean = 0;//100次的平均值
float mxl_val[99]={0};//保存100次采集的温度值
u8 cnt = 0;
OS_ERR err;
OSTmrStart(&tmr1,&err); //开启定时器1
while(1)
{
//请求信号量
OSTaskSemPend(0,OS_OPT_PEND_BLOCKING,0,&err);
printf("信号量请求成功\r\n");
mxl_val_mean = 0;
for(cnt = 0;cnt<100;cnt++)//采集100次
{
mxl_val[cnt] = SMBus_ReadTemp();
mxl_val_mean += mxl_val[cnt];//100次累加和
}
mxl_val_mean = mxl_val_mean/100;//取100次的平均值
printf("mxl_val_mean = %.2f.\r\n",mxl_val_mean);
if(mxl_val_mean>30)
{
printf("温度过高\r\n");
pbuf = mymalloc(SRAMIN,20); //申请10个字节
if(pbuf)
{
printf("内存申请成功\r\n");
sprintf((char*)pbuf,"high %d",(int)mxl_val_mean);
//发送消息
printf("pbuf = %p.\r\n",pbuf);
OSTaskQPost((OS_TCB* )&MSGTaskTCB, //向任务Msgdis发送消息
(void* )pbuf,
(OS_MSG_SIZE)30,
(OS_OPT )OS_OPT_POST_FIFO,
(OS_ERR* )&err);
printf("消息发送成功\r\n");
if(err != OS_ERR_NONE)
{
myfree(SRAMIN,pbuf); //释放内存
OSTmrStop(&tmr1,OS_OPT_TMR_NONE,0,&err); //停止定时器1
tmr1sta = !tmr1sta;
}
}
}
delay_ms(1000);//延时1秒输出
}
}
③ 接收消息任务
void receive_msg_task(void *p_arg)
{
u8 *p;
OS_MSG_SIZE size;
OS_ERR err;
while(1)
{
//请求消息
printf("正在请求消息\r\n");
p=OSTaskQPend((OS_TICK )0,
(OS_OPT )OS_OPT_PEND_BLOCKING,
(OS_MSG_SIZE* )&size,
(CPU_TS* )0,
(OS_ERR* )&err );
printf("消息请求成功\r\n");
//==================================//
printf("receive_msg_task接收到的p = %p\r\n",p);
//==================================//
myfree(SRAMIN,p); //释放内存
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时1s
}
}
④ 定时器1回调函数
//定时器1的回调函数
void tmr1_callback(void *p_tmr,void *p_arg)
{
OS_ERR err;
printf("定时时间到\r\n");
OSTaskSemPost(&MlxTaskTCB,OS_OPT_POST_NONE,&err);//发送内建信号量
}
四、实验结果
烧录程序之后,定时器时间到,就发送信号量,任务接收到信号量,就采集温度数据,当温度高于30℃,就发送内建消息,通过接收消息任务将温度数据打印至上位机。