这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 【EFM8BB52单片机】室内环境监测物联网系统(完结)

共35条 1/4 1 2 3 4 跳转至

【EFM8BB52单片机】室内环境监测物联网系统(完结)

助工
2021-12-20 10:45:55   被打赏 50 分(兑奖)     打赏

试用项目:室内温湿度度监测终端

很开心能参加这次的EFM8BB52单片机的试用活动,由于毕业在即需要写毕业论文。这次就依托以前做项目剩余物资做个室内环境监测物联网系统。系统的功能框图如图1所示:

1639968299353579.png          

图1 系统功能框图

功能简介:

1 单片机通过485读取传感器数据以获得室内环境信息

2 单片机通过串口将数据发送到网关

3 单片机上的OLED屏幕显示采集到的传感器信息

4 网关将数据通过网线或者4G模块将数据发送到服务器端

5 可以在网页上查看采集的实时数据

调试记录如下

//定时器调试记录

//串口0的调试记录

//串口和OLED屏幕

//串口1的发送调试

//OLED屏幕调试记录

//串口调试加更

//系统原理图

//完结总结




关键词: 单片机     EFM8BB52     温湿度    

助工
2021-12-20 11:06:49   被打赏 100 分(兑奖)     打赏
2楼

增加开箱贴

     快递邮寄到了学校,取的时候站点想要大大的快递箱子,就把快递箱子拆开送给了他们。能展示的就只剩下朴实无华的板子和数据线。如图1所示,图一是收到的物件合影,图2和图3是开发板的反正面,给我的感觉就是开发板很质朴,做工很沉重,简约而不缺实用精神。

1639969383349530.jpg

图1 物件合影

1639969526255876.jpg

图2 开发板正面图

1639969576123317.jpg

图3 开发板反面图


专家
2021-12-20 11:25:32     打赏
3楼

祝楼主成功


专家
2021-12-20 13:15:48     打赏
4楼

看看


专家
2021-12-20 13:21:24     打赏
5楼

学习  学习


专家
2021-12-20 13:28:04     打赏
6楼

学习 学习


工程师
2021-12-20 23:31:25     打赏
7楼

系统做的还是蛮不错的


专家
2021-12-22 22:16:47     打赏
8楼

点赞,


助工
2021-12-23 19:27:45     打赏
9楼
定时器中断(16位模式)

该MCU内部共有6个定时器,其中time0和time1不能自重重装计数寄存器的值,timer2,timer3,time4,time5则都可以自动重装计数预装值。定时器的输入时钟在默认情况下是系统时钟的12分频输入,而系统时钟在默认情况下则是内部时钟24.5MHz的8分频。因此,定时器在默认情况下输入时钟为24.5MHz/8/12。其中,对于定时器的调试除了参考datasheet,也参考了石恒瑞同志的串口和定时器内容。

Timer0和Timer1

 相关定时器:TCON  THX TLX CKCON0  TMOD  IE 其中TCON用来设置Timer0和Timer1的是能与否,THX和TLX是存储定时器重载计数预装值寄存器,CKCON0用来设置Timer0和Timer1的时钟即选择时钟源,TMOD用来设置是否是计数器模式,以及是8位,13位或者16位等。默认是16位。IE则是控制Timer0和Timer1的中断与否。由于这两个定时器不能重载计数预装值,因此,在中断函数里需要重载值。Timer0初始化函数和中断服务子函数如下,其中t_ms是在输入时钟为24.5MHz/8/12条件下中断溢出时间,单位是ms。

uint8_t  TIME0_MS=0 ;         //定时器0的初始化中断时间
uint8_t  TIME1_MS=0 ;         //定时器0的初始化中断时间
/**
 * 定时0 初始化
 * @param t_ms 每次中断的时间,时间单位为ms
 * 默认频率为30625HZ,则定时器2的输入时钟为 其12分频
 */
void timer0_init(uint8_t t_ms)
{
   uint8_t TCON_save;
   TIME0_MS=t_ms;                                                      //定时器0的初始化中断时间
   TCON_save = TCON;
   SFRPAGE = 0x00;
   TCON &= ~TCON_TR0__BMASK & ~TCON_TR1__BMASK;
   TH0 = (uint16_t)(65536-0.001*t_ms*255208)/256;
   TL0 = (uint16_t)(65536-0.001*t_ms*255208)%256;
   TCON |= (TCON_save & TCON_TR0__BMASK) | (TCON_save & TCON_TR1__BMASK);
   CKCON0=CKCON0|CKCON0_SCA__SYSCLK_DIV_12| CKCON0_T0M__PRESCALE;      //系统时钟的12分频
   TMOD=TMOD|TMOD_T0M__MODE1|TMOD_CT0__TIMER|TMOD_GATE0__DISABLED;     //计数器模式,16bit
   TCON |= TCON_TR0__RUN;
   IE = IE|IE_ET0__ENABLED;
}
 /**
  * 定时器0中断
  * 每中断一次加变量count_ms加1
  */
 SI_INTERRUPT(TIMER0_ISR, TIMER0_IRQn)
 {
   count0_ms++;
   TCON_TR0 = 0;                       // Stop Timer 0
   TH0 = (uint16_t)(65536-0.001*TIME0_MS*255208)/256;
   TL0 = (uint16_t)(65536-0.001*TIME0_MS*255208)%256;
   TCON_TR0 = 1;                       // Start Timer 0
 }
Timer2-----Timer5

Timer2--Timer5是自动重载定时器,也就是计数溢出后能够自动从TMRXRLH和TMRXRLL寄存器中将预装值加载到TMRXH和TMRXL中。相关的寄存器如下:TMRXCN0,TMRXH,TMRXL,TMRXRLH,TMRXRLL,IE,EIE1,EIE2,其中,TMRXCN0用来控制TIMRX的使能与否,TMRXH,TMRXL是存储计数预装值,TMRXRLH,TMRXRLL存储要自动重装到TMRXH,TMRXL的数据,IE是Timer2的中断控制寄存器,EIE1是Timer3的中断控制寄存器,EIE2是Timer4和Timer5的中断控制寄存器。其中TMR3CN0好像不能位操作,因此需要直接赋值

定时器2初始化和中断服务子程序如下

/**
 * 定时2 初始化
 * @param t_ms 每次中断的时间,时间单位为ms
 * 默认频率为30625HZ,则定时器2的输入时钟为 其12分频
 */
void timer2_init(uint8_t t_ms)
{
   SFRPAGE = 0x00;
   TMR2CN0 &= ~(TMR2CN0_TR2__BMASK);
   TMR2H = (uint16_t)(65536-0.001*t_ms*255208)/256;
   TMR2L = (uint16_t)(65536-0.001*t_ms*255208)%256;
   TMR2RLH = (uint16_t)(65536-0.001*t_ms*255208)/256;
   TMR2RLL = (uint16_t)(65536-0.001*t_ms*255208)%256;//溢出后自动装载TMR2RLH和TMR2RLL寄存器的值到TMR2H和TMR2L寄存器
   TMR2CN0 |= TMR2CN0_TR2__RUN;
   IE = IE | IE_ET2__ENABLED;
}
 /**
  * 定时器2中断
  * 每中断一次加变量count_ms加1
  */
 SI_INTERRUPT(TIMER2_ISR, TIMER2_IRQn)
 {
   count2_ms++;
   TMR2CN0_TF2H = 0; // Clear the interrupt flag
 }

定时器3初始化和中断服务子程序如下

/**
  * 定时3 初始化
  * @param t_ms 每次中断的时间,时间单位为ms
  * 默认频率为30625HZ,则定时器2的输入时钟为 其12分频
  */
 void timer3_init(uint8_t t_ms)
 {
    SFRPAGE = 0x00;
    TMR3CN0 &= ~(TMR3CN0_TR3__BMASK);
    TMR3H = (uint16_t)(65536-0.001*t_ms*255208)/256;
    TMR3L = (uint16_t)(65536-0.001*t_ms*255208)%256;
    TMR3RLH = (uint16_t)(65536-0.001*t_ms*255208)/256;
    TMR3RLL = (uint16_t)(65536-0.001*t_ms*255208)%256;//溢出后自动装载TMR2RLH和TMR2RLL寄存器的值到TMR2H和TMR2L寄存器
    TMR3CN0 |= TMR3CN0_TR3__RUN;
    EIE1 = EIE1 | EIE1_ET3__ENABLED;
 }
  /**
   * 定时器3中断
   * 每中断一次加变量count_ms加1
   */
  SI_INTERRUPT(TIMER3_ISR, TIMER3_IRQn)
  {
    count3_ms++;
    TMR3CN0 &=0x7F; // Clear the interrupt flag
  }




助工
2021-12-27 10:12:17     打赏
10楼
串口0的调试

      该MCU有2个串口,串口0和串口1,从器件手册中可以看出两个串口还是不同的。串口0需要用定时器来进行产生波特率,而串口1自己可以产生波特率。考虑到在接收数据时一帧数据有时候没有结尾标识。因此采用定时器中断溢出来检测一帧数据是否接收完成。具体思想为:当串口接收到一个byte数据后,重新装载定时器计数值,相当于定时器重新计数,然后打开定时器开始计数(初始化时该定时器是关闭不运行的)。当接收数据结束后,由于定时器没有重新装载数据,这时候会溢出中断,从而能够指示一帧数据接收完成。串口初始化部分参考了石恒瑞同志的串口点灯,在此对其表示感谢。部分关键程序如下。

串口0初始化

void uart0_init(void)
{
  SFRPAGE = 0x00;
  //初始化IO口
  P0MDOUT = P0MDOUT | P0MDOUT_B4__PUSH_PULL | P0MDOUT_B5__OPEN_DRAIN;
  P0MDIN = P0MDIN | P0MDIN_B4__DIGITAL | P0MDIN_B5__DIGITAL;
  SCON0 |= SCON0_REN__RECEIVE_ENABLED;//接收使能
  IE = IE | IE_ES0__ENABLED; //使能中断
  XBR0 = XBR0 | XBR0_URT0E__ENABLED;
}

串口0中断

uint8_t byte_flag=0; //用来表示是否接收到一个字节数据,这样可以在主函数
                     //里对定时器采用重新赋值处理
SI_INTERRUPT(UART0_ISR, UART0_IRQn)
{
  uint8_t res;
  if (SCON0_TI == 1) // Check if transmit flag is set
  {
    tx_flag = 1;
    SCON0_TI = 0; // Clear interrupt flag
  }
  if (SCON0_RI == 1)
  {
      byte_flag=1;
      SCON0_RI = 0;                  //清0中断标志位
      res=SBUF0;
      recive_buff[rec_sta]=res;
      rec_sta++;
      if(rec_sta==RX_BUF_MAX_LEN) rec_sta=0;//如果接受的数据大于最大长度则重新接收
      //定时器0值重载
  }

}

定时器0初始化用于监测数据帧是否结束

//用定时器0来进行接收帧结束判断
void timer0_uart_init(void)
{
    uint8_t TCON_save;
    TIME0_MS=TIME0_MS;                                                      //定时器0的初始化中断时间
    TCON_save = TCON;
    SFRPAGE = 0x00;
    TCON &= ~TCON_TR0__BMASK & ~TCON_TR1__BMASK;
    TH0 = (65536-(uint16_t)(0.001*TIME0_MS*255208))/256;
    TL0 = (65536-(uint16_t)(0.001*TIME0_MS*255208))%256;
    TCON |= (TCON_save & TCON_TR0__BMASK) | (TCON_save & TCON_TR1__BMASK);
    CKCON0=CKCON0|CKCON0_SCA__SYSCLK_DIV_12| CKCON0_T0M__PRESCALE;      //系统时钟的12分频
    TMOD=TMOD|TMOD_T0M__MODE1|TMOD_CT0__TIMER|TMOD_GATE0__DISABLED;     //计数器模式,16bit
    TCON |= TCON_TR0__RUN;
    TCON_TR0 = 0;                                                       //初始化时关闭定时器
    IE = IE|IE_ET0__ENABLED;

}

定时器中断用来表示一帧数据是否结束

SI_INTERRUPT(TIMER0_ISR, TIMER0_IRQn)
 {
   //count0_ms++;
   //TCON_TR0 = 0;                       // Stop Timer 0
   //TH0 = (uint16_t)(65536-0.001*TIME0_MS*255208)/256;
   //TL0 = (uint16_t)(65536-0.001*TIME0_MS*255208)%256;
   //TCON_TR0 = 1;                       // Start Timer 0
   TCON_TR0 = 0;                       // Stop Timer 0
   rx_flag=1;                    //接收完成标志
 }

对定时器0计数寄存器重新装载放在主函数里执行,在STM32或者51里都是放在串口接收中断里执行,但是在该MCU中不行,如果这样操作会造成接收不完整,放在主函数里就可以了。这里先不具体追究具体原因了。

while (1)
    {
       if(byte_flag==1)
        {
            byte_flag=0;
            SFRPAGE = 0x00;
            TH0 = (65536-(uint16_t)(0.001*TIME0_MS*255208))/256;
            TL0 = (65536-(uint16_t)(0.001*TIME0_MS*255208))%256;
            //打开定时器0,这样就会进行计数,当定时器中断发生的时候就表示一帧数据接收完毕
            TCON_TR0 = 1;                       // Start Timer 0
            SFRPAGE = SFRPAGE_save;
        }
       if(rx_flag)
       {
          send_data0(recive_buff,rec_sta);
          rec_sta=0;          //清0以便下一次接收
          LED0=!LED0;
          rx_flag=0;
       }
    }



共35条 1/4 1 2 3 4 跳转至

回复

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