【EFM8BB52单片机】室内环境监测物联网系统(完结)
快递邮寄到了学校,取的时候站点想要大大的快递箱子,就把快递箱子拆开送给了他们。能展示的就只剩下朴实无华的板子和数据线。如图1所示,图一是收到的物件合影,图2和图3是开发板的反正面,给我的感觉就是开发板很质朴,做工很沉重,简约而不缺实用精神。

图1 物件合影

图2 开发板正面图

图3 开发板反面图
该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-----Timer5Timer2--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
} 该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;
}
}回复
| 有奖活动 | |
|---|---|
| 硬核工程师专属补给计划——填盲盒 | |
| “我踩过的那些坑”主题活动——第002期 | |
| 【EEPW电子工程师创研计划】技术变现通道已开启~ | |
| 发原创文章 【每月瓜分千元赏金 凭实力攒钱买好礼~】 | |
| 【EEPW在线】E起听工程师的声音! | |
| 高校联络员开始招募啦!有惊喜!! | |
| 【工程师专属福利】每天30秒,积分轻松拿!EEPW宠粉打卡计划启动! | |
| 送您一块开发板,2025年“我要开发板活动”又开始了! | |
我要赚赏金
