前言:定时器是用的珍妮码多。
题目
个人理解与感受
首先看上去模块比前两年多了,但其实底层大部分用的是IIC,另外两个没考。来了个PWM NE555 长短按检测 LED秒闪定时器全家桶我是真的服了,PWM还挤不进去定时器0和1(因为1KHZ的80%占空比需要有100μs才能好好走,而频率测量占用的完全是两个极端:一个要1s一个就要一次),把它塞进定时器2的后果是优先级太低了,占空比调不准确。
然后我发现记录“数据记录”的次数,好像连续考了三年,还是两年,非常奇特,一个标志位的事情搞定。
这次新加了Y5,之前都是只考Y4(LED)、Y6(数码管位选)、(Y7)(数码管段选),这也称得上是一种创新吧。霍尔元件对参赛选手对单片机开发以外的学科要求比较高,可能就不大会考,步进电机更是要大量占用定时器资源,更不太可能考。
底层驱动代码
玛德把IIC给玩出花了,IIC全来一遍,另外两个驱动愣是用都不用。
IIC
unsigned char dianya(){
unsigned char ret;
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(0x43);
I2CWaitAck();
I2CStart();
I2CSendByte(0x91);
I2CWaitAck();
ret = I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return ret;
}
void DAC(unsigned char dat){
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(0x41);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
void eepromwrite(unsigned char dat){
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(0x00);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
unsigned char eepromread(){
unsigned char ret;
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(0x00);
I2CWaitAck();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
ret = I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return ret;
}
延迟函数以及锁存器
void Delay(unsigned char xms) //@12.000MHz
{
while(xms--){
unsigned char data i, j;
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}
}
#include <STC15F2K60S2.H>
#include "delay.h"
void inithc138(unsigned char channel){
switch(channel){
case 0:P2 = (P2 & 0x1f) | 0x00;break;
case 4:P2 = (P2 & 0x1f) | 0x80;break;
case 5:P2 = (P2 & 0x1f) | 0xa0;break;
case 6:P2 = (P2 & 0x1f) | 0xc0;break;
case 7:P2 = (P2 & 0x1f) | 0xe0;break;
}
}
void outputp0(unsigned char channel,unsigned char dat){
inithc138(0);
P0 = dat;
inithc138(channel);
inithc138(0);
}
void showsmg(unsigned char pos,unsigned char dat){
outputp0(7,0xff);
outputp0(6,0x01 << (pos - 1));
outputp0(7,dat);
Delay(1);
}
void initsys(){
outputp0(4,0xff);
outputp0(5,0x00);
outputp0(6,0xff);
outputp0(7,0xff);
}
主程序核心代码
#include <STC15F2K60S2.H>
#include "intrins.h"
#include "delay.h"
#include "inithc138.h"
#include "iic.h"
#define de 5
code unsigned char Seg_Table[17] =
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
0x88, //A
0x83, //b
0xc6, //C
0xa1, //d
0x86, //E
0x8e, //F
0xbf
};
unsigned char count = 0;
unsigned int hz_count = 0;
unsigned int tmp = 0;
unsigned char show = 0;
bit danwei1 = 0;//频率显示的单位
unsigned char voltage = 0;//用于读取电压值
unsigned int wet = 0;//用于转化湿度值
unsigned int time = 0;//超声波接受时间
unsigned int distance = 0;//测距距离
bit danwei2 = 0;//测距界面显示的单位
unsigned char canshu = 0;//参数界面切换
unsigned char hzcanshu = 15;//频率参数
unsigned char wetcanshu = 20;//湿度参数
unsigned char discanshu = 50;//距离参数
bit keystat = 0;//按键状态
unsigned char count_key = 0;//用于数长按的秒数
unsigned int wetDAC = 0;//湿度对输出电压的转换
bit motor = 0;//脉冲输出
unsigned char y5stat = 0x00;
unsigned char pwm = 0;
bit flag = 0;
unsigned char ledstat = 0xff;
bit ledspark = 0;//LED闪烁
bit flag2 = 0;//继电器吸合状态
unsigned char xihecishu = 0;//继电器吸合次数
//**************************************频率测量
void Timer0_Isr(void) interrupt 1
{
hz_count++;
}
void Timer1_Isr(void) interrupt 3
{
count++;
if(keystat){
count_key++;
}
if(count == 20){
tmp = hz_count;
hz_count = 0;
count = 0;
}
if(count % 2 == 0){
ledspark = ~ledspark;
}
}
void Timer_Init(void) //@12.000MHz
{
//50毫秒
TMOD = 0x07; //设置定时器模式
TL1 = 0xB0; //设置定时初始值
TH1 = 0x3C; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1; //使能定时器1中断
//单次
TL0 = 0xFF; //设置定时初始值
TH0 = 0xFF; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
EA = 1;
}
//**************************************
//**************************************湿度读取
void shidu(){
voltage = dianya();
Delay(1);
wet = voltage * 100 / 255;
}
//**************************************
//**************************************超声波测距
sbit TX = P1^0;
sbit RX = P1^1;
void Delay12us(void) //@12.000MHz
{
unsigned char data i;
_nop_();
_nop_();
i = 33;
while (--i);
}
void fangbo(){
unsigned char i;
for(i = 0;i < 8;i++){
TX = 1;
Delay12us();
TX = 0;
Delay12us();
}
}
void ceju(){
CMOD = 0x00; //设置定时器模式
CL = 0x00; //设置定时初始值
CH = 0x00; //设置定时初始值
CF = 0; //清除TF0标志
CR = 0; //定时器0开始计时
fangbo();
CR = 1;
while((RX == 1) && (CH < 17));
CR = 0;
if(RX == 0){
RX = 1;
time = (CH << 8) | CL;
distance = time * 0.017;
}
}
//**************************************
//**************************************显示功能
void pinlvjiemian(){//频率界面
showsmg(1,Seg_Table[15]);
if(danwei1 == 0){
showsmg(8,Seg_Table[tmp % 10]);
if(tmp > 9){showsmg(7,Seg_Table[tmp / 10 % 10]);}
if(tmp > 99){showsmg(6,Seg_Table[tmp / 100 % 10]);}
if(tmp > 999){showsmg(5,Seg_Table[tmp / 1000 % 10]);}
if(tmp > 9999){showsmg(4,Seg_Table[tmp / 10000 % 10]);}
}else if(danwei1){
showsmg(8,Seg_Table[tmp / 100 % 10]);
showsmg(7,Seg_Table[tmp / 1000 % 10] & 0x7f);
if(tmp > 9999){showsmg(6,Seg_Table[tmp / 10000 % 10]);}
}
}
void shidujiemian(){//湿度界面
showsmg(1,0x89);
showsmg(8,Seg_Table[wet % 10]);
if(wet > 9){showsmg(7,Seg_Table[wet / 10 % 10]);}
if(wet > 99){showsmg(6,Seg_Table[wet / 100]);}
}
void cejujiemian(){
showsmg(1,Seg_Table[10]);
if(danwei2 == 0){
showsmg(8,Seg_Table[distance % 10]);
if(distance > 9){showsmg(7,Seg_Table[distance / 10 % 10]);}
if(distance > 99){showsmg(6,Seg_Table[distance / 100 % 10]);}
}else if(danwei2){
showsmg(8,Seg_Table[distance % 10]);
showsmg(7,Seg_Table[distance / 10 % 10]);
showsmg(6,Seg_Table[distance / 100 % 10] & 0x7f);
}
}
/*void showcishu(){
showsmg(8,Seg_Table[xihecishu % 10]);
showsmg(7,Seg_Table[xihecishu / 10 % 10]);
showsmg(6,Seg_Table[xihecishu / 100 % 10]);
}*/
//**************************************
//**************************************参数界面
void pinlvcanshu(){//频率参数
showsmg(1,0x8c);
showsmg(2,Seg_Table[1]);
showsmg(7,Seg_Table[hzcanshu / 10 % 10] & 0x7f);
showsmg(8,Seg_Table[hzcanshu % 10]);
if(hzcanshu > 99){
showsmg(6,Seg_Table[hzcanshu / 100]);
}
}
void shiducanshu(){
showsmg(1,0x8c);
showsmg(2,Seg_Table[2]);
showsmg(7,Seg_Table[wetcanshu / 10]);
showsmg(8,Seg_Table[wetcanshu % 10]);
}
void julicanshu(){
showsmg(1,0x8c);
showsmg(2,Seg_Table[3]);
showsmg(7,Seg_Table[discanshu / 100] & 0x7f);
showsmg(8,Seg_Table[discanshu / 10 % 10]);
}
void canshujiemian(){
switch(canshu){
case 0:pinlvcanshu();break;
case 1:shiducanshu();break;
case 2:julicanshu();break;
}
}
//**************************************
//**************************************
void showselect(){
switch(show){
case 0:pinlvjiemian();break;
case 1:shidujiemian();break;
case 2:cejujiemian();break;
case 3:canshujiemian();break;
//case 4:showcishu();break;
}
}
//**************************************
//**************************************DAC输出
void DACshuchu(){
if(wet > 80){
wetDAC = 255;
}else if((wet >= 10) && (wet <= 80)){
wetDAC = 2.91429 * wet + 21.8568;
}else if(wet < 10){
wetDAC = 51;
}
DAC(wetDAC);
}
//**************************************
//**************************************按键扫描
void scankey(){
if(P33 == 0){Delay(de);while(P33 == 0){showselect();}//S4
show++;
show %= 4;
canshu = 0;
}
if(show == 3){
if(P32 == 0){Delay(de);while(P32 == 0){showselect();}//S5
canshu++;
canshu %= 3;
}
}
if(P31 == 0){Delay(de);while(P31 == 0){showselect();}//S6
if(show == 2){
danwei2 = ~danwei2;
}
if(show == 3){
if(canshu == 0){hzcanshu += 5;if(hzcanshu > 120){hzcanshu = 10;}}
if(canshu == 1){wetcanshu += 10;if(wetcanshu > 60){wetcanshu = 10;};}
if(canshu == 2){discanshu += 10;if(discanshu > 120){discanshu = 10;}}
}
}
if(P30 == 0){
Delay(de);
while(P30 == 0){
showselect();
if(show == 1){
keystat = 1;
}
}
keystat = 0;
if(count_key <= 20){
if(show == 0){
danwei1 = ~danwei1;
}
if(show == 3){
if(canshu == 0){hzcanshu -= 5;if(hzcanshu < 10){hzcanshu = 120;}}
if(canshu == 1){wetcanshu -= 10;if(wetcanshu < 10){wetcanshu = 60;};}
if(canshu == 2){discanshu -= 10;if(discanshu < 10){discanshu = 120;}}
}
}else if(count_key > 20){
xihecishu = 0;
eepromwrite(xihecishu);
Delay(2);
}
count_key = 0;
}
}
//**************************************
//**************************************继电器控制功能
void jidianqikongzhi(){
if(distance > discanshu){
if(flag2 == 0){
xihecishu++;
eepromwrite(xihecishu);
Delay(2);
}
flag2 = 1;
y5stat = y5stat | 0x10;
}else{
flag2 = 0;
y5stat = y5stat & ~0x10;
}
outputp0(5,y5stat);
}
//**************************************
//**************************************脉冲输出功能
void Timer2_Isr(void) interrupt 12
{
pwm++;
if(pwm > 10){
pwm = 1;
}
if(motor){
if(pwm <= 8){
flag = 1;
}else if((pwm > 8) && (pwm <= 10)){
flag = 0;
}
}else if(motor == 0){
if(pwm <= 8){
flag = 0;
}else if((pwm > 8) && (pwm <= 10)){
flag = 1;
}
}
}
void Timer2_Init(void) //100微秒@12.000MHz
{
AUXR &= 0xFB; //定时器时钟12T模式
T2L = 0x9C; //设置定时初始值
T2H = 0xFF; //设置定时初始值
AUXR |= 0x10; //定时器2开始计时
IE2 |= 0x04; //使能定时器2中断
}
void pinlvpanduan(){
if(tmp > (100*hzcanshu)){
motor = 1;
}else{
motor = 0;
}
}
void maichongshuchu(){
pinlvpanduan();
if(flag){
y5stat = y5stat | 0x20;
}else{
y5stat = y5stat & ~0x20;
}
outputp0(5,y5stat);
}
//**************************************
//**************************************LED指示灯
void LED(){
if((show == 0) && (ledspark == 1)){
ledstat = ledstat & ~0x01;
}else{
ledstat = ledstat | 0x01;
}
if((show == 1) && (ledspark == 1)){
ledstat = ledstat & ~0x02;
}else{
ledstat = ledstat | 0x02;
}
if((show == 2) && (ledspark == 1)){
ledstat = ledstat & ~0x04;
}else{
ledstat = ledstat | 0x04;
}
if(tmp > (hzcanshu*100)){
ledstat = ledstat & ~0x08;
}else{
ledstat = ledstat | 0x08;
}
if(wet > wetcanshu){
ledstat = ledstat & ~0x10;
}else{
ledstat = ledstat | 0x10;
}
if(distance > discanshu){
ledstat = ledstat & ~0x20;
}else{
ledstat = ledstat | 0x20;
}
outputp0(4,ledstat);
}
//**************************************
//**************************************
void main(){
xihecishu = eepromread();
Delay(2);
initsys();
Timer_Init();
Timer2_Init();
while(1){
ceju();
shidu();
DACshuchu();
jidianqikongzhi();
maichongshuchu();
LED();
scankey();
showselect();
outputp0(4,ledstat);
}
}
展示视频
注:
DAC我一个人不好拿手机拍,因而不在视频中展示
脉冲输出功能没办法直接体现,我用L8代替了,效果还不错,只不过定时器用的是定时器2,优先级很低,就导致看起来断断续续。但是明显可以看出超过频率参数的时候led闪的更连续,没超过时闪的断断续续。
对于存储在EEPROM内的触发次数,我临时创了一个界面来展示,在主程序代码中注释掉了。
G013L8代替MOTOR
G013
G013触发次数展示