基于西风模板格式,在此感谢西风等up主。
定时器0用于Ne555测频率
定时器1用于主程序控制
定时器2用于串口通信
PCA定时器用于超声波
注意:不要死记模板,理解记忆,基础最重要。
上代码,文末有 某度网盘链接 直接下载。
Led.c文件,包含初始化,led、beep和Relay相关函数
static unsigned char temp = 0x00; //目的共用蜂鸣器和继电器 static unsigned char temp_Old = 0xff; void System_Init() { P0 =0xff; P2 = P2 & 0x1f | 0x80; P2 = P2 & 0x1f; P0 =0x00; P2 = P2 & 0x1f | 0xa0; P2 = P2 & 0x1f; } void Led_Disp(unsigned char addr, Enable) { static unsigned char temp = 0x00; static unsigned char temp_Old = 0xff; if(Enable) temp |= (0x01 << addr); else temp &= ~(0x01 << addr); if(temp != temp_Old) { P0 = ~temp; P2 = P2 & 0x1f | 0x80; P2 = P2 & 0x1f; temp_Old = temp; } } void Beep(unsigned char Enable) { if(Enable) temp |= 0x40; else temp &= ~(0x40); if(temp != temp_Old) { P0 = temp; P2 = P2 & 0x1f | 0xa0; //Y5C P2 = P2 & 0x1f; temp_Old = temp; } } void Relay(unsigned char Enable) { if(Enable) temp |= 0x10; else temp &= ~(0x10); if(temp != temp_Old) { P0 = temp; P2 = P2 & 0x1f | 0xa0; P2 = P2 & 0x1f; temp_Old = temp; } }
Key.c 按键扫描函数,判断多按键可将temp定义为16位,然后按位标志来实现
比如temp = 4,就可以写为temp = temp | (0x01 << 0),依次类推即可。
关闭T2R为了避免串口和按键冲突,最后记得再次打开。
(在串口初始化函数中有,可isp生成)
(不用的按键在比赛时记得注释掉,用不上的尽量不敲,比如可能会影响NE555的测量的准确率)
unsigned char Key_Scan() { unsigned char temp = 0; //必须有赋值为0 AUXR &= 0xEF; //关闭T2R 即 定时器2 (串口波特率发生所用的定时器) P44=0; P42=1; P35=1 ;P34=1; if(P33 == 0) temp=4; if(P32 == 0) temp=5; if(P31 == 0) temp=6; if(P30 == 0) temp=7; P44=1; P42=0; P35=1 ;P34=1; if(P33 == 0) temp=8; if(P32 == 0) temp=9; if(P31 == 0) temp=10; if(P30 == 0) temp=11; P44=1; P42=1; P35=0 ;P34=1; if(P33 == 0) temp=12; if(P32 == 0) temp=13; if(P31 == 0) temp=14; if(P30 == 0) temp=15; P44=1; P42=1; P35=1 ;P34=0; if(P33 == 0) temp=16; if(P32 == 0) temp=17; if(P31 == 0) temp=18; if(P30 == 0) temp=19; P3 = 0xff; AUXR |= 0x10; //打开T2R 即 定时器2 (串口波特率发生所用的定时器) return temp; }
Seg.c文件,包含数码管动态显示函数
段码数组duala[],不用记,比赛的时候,直接从赛点资源包拷贝,还自带注释,干嘛要自己敲。
unsigned char code weila[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; unsigned char code duala[] = { 0xc0, //0 0xf9, //1 0xa4, //2 0xb0, //3 0x99, //4 0x92, //5 0x82, //6 0xf8, //7 0x80, //8 0x90, //9 0xff, // 0x88, //A 0x83, //b 0xc6, //C 0xa1, //d 0x86, //E 0x8e //F }; void Seg_Disp(unsigned char wei,dua,point) { //消隐 P0 = 0xff; P2 = P2 & 0x1f |0xe0; P2 &= 0x1f; //给位码 P0 = weila[wei]; P2 = P2 & 0x1f |0xc0; P2 &= 0x1f; //给段码 P0 = duala[dua]; if(point) P0 &= 0x7f;//小数点 P2 = P2 & 0x1f | 0xe0; P2 &= 0x1f; }
iic.c文件,EEPROM和ADC都要用到IIC。
在这里,EEPROM我没选择用块写,按字节写的话,这两个模块的读写程序一样。
a0和a1时用在EEPROM的器件写读地址,90和91是ADC的器件写读地址;
void AT24C02_Write(unsigned char addr, dat)//addr取值0-255,也就是写的位置 { I2CStart(); I2CSendByte(0xa0); I2CWaitAck(); I2CSendByte(addr); I2CWaitAck(); I2CSendByte(dat); I2CWaitAck(); I2CStop(); I2C_Delay(5); } unsigned char AT24C02_Read(unsigned char addr)//addr取值0-255, { unsigned char dat; I2CStart(); I2CSendByte(0xa0); I2CWaitAck(); I2CSendByte(addr); I2CWaitAck(); I2CStart(); I2CSendByte(0xa1); I2CWaitAck(); dat = I2CReceiveByte(); I2CSendAck(1); I2CStop(); return dat; } void Da_Write(unsigned char dat) { I2CStart(); I2CSendByte(0x90); I2CWaitAck(); I2CSendByte(0x41); //0x41为开启数模转换通道,0x0100 0001,第7位是1就可 I2CWaitAck(); I2CSendByte(dat); I2CWaitAck(); I2CStop(); } unsigned char Ad_Read(unsigned char addr) //addr为模数转换的地址,光敏0x41和变阻器0x43 { unsigned char temp; I2CStart(); I2CSendByte(0x90); I2CWaitAck(); I2CSendByte(addr); I2CWaitAck(); I2CStart(); I2CSendByte(0x91); I2CWaitAck(); temp = I2CReceiveByte(); I2CSendAck(1); I2CStop(); return temp; }
onewire.c DS18B20,没啥说的,记得就行。
float DS18B20_Read() { unsigned char low,high; init_ds18b20(); Write_DS18B20(0xcc); Write_DS18B20(0x44); init_ds18b20(); Write_DS18B20(0xcc); Write_DS18B20(0xbe); low = Read_DS18B20(); high = Read_DS18B20(); return ((high<<8)|low)/16.0; }
DS1302,需要写两个函数,写,读函数
void RTC_Set(unsigned char* Rtc) { unsigned char i = 0; Write_Ds1302_Byte(0x8e,0x00); for(i=0;i++;i<3) { Write_Ds1302_Byte(0x84-2*i,Rtc[i]); //16进制写,RTC[]数组也为16进制写 //Write_Ds1302_Byte(0x84-2*i,Rtc[i]/10*16+Rtc[i]%10);//10进制写,RTC[]数组也为10进制写 } Write_Ds1302_Byte(0x8e,0x80); } void RTC_Read(unsigned char* Rtc) { unsigned char i = 0; for(i=0;i++;i<3) { Rtc[i] = Read_Ds1302_Byte(0x85-2*i);//16进制读 //Rtc[i] = Rtc[i]/16*10+Rtc[i]%16; //10进制 } }
超声波模块distance.c
#include "Distance.h" sbit TX_D = P1^0; sbit RX_D = P1^1; void Delay12us() //@12.000MHz { unsigned char i; i = 38; //isp上出来是33,改为38好用一点 while (--i); } void Wave_Init() { unsigned char i; for(i=0;i<8;i++) { TX_D = 1; Delay12us(); TX_D = 0; Delay12us(); } } unsigned char Get_Distance() { unsigned char distance; CMOD = 0x00; CH=CL=0; Wave_Init(); CR = 1; while(CF == 0 && RX_D == 1);//等待,RX_D收到为0 CR = 0; if(CF == 1) { distance = 255; CF = 0; } else { distance = (CH << 8 | CL) * 0.017; //CH和CL单位为us,转换单位时要用到 } return distance; }
串口模块,非常重要,如果出串口,串口在程序中的中作用能和按键一样,控制界面等;
#include "Uart.h" void UartInit(void) //9600bps@12.000MHz { SCON = 0x50; //8位数据,可变波特率 AUXR |= 0x01; //串口1选择定时器2为波特率发生器 AUXR &= 0xFB; //定时器2时钟为Fosc/12,即12T T2L = 0xE6; //设定定时初值 T2H = 0xFF; //设定定时初值 AUXR |= 0x10; //启动定时器2 ES = 1; //生成没有以下两句,启动串口中断 EA = 1; } void SendData(unsigned char dat) { SBUF = dat; //写数据到UART数据寄存器 while(TI == 0); TI = 0; } void SendString(char *s) { while (*s) //检测字符串结束标志 { SendData(*s++); //发送当前字符 } } extern char putchar(char dat) //使用stdio库,printf打印函数 { SBUF = dat; //写数据到UART数据寄存器 while(TI == 0); TI = 0; return dat; // 就是SendData函数加return,然后在.h中声明就可 }
main函数,直接上吧,不想改了,我自己用的测试。
发送love,返回距离和频率。
按下按键,在第0个数码管显示键值。
#include <STC15F2K60S2.H> #include <stdio.h> #include "Distance.h" #include "iic.h" #include "ds1302.h" #include "onewire.h" #include "Key.h" #include "Seg.h" #include "Led.h" #include "Uart.h" #define uint8 unsigned char #define uint16 unsigned int unsigned char Key_Old, Key_Val, Key_Down, Key_Up; unsigned char Seg_Pos; unsigned char Seg_Buf[8] = {10,10,10,10,10,10,10,10}; unsigned char Seg_Poi[8] = {0,0,0,0,0,0,0,0}; unsigned char Led_Buf[8] = {0,0,0,0,0,0,0,0}; unsigned char Uart_Buf[6]; unsigned char Uart_Buf_Index = 0; unsigned char Rtc_Buf[3] = {0x23,0x59,0x55}; unsigned int Key_Slow,Seg_Slow,Uart_Slow; unsigned int Freq,Freq_Time; int Distance; bit Beep_Flag; void Key_Proc() { if(Key_Slow) return; Key_Slow = 1; Key_Val = Key_Scan(); Key_Down = Key_Val & (Key_Old ^ Key_Val); Key_Up = ~Key_Val & (Key_Old ^ Key_Val); Key_Old = Key_Val; if(Key_Down) Seg_Buf[0] = Key_Down; } void Seg_Proc() { if(Seg_Slow) return; Seg_Slow = 1; Distance = Get_Distance(); Seg_Buf[0] = Distance / 100; Seg_Buf[1] = Distance / 10 % 10; Seg_Buf[2] = Distance % 10; Seg_Buf[3] = (Freq / 10000) ; Seg_Buf[4] = (Freq / 1000 % 10) ; Seg_Buf[5] = (Freq / 100 % 10) ; Seg_Buf[6] = (Freq / 10 % 10); Seg_Buf[7] = (Freq % 10); } void Led_Proc() { Led_Buf[0] = 1; Beep(Beep_Flag); Relay(Beep_Flag); } void Uart_Proc() { if(Uart_Slow) return; Uart_Slow = 1; //串口数据三次判断 if(Uart_Buf_Index != 0) //判断有无数据 { if(Uart_Buf_Index == 4) //判断字符个数是否为目的字符串个数 { if(Uart_Buf[0] == 'l' && Uart_Buf[1] == 'o' && Uart_Buf[2] == 'v' && Uart_Buf[3] == 'e') //判断该字符串 { Led_Buf[1] = ~Led_Buf[1]; Beep_Flag = ~Beep_Flag; printf("距离为:%d\r\n",Distance); printf("频率为:%d\r\n",Freq); SendString("I love you too.\r\n"); SendString("send over\r\n"); } } Uart_Buf_Index = 0; //注意将串口数组清零, } } void Timer0Init(void) //1毫秒@12.000MHz { AUXR &= 0x7F; //定时器时钟12T模式 TMOD &= 0xF0; //设置定时器模式 TMOD |= 0x05; //设置定时器模式 TL0 = 0; //设置定时初值 TH0 = 0; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 } void Timer1Init(void) //1微秒@12.000MHz { AUXR &= 0xBF; //定时器时钟12T模式 TMOD &= 0x0F; //设置定时器模式 TL1 = 0x18; //设置定时初值 TH1 = 0xFC; //设置定时初值 TF1 = 0; //清除TF1标志 TR1 = 1; //定时器1开始计时 ET1 = 1; EA = 1; } void Timer1Service() interrupt 3 { if(++Key_Slow == 30) Key_Slow = 0; if(++Seg_Slow == 500) Seg_Slow = 0; if(++Uart_Slow == 200) Uart_Slow = 0; if(++Freq_Time == 1000) { Freq_Time = 0; TR0 = 0; Freq = (TH0<<8)| TL0; TL0 = 0; //设置定时初值 TH0 = 0; //设置定时初值 TR0 = 1; } if(++Seg_Pos == 8) Seg_Pos = 0; Led_Disp(Seg_Pos,Led_Buf[Seg_Pos]); Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos], Seg_Poi[Seg_Pos]); } void Uart1Service() interrupt 4 //必须要写 { if (RI == 1) { Uart_Buf[Uart_Buf_Index] = SBUF; Uart_Buf_Index++; RI = 0; //清除RI位 } // if(Uart_Buf_Index > 5) Uart_Buf_Index = 0; } void main() { System_Init(); Timer0Init(); Timer1Init(); UartInit(); while(1) { Key_Proc(); Seg_Proc(); Led_Proc(); Uart_Proc(); } }
附上工程链接,水平有限,如有错误,请谅解。
百度网盘链接:https://pan.baidu.com/s/100JJPNd_FLn9G6_vOX90aQ?pwd=8888
再次感谢B站 西风 等up主。
祝愿大家都能有所收获。