这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 企业专区 » TI » 【TIMSPM0MCU焕新大作战】+课程3任务1:实现温度报警器

共5条 1/1 1 跳转至

【TIMSPM0MCU焕新大作战】+课程3任务1:实现温度报警器

专家
2024-04-23 22:58:33     打赏

【TI MSPM0 MCU 焕新大作战】+课程3任务1:实现温度报警器

这次的任务是实现温度报警,关键点在于实际检测环境温度。活动提供了经典的Dallas18B20传感器。但凡搞单片机的,恐怕没几个人不知道Dallas18B20。一个原因是Dallas18B20是很经典的一个温度传感器、例子多,另外一个原因可能还和18B20的特殊通讯协议有关。在我的单片机学习过程中,大概也是唯一一次采用单总线通讯方式收发数据的经历。

18B20的资料,不多讲,网上可以找到的例子一大把。本次实验提供的是模组方式,已经配置好了外围电路,只需要用杜邦线简单连接到开发板上就行。本次实验为了方便显示调试信息和测试结果,除了之前用到的数码管模块,还加入了OLED显示。

OLED显示屏耗电量低,需要显示什么数据一次传递便好,不需要不停扫描。

开发板的电路原理图:

图片1.png整个电路的简易结构图如下:

DS18B20的一些资料:

图片4.png对于报警处理,我用的方式是:在采集到温度后,显示到OLED和数码管模块上,然后和警戒线比较、判断,超出报警线,驱动蜂鸣器报警。和18B20的通讯过程,要注意通讯时序的问题。

所有和18B20有关的处理代码,都放在主文件中了。18B20数据通讯接口,初始化时配置为输出,但在运行过程中根据通讯需要动态设置为输入或者输出口模式。

让DS18B20进行一次温度的转换,那具体的操作就是:

1、主机先作个复位操作,

2、主机再写跳过ROM的操作(0xCC)命令,

3、然后主机接着写个转换温度的操作命令(0x44),后面释放总线至少一秒,让DS18B20完成转换的操作。在这里要注意的是每个命令字节在写的时候都是低字节先写,例如CCH的二进制为11001100,在写到总线上时要从低位开始写,写的顺序是“0、0、1、1、0、0、1、1”。整个转换过程大概需要125ms的样子。

转换完成后,立即读取转换结果,这个处理同样需要三个步骤:

1、主机发出复位操作并接收DS18B20的应答(存在)脉冲。

2、主机发出跳过对ROM操作的命令(0xCC)。

4、主机发出读取RAM的命令(0xBE),随后主机依次读取DS18B20发出的数据。如果只想读取温度数据,那在读完第0和第1个数据后就不再理会后面DS18B20发出的数据即可。同样读取数据也是低位在前的。这两个数据,低8位在前,高八位在后,处理的时候要注意。从功能上讲,18B20读出来的数据,是包括负温度数据的。但测试室温场合,不可能有负数,所以在数据处理上可以更简单一些。

程序的处理逻辑如下:

1、定时器处理完成时间计数,

2、主程序的循环中检测定时周期信号,收到信号后,启动温度检测。将检测到的数据进行转换,显示到OLED上,同时转换为数码管模块可以显示的形式。

3、主程序的循环中不断显示温度数据到数码管上。

主处理代码:


int main(void) {
    uint32_t pos=0, i=0, cnt=0, flag=0;
    char temp_char[20]={'\0'};
    char alert_flag=0;
    /* Power on GPIO, initialize pins as digital outputs */
    SYSCFG_DL_init();
    // 设置定时器0中断优先级
    NVIC_SetPriority(TIMER_0_INST_INT_IRQN, 1);
    // 允许定时器0中断
    NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN);
    // 启动定时器计数
    DL_TimerG_startCounter(TIMER_0_INST);
    //DL_GPIO_setPins(GPIOA_PORT, GPIOA_USER_LED_1_PIN | GPIOA_USER_LED_2_PIN | GPIOA_USER_LED_3_PIN | GPIOA_USER_TEST_PIN);
    closeBeep();
    OLED_Init();
    OLED_Clear(0);
    GUI_ShowCHinese(0, 16, (uint8_t *)"温度:", 16, 1);
    cnt_1s_flag=0;
    while (1) {
        // 每秒读取一次温度
        if (cnt_1s_flag==1) {
            // 设置标志,表示读取传感器的处理进行中
            cnt_1s_flag = 2;
            // 获取温度
            t=Ds18b20GetTemp();
            // 如果温度大于报警线,则报警;恢复正常后,停止报警
            if (t > TEMP_ALARM_H1) {
                openBeep();
            } else {
                closeBeep();
            }
            if (old_t != t) {
                old_t = t;
                // 将温度转换为字符串
                if (t > 0) {
                    sprintf(temp_char, "%3.1f", t);
                } else {
                    sprintf(temp_char, "%-3.1f", t);
                }
                // 在OLED上显示温度数据
                GUI_ShowString(48, 16, (uint8_t *)"      ", 16, 1);
                GUI_ShowString(48, 16, (uint8_t *)temp_char, 16, 1);
                // 利用数码管模块显示温度。显示之前,先做转换处理,
                // 1、将小数点和前面数字结合
                // 2、所有要显示的数据,转换为数码管上显示用的笔段码
                pos=0;
                cnt=0;      // 显示数据位数,为了调整显示数码管的位置用,忽略的话,是从最前面开始显示
                for (i=0; i<8; i++) {
                    if (temp_char[i]=='\0') {
                        disp_segdat[pos]='\0';
                        break;
                    }
                    if (temp_char[i]=='-') {
                        // 负号对应的笔段码
                        disp_segdat[pos]=0xBF;
                        // 调整保存位置为下一个数码管的
                        pos++;
                        cnt++;
                    } else if (temp_char[i]=='.') {
                        // 前一个字符的段位码数据加上小数点位
                        disp_segdat[pos-1]=disp_segdat[pos-1] & 0x7F;
                    } else if (temp_char[i]>='0' && temp_char[i]<='9') {
                        // 数字0-9,取得字段码数据
                        disp_segdat[pos]=TAB_SEG[temp_char[i]-48];
                        // 调整保存位置为下一个数码管的
                        pos++;
                        cnt++;
                    }
                }
            }
            // 清除1秒标志
            cnt_1s_flag = 0;
//
        }
        // 发送字段码数据给数码管显示模块
        // 方式1:循环一次只显示一个数码管,显示有闪烁
        smpos=(smpos+1)%8;
        if (disp_segdat[smpos]=='\0') {
            smpos=-1;
        } else {
            // 显示的位置,可以根据cnt的值(显示数据的总位数)调整到任意位置
            DispSegData(smpos, disp_segdat[smpos]);
            delay_cycles(ms1*SMSEG_DISP_TIMES);
        }
        DL_GPIO_togglePins(GPIOA_PORT, GPIOA_USER_LED_3_PIN);
    }
}

定时器的中断处理代码:


// Timer0的中断处理,按照配置,每10ms完成一次中断,累计100次,得到1S的周期
void TIMER_0_INST_IRQHandler(void) {
    switch (DL_TimerG_getPendingInterrupt(TIMER_0_INST)) {
        case DL_TIMERG_IIDX_ZERO:
            // 定时器0完成一次中断
            if (cnt_1s_flag == 2) {
                // 读取温度数据时,停止计算秒的计数
                cnt_10ms=0;
            } else if (cnt_1s_flag == 0) {
                // 累计50次,500mS
                cnt_10ms=(cnt_10ms+1)%50;
                if (cnt_10ms==0) {
                    // 达到1秒钟
                    if (cnt_1s_flag == 0) {
                        cnt_1s_flag=1;
                    }
                    //DL_GPIO_togglePins(GPIOA_PORT, GPIOA_USER_LED_3_PIN);
                }
            }
            break;
        case DL_TIMERG_IIDX_LOAD:
        case DL_TIMERG_IIDX_CC0_DN:
        case DL_TIMERG_IIDX_CC1_DN:
        case DL_TIMERG_IIDX_CC0_UP:
        case DL_TIMERG_IIDX_CC1_UP:
        case DL_TIMERG_IIDX_OVERFLOW:
        default:
            break;
    }
}

程序编好后,下载到开发板,接好各个部件,测试效果如下:

课程3_2_1_1.png数码管的显示处理不好,闪烁明显,但不影响正确显示温度。

DS18B20处理部分的代码:


void Ds18b20Rst(void) {
    // 设置TEMP_IO口为输出模式
    DL_GPIO_initDigitalOutputFeatures(GPIOA_DS18B20_DIO_IOMUX,
         DL_GPIO_INVERSION_DISABLE, DL_GPIO_RESISTOR_PULL_UP,
         DL_GPIO_DRIVE_STRENGTH_LOW, DL_GPIO_HIZ_DISABLE);
    DL_GPIO_clearPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
    DL_GPIO_enableOutput(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
    // 主机发送复位脉冲480us-960us
    delay_us(750);
    DL_GPIO_setPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
    delay_us(15);
}
/*等待18b20响应
 * 返回1:未检测到18b20
 * 返回0:存在
 */
char Ds18b20Check(void) {
   char retry=0;
//   DL_GPIO_disableOutput(GPIOA, GPIOA_DS18B20_DIO_PIN);
//   DL_GPIO_initDigitalInputFeatures(GPIOA_DS18B20_DIO_IOMUX,
//        DL_GPIO_INVERSION_DISABLE, DL_GPIO_RESISTOR_PULL_UP,
//        DL_GPIO_HYSTERESIS_DISABLE, DL_GPIO_WAKEUP_DISABLE);
//
//   while(DL_GPIO_readPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN)>0 && retry<200) {
//       retry++;
//       delay_us(1);
//   }
//   if(retry>=200) {
//       return 1;
//   }
//   retry=0;
//
//   while(DL_GPIO_readPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN)==0 && retry<240) {
//       retry++;
//       delay_us(1);
//    }
//    if(retry>=240) return 1;
   delay_us(125);
    return 0;
  }
/*从18b20读取一个位
 * 返回值1/0
 */
char Ds18b20ReadBit(void) {
     char data;
     // 设置TEMP_IO口为输出模式
     DL_GPIO_initDigitalOutputFeatures(GPIOA_DS18B20_DIO_IOMUX,
          DL_GPIO_INVERSION_DISABLE, DL_GPIO_RESISTOR_PULL_UP,
          DL_GPIO_DRIVE_STRENGTH_LOW, DL_GPIO_HIZ_DISABLE);
     DL_GPIO_enableOutput(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
     DL_GPIO_clearPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
     delay_us(2);
     DL_GPIO_setPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
     // 设置TEMP_IO口为输入模式
     DL_GPIO_disableOutput(GPIOA, GPIOA_DS18B20_DIO_PIN);
     DL_GPIO_initDigitalInputFeatures(GPIOA_DS18B20_DIO_IOMUX,
          DL_GPIO_INVERSION_DISABLE, DL_GPIO_RESISTOR_PULL_UP,
          DL_GPIO_HYSTERESIS_DISABLE, DL_GPIO_WAKEUP_DISABLE);
     delay_us(12);
     if(DL_GPIO_readPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN)>0)
         data=1;
     else
         data=0;
     delay_us(50);
     return data;
  }
/*从18b20读取一个字节
 * 返回值:读到的数据
 */
char Ds18b20ReadByte(void) {
    char i,j,dat;
    dat=0;
    for(i=1;i<=8;i++) {
        j=Ds18b20ReadBit();
        dat=(j<<7)|(dat>>1);//低位在前
    }
    return dat;
  }
/*写一个字节到Ds18b20
 * dat:要写入的字节
 */
void Ds18b20WriteByte(char dat) {
    char i;
    char temp;

    DL_GPIO_initDigitalOutputFeatures(GPIOA_DS18B20_DIO_IOMUX,
         DL_GPIO_INVERSION_DISABLE, DL_GPIO_RESISTOR_PULL_UP,
         DL_GPIO_DRIVE_STRENGTH_LOW, DL_GPIO_HIZ_DISABLE);
    DL_GPIO_enableOutput(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
    for(i=1;i<=8;i++) {
        temp=dat&0x01;
        dat=dat>>1;
        //write 1
        if (temp>0) {
            DL_GPIO_clearPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
            delay_us(2);
            DL_GPIO_setPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
            delay_us(60);
        } else {
            DL_GPIO_clearPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
            delay_us(60);
            DL_GPIO_setPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
            delay_us(2);
        }
    }
  }
/*开始温度转换
 *
 */
void Ds18b20Start(void) {
   Ds18b20Rst();
   Ds18b20Check();
//   if (Ds18b20Check()>0) {
//       GUI_ShowString(0, 0, (uint8_t *)"Start NG", 8, 1);
//       return;
//   }
   Ds18b20WriteByte(0XCC);
   Ds18b20WriteByte(0X44);
 }
/*从Ds18b20得到温度值
 * 精度0.1c
 * 返回值:温度值(-550-1250)
 */
float Ds18b20GetTemp() {
   char temp;
   char TH=0,TL=0;
   short tem;
   float t;
   Ds18b20Start();
   Ds18b20Rst();
//   if (Ds18b20Check()==0) {
//       GUI_ShowString(0, 0, (uint8_t *)"GetTemp NG", 8, 1);
//       return 999;
//   }
   Ds18b20Check();
   Ds18b20WriteByte(0XCC);
   Ds18b20WriteByte(0XBE);
   TL=Ds18b20ReadByte();//LSB
   TH=Ds18b20ReadByte();//MSB
   GUI_ShowNum(20, 8, TH, 3, 8, 1);
   GUI_ShowNum(50, 8, TL, 3, 8, 1);
   if(TH>7) {
       TH=~TH;
       TL=~TL;
       temp=0;//the temperature is negative
    } else
        temp=1;// the temperature is positive
    tem=TH;//高八位
    tem<<=8;
    tem+=TL;//低八位
    t=((float)tem*0.0625);
    if(temp)
        return t;
    else
        return -t;
  }





关键词: MSPM0焕新大作战     温度报警    

助工
2024-04-24 07:00:40     打赏
2楼

感谢分享呀,这么快就完成了任务,优秀!


专家
2024-04-24 08:52:56     打赏
3楼

最近太忙了,所以赶着把或弄完一个。不弄完,心理有压力,


助工
2024-04-25 07:55:26     打赏
4楼

大佬,发一下ds18b20的程序出来学习一下,我昨天时序总是调不对呀。


专家
2024-04-25 10:59:34     打赏
5楼

好,马上哈



共5条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]