📄 proccess.c
字号:
#include "cc.h"
#include "event.h"
#include "display.h"
#include "checksum.h"
#include "key.h"
#include "proccess.h"
#include <avr/wdt.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
struct EEPROM {
u16_t index; // data address in eeprom
u8_t sumw[4]; // data in eeprom
u8_t chksum; // checksum for mbuf
};
struct EEPROM save;
// 从上电开始的‘功’的总和,要保存在EEPROM的数据。定义它是为方便计算。
#define SUM_W (*((u32_t *)(&save.sumw[0])))
struct DISPLAY {
u32_t u; // 显示电压
u32_t i; // 显示电流
u32_t p; // 显示功率
// W = Σ(Pi*T)/3600, T = 1, Pi = sample_v * sample_i
u32_t w; // 按秒累计,1小时的功;满1小时除3600后,再累加给:SUM_W
u16_t cnt_3600; // 3600秒计数,记录w累加过的时间
u8_t cnt_down; // 30秒倒计时,用于持续按键功能
};
struct DISPLAY disbuf;
u8_t key_type = 0x00;
#define KEY_TYPE_U 0 // 当前显示电压
#define KEY_TYPE_I 1 // 当前显示电流
#define KEY_TYPE_P 2 // 当前显示功率
u8_t proflag = 0x00;
#define PRO_POWER_OFF 0 // bit0: 掉电进程标志
#define PRO_SAVE 1 // bit1:保存进程标准
#define PRO_DISPLAY 2 // bit2:切换显示进程标志
#define PRO_ONE_KEY 3 // bit3:单次按键进程标志
#define PRO_KEEP_KEY 4 // bit4:持续按键进程标志
// =========================================================================
void proc_init(void) {
u8_t tmp;
wdt_enable(WDTO_1S);
init_display();
init_interrupt();
init_key();
// read W from eeprom
cli();
eeprom_busy_wait();
save.index = eeprom_read_word ((u16_t *)0);
if (save.index > 511) { // 新的EEPROM读出总是: 0xFF
// 初始化EEPROM
save.index = 0x0002;
SUM_W = 0x00000000;
save.chksum = 0x00;
save.chksum = checksum(&save.sumw[0], 5);
eeprom_busy_wait();
eeprom_write_block(&save.index, (u8_t *)0, 7);
// 重读 index
eeprom_busy_wait();
save.index = eeprom_read_word((u16_t *)0);
}
// 读数据块,最大读10次
for (tmp = 0; tmp < 10; tmp++) {
eeprom_busy_wait();
eeprom_read_block(&save.sumw[0], (u8_t *)save.index, 5);
if (checksum(&save.sumw[0], 5) == 0) {
break;
}
}
if (tmp == 10) { // 读失败,无法得出原始数据!给错误信息显示在LED6-13: "FFFFFFFF"
for (tmp = 5; tmp < 13; tmp++) {
bcd_buf[tmp] = 0x0f; // "F"
}
sei();
while (1); // 永远的停在这里!
}
else { // 正常结束
eeprom_busy_wait();
sei();
}
// 显示进程
proflag = _BV(PRO_DISPLAY);
key_type = KEY_TYPE_U; // 默认显示电压
//key_type = KEY_TYPE_I; // 默认显示电流
//key_type = KEY_TYPE_P; // 默认显示功率
disbuf.w = 0; // 当前 MW*S
disbuf.cnt_3600 = 0; // 小时秒计数
disbuf.cnt_down = 30; // 30秒倒计时,用于持续按键功能
return;
}
// =========================================================================
void proc_poweroff(void) {
if ((flag0 & _BV(FLAG0_INT0)) == 0) {
return;
}
// 掉电中....
display_off();
proflag |= _BV(PRO_SAVE);
flag0 &= ~_BV(FLAG0_INT0);
return;
}
// =========================================================================
void proc_calculate(void) {
u32_t tmp1, tmp2, tmp3;
if ((flag0 & _BV(FLAG0_T1)) == 0) {
return; // 没到1秒,等待采样
}
else {
flag0 &= ~_BV(FLAG0_T1); // 清标志
}
// 警告:类型不匹配的计算将导致意外的结果!
// 计算采样显示电压(伏)
tmp1 = (u32_t)sample_i;
tmp1 *= C_DIS_U;
tmp1 /= C_SMP_U;
if (tmp1-C_DIS_U*0.1 >= 0){
tmp1 -= C_DIS_U*0.1;
}
else {
tmp1 = 0 ;
}
disbuf.u = tmp1;
// 测试计算结果
//htobcd5(&bcd_buf[0], disbuf.u);
// 计算采样显示电流(千安)
tmp2 = (u32_t)sample_v;
tmp2 *= C_DIS_I;
tmp2 /= C_SMP_I;
disbuf.i = tmp2;
// 测试计算结果
//htobcd5(&bcd_buf[0], disbuf.i);
// 计算显示功率(兆瓦)
tmp3 = tmp1;
// tmp3 *= tmp2;
// tmp3 /= 1000;
disbuf.p = tmp3;
// 测试计算结果
//htobcd8(&bcd_buf[5], disbuf.p);
// 计算累加1小时内的功 (按秒计算)
disbuf.w += tmp3;
// 测试计算结果
// htobcd8(&bcd_buf[5], disbuf.w);
// 累计1小时的功
if (++disbuf.cnt_3600 > 3600) { // 1小时到,计算 MW*H 总和
disbuf.cnt_3600 = 0; // 计数复位
proflag |= _BV(PRO_SAVE); // 启动保存进程
}
// 累计持续按键时间
if ((proflag & _BV(PRO_KEEP_KEY)) != 0) {
if (--disbuf.cnt_down == 0) {
disbuf.cnt_down = 30;
disbuf.w = 0;
SUM_W = 0;
proflag |= _BV(PRO_SAVE);
}
}
proflag |= _BV(PRO_DISPLAY); // 启动显示进程
return;
}
// ===========================================================================
void proc_save(void) {
struct EEPROM readsave;
u32_t tmp32;
u16_t tmp16;
u8_t tmp8;
if ((proflag & _BV(PRO_SAVE)) == 0) {
return;
}
cli();
tmp32 = disbuf.w + 1800; // 四舍五入;保证不至于永远丢“功”显示不出来的0.01~0.09
tmp32 /= 3600; // 由于显示电流小数点1位,电压小数点1位,功小数点1位,所以要再除10
SUM_W += tmp32; // 累加给总计
if (SUM_W > 99999999) { // “功”累计超过8位BCD码,就循环....
SUM_W -= 100000000;
}
disbuf.w = 0; // 清除1小时的记录(因为已经累加给总记录了)
disbuf.cnt_3600 = 0; // 计数复位
save.chksum = 0;
save.chksum = checksum(&save.sumw[0], 5); // 计算校验码
for (tmp16 = save.index; tmp16 < 511; tmp16 += 5) { // ATMEGA8 的 EEPROM = 512
// 3次读写失败就换块
for (tmp8 = 0; tmp8 < 3; tmp8++) {
eeprom_busy_wait();
eeprom_write_block(&save.sumw[0], (u8_t *)save.index, 5); // 写块数据
eeprom_busy_wait();
eeprom_read_block(&readsave.sumw[0], (u8_t *)save.index, 5); // 读刚写的块
if (checksum(&readsave.sumw[0], 5) == 0) { // 校验成功,写入正确
goto fin_save;
}
}
if (tmp8 == 3) { // 已经进行过3次操作并失败,换块!
save.index += 5; // 偏移 5 byte
eeprom_busy_wait();
eeprom_write_word((u16_t *)0, save.index); // 更新 index
}
}
// 所有块都写坏,报废!显示 "EEEEEEEE"
for (tmp8 = 5; tmp8 < 13; tmp8++) {
bcd_buf[tmp8] = 0x0e; // "E"
}
sei();
while (1); // 永远的停在这里!
fin_save: // 保存完成
eeprom_busy_wait();
sei();
proflag &= ~_BV(PRO_SAVE); // 清除该进程标志
return;
}
// ===========================================================================
void proc_key(void) {
u8_t tmp8;
if (read_key() == 0) { // 无键按下
proflag &= ~_BV(PRO_ONE_KEY); // 清除单次按键标志
proflag &= ~_BV(PRO_KEEP_KEY); // 清除持续按键标志
disbuf.cnt_down = 30; // 复位持续按键倒计数
return; // 退出
}
// 检查是否持续按键
if ((proflag & _BV(PRO_ONE_KEY)) != 0x00) { // 以前按过键,状态为连续按键状态
proflag |= _BV(PRO_KEEP_KEY);
return;
}
// 不是持续按键,是单次按键
// 抖动检测
tmp8 = cnt_t2;
while (((cnt_t2 - tmp8) < 3) && (read_key() == 1)); // 150mS去抖动等待
if (read_key() == 1) { //有效单次按键
proflag |= _BV(PRO_ONE_KEY); // 设置单次按键标志
// 切换显示类型
if (++key_type == 3) {
key_type = 0;
}
proflag |= _BV(PRO_DISPLAY); // 启动显示切换进程
}
else {
proflag &= ~_BV(PRO_ONE_KEY); // 清除单次按键标志
proflag &= ~_BV(PRO_KEEP_KEY); // 清除持续按键标志
disbuf.cnt_down = 30; // 复位持续按键倒计数
}
return;
}
// ===========================================================================
void proc_display(void) {
u32_t tmp1, tmp2;
// 是否起用本进程?
if ((proflag & _BV(PRO_DISPLAY)) == 0) {
return;
}
// 显示‘功’W
tmp1 = disbuf.w + 1800; // 四舍五入;保证不至于永远丢“功”显示不出来的0.01~0.09
tmp1 /= 3600; // 由于显示电流小数点1位,电压小数点1位,功小数点1位,所以要再除10
tmp2 = SUM_W;
tmp2 += tmp1;
if (tmp2 > 99999999) { // “功”累计超过8位BCD码,就循环....
tmp2 -= 100000000;
}
htobcd8(&bcd_buf[5], tmp2); // 刷新显示缓冲区
bcd_buf[10] |= 0x80; // 小数点位置
// 倒计时显示
if ((proflag & _BV(PRO_KEEP_KEY)) != 0) { // 倒计时清除"FFF30".."FFF29"...
htobcd5(&bcd_buf[0], disbuf.cnt_down);
bcd_buf[0] = 0x0f; // "F"
bcd_buf[1] = 0x0f; // "F"
bcd_buf[2] = 0x0f; // "F"
goto fin_display;
}
// V/I/P显示
switch (key_type)
{
case KEY_TYPE_U:
htobcd5(&bcd_buf[0], disbuf.u); // 显示数
bcd_buf[2] |= 0x80; // 小数点位置
LED14_ON;
LED15_OFF;
LED16_OFF;
break;
case KEY_TYPE_I:
htobcd5(&bcd_buf[0], disbuf.u); // 显示数
bcd_buf[2] |= 0x80; // 小数点位置
LED14_OFF;
LED15_ON;
LED16_OFF;
break;
case KEY_TYPE_P:
htobcd5(&bcd_buf[0], disbuf.u); // 显示数
bcd_buf[2] |= 0x80; // 小数点位置
LED14_OFF;
LED15_OFF;
LED16_ON;
break;
}
fin_display:
proflag &= ~_BV(PRO_DISPLAY);
return;
}
// ===========================================================================
void proc_test_sample(void) {
if ((flag0 & _BV(FLAG0_T1)) != 0) {
htobcd5(&bcd_buf[0], (u32_t)sample_i);
htobcd8(&bcd_buf[5], (u32_t)sample_v);
flag0 &= ~_BV(FLAG0_T1);
}
return;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -