这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界» 论坛首页» 活动中心» 板卡试用» 【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 跳转至

回复

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