综述
第四届初赛赛题除了基础的独立按键、数码管显示、继电器和蜂鸣器的控制、LED灯的控制以外,难度增加的部分体现在AD和E2PROM。需要涉及到I2C总线驱动程序的调用。
本解析不代表标准答案或官方答案,仅做分享。若有不足或是更好的写法,望在评论区进行指正!
模板搭建
基础的功能:
1、独立按键
2、数码管显示
可以独立于赛题,提前写好,适用于各类赛题,仅做修改即可。
模板部分代码与解释在第三届赛题分享中
模板代码
根据此题要求对模板做出的改变
1、我们发现该题需要调用I2C通信。而在I2C通信期间,会调用P2端口。也就是说,当我们使能一些芯片的时候,需要改变P2.5-P2.7的值,如果此时直接采用
P2 = 0xXX;
这样的赋值语句,会对I2C产生干扰,也就是说我们只能改变P2端口后三位,而不能影响前面的数值。所以我们需要对模板进行修改。
void openP2(uchar num){
P2 &= 0x1f;
P2 |= (num << 5);
}
编辑这样一个函数
该函数不同于普通的P2赋值语句的区别在于,它不会影响前P2前五位的值。
比如我们要控制LED灯的时候,参照CT107D单片机原理图可以发现——
LE口对应的是Y4C,也就是要控制LED灯时,openP2
函数的输入应为4
,即openP2(4);
需要将showNum
函数进行修改:
void showNum(){
uchar i;
for(i = 0; i < 8; i ++){
if(numGrp[i] != 11){
openP2(6);P0 = (0x01 << i);
openP2(7);P0 = shapeOfNum[numGrp[i]];
Delay1ms();
}
}
openP2(6);P0 = 0x00;
}
程序设计
IIC通信部分(E2PROM和AD)
官方为我们提供了iic的一个c文件一个h头文件。需要在项目中导入iic.c,同时声明iic.h。
同时要注意,由于官方提供的参考驱动代码是基于52单片机的,我们需要在.c文件中将所有的延时函数扩展为原来的12倍,才能成功调用驱动。
官方提供的AT24C02芯片说明书中描述了如何用IIC读取和写入。
uchar ADRead(uchar add){
uchar dat;
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_Stop();
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
dat = IIC_RecByte();
IIC_WaitAck();
IIC_Stop();
return dat;
}
这里A/D转换的 写、读硬件地址为0x90/0x91
。
而word address可以由CT107D单片机原理图上找到,AIN3对应的是Rb2,所以此处函数输入应为0x03
。即add=0x03;
uchar EEPROMRead(uchar add){
uchar dat;
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_Stop();
IIC_Start();
IIC_SendByte(0xa1);
IIC_WaitAck();
dat = IIC_RecByte();
IIC_Stop();
return dat;
}
void EEPROMWrite(uchar add, uchar dat){
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_SendByte(dat);
IIC_WaitAck();
IIC_Stop();
}
E2PROM的与A/D一样,都是通过I2C通信进行的数据转换。
唯一的区别在于,写/读地址为 0xa0/0xa1
DS1302
DS1302的原理部分不再做赘述,主要参考的表格为官方芯片资料DS1302中的下面这个表。
通过循环向0x80 -> 0x8c写初始数据
从0x81 ->0x8d读时间数据来实现调用。
函数实现如下,其中oriTime和nowTime都为长度为8的uchar数组
void writeTime(){
uchar i, add, dat;
for(i = 0; i < 7; i ++){
add = 0x80 + i * 2;
dat = ((oriTime[i] / 10) << 4) + (oriTime[i] % 10);
Write_Ds1302(add,dat);
}
}
void readTime(){
uchar i, dat, add;
for(i = 0; i < 7; i ++){
add = 0x81 + i * 2;
dat = Read_Ds1302 (add);
nowTime[i] = (dat >> 4) * 10 + (dat & 0x0f);
}
}
系统设计
在系统设计的时候,尽量避免对P2的重复操作。第三届系统设计示例
同时程序需要写入两次,因为题目要求E2PROM写入存储,因此在第一次运行程序的时候,要求保留这一段语句:
EEPROMWrite(0x01, 50);//初始化语句,第一次运行后即可注释掉
而写入单片机后,在将这一句话注释掉,再次写入单片机。
这一段操作与E2PROM的原理有关,不明白的朋友可以去了解一下E2PROM。
主函数代码:
#include <stc15f2k60s2.h>
#include <intrins.h>
#include <iic.h>
#include <ds1302.h>
#define uchar unsigned char
uchar keyPress[4] = {0, 0, 0, 0};
uchar keyPressFlag[4] = {0, 0, 0, 0};
uchar code shapeOfNum[11] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0xbf};
uchar numGrp[8] = {11, 0, 5, 0, 0, 0, 0, 0};
uchar oriTime[7] = {0, 30, 8, 3, 2, 7, 19};
uchar nowTime[7] = {0, 0, 0, 0, 0, 0, 0};
void Delay1ms(){
unsigned char i, j;
_nop_();
_nop_();
_nop_();
i = 11;
j = 190;
do
{
while (--j);
} while (--i);
}
void Delay5ms(){
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}
void openP2(uchar num){
P2 &= 0x1f;
P2 |= (num << 5);
}
void showNum(){
uchar i;
for(i = 0; i < 8; i ++){
if(numGrp[i] != 11){
openP2(6);P0 = (0x01 << i);
openP2(7);P0 = shapeOfNum[numGrp[i]];
Delay1ms();
}
}
openP2(6);P0 = 0x00;
}
void keyScan(){
uchar i;
for(i = 0; i < 4; i ++) keyPress[i] = 0;
if((P3 & 0x0f) != 0x0f){
Delay5ms();
if((P3 & 0x0f) != 0x0f){
switch(P3 & 0x0f){
case 0x0e:keyPressFlag[0] = 1;break;
case 0x0d:keyPressFlag[1] = 1;break;
case 0x0b:keyPressFlag[2] = 1;break;
case 0x07:keyPressFlag[3] = 1;break;
}
}
}
for(i = 0; i < 4; i ++){
if(keyPressFlag[i] != 0){
if((P3 & 0x0f) == 0x0f){
keyPress[i] = 1;
keyPressFlag[i] = 0;
}
}
}
}
uchar ADRead(uchar add){
uchar dat;
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_Stop();
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
dat = IIC_RecByte();
IIC_Stop();
return dat;
}
uchar EEPROMRead(uchar add){
uchar dat;
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_Stop();
IIC_Start();
IIC_SendByte(0xa1);
IIC_WaitAck();
dat = IIC_RecByte();
IIC_Stop();
return dat;
}
void EEPROMWrite(uchar add, uchar dat){
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_SendByte(dat);
IIC_WaitAck();
IIC_Stop();
}
void writeTime(){
uchar i, add, dat;
for(i = 0; i < 7; i ++){
add = 0x80 + i * 2;
dat = ((oriTime[i] / 10) << 4) + (oriTime[i] % 10);
Write_Ds1302(add,dat);
}
}
void readTime(){
uchar i, dat, add;
for(i = 0; i < 7; i ++){
add = 0x81 + i * 2;
dat = Read_Ds1302 (add);
nowTime[i] = (dat >> 4) * 10 + (dat & 0x0f);
}
}
void main(){
uchar mod = 0, preMod = 1, wetMod = 0;
uchar wetLine = 50, wet = 0, wetCh = 0;
uchar L10 = 0, buzz = 0, buzzOn = 1;
P2 = 0xa0;P0 = 0x00;
P2 = 0x80;P0 = 0xff;
writeTime();
//EEPROMWrite(0x01, 50);//初始化语句,第一次运行后即可注释掉
Delay5ms();Delay5ms();Delay5ms();//初始化的时候给一小段延时,否则立即读出的时候会出现数据错误
wetLine = EEPROMRead(0x01);
Delay5ms();Delay5ms();Delay5ms();
while(1){
keyScan();
if(mod == 0){ //这里表示进入第一种模式,即自动模式
if(preMod == 1){ //如果该次循环发生在,由手动模式跳转到自动模式,则需要进行LED灯的切换,下面同理
openP2(4);P0 = ~(0x01);openP2(0);
preMod = 0;
}
readTime();
wet = (ADRead(0x03) * 99) / 255;
if(wet >= wetLine){
if(L10 == 1){
openP2(5);P0 = 0x00;openP2(0);
L10 = 0;
}
}
else{
if(L10 == 0){
openP2(5);P0 = 0x10;openP2(0);
L10 = 1;
}
}
if(wetMod == 0){
//设置数码管
numGrp[0] = (nowTime[2] / 10) % 10;
numGrp[1] = nowTime[2] % 10;
numGrp[2] = 10;
numGrp[3] = (nowTime[1] / 10) % 10;
numGrp[4] = nowTime[1] % 10;
numGrp[5] = 11;
numGrp[6] = (wet / 10) % 10;
numGrp[7] = wet % 10;
if(keyPress[1] == 1){
wetMod = 1;
wetCh = wetLine;
}
}
else{
numGrp[0] = 10;
numGrp[1] = 10;
numGrp[2] = 11;
numGrp[3] = 11;
numGrp[4] = 11;
numGrp[5] = 11;
numGrp[6] = (wetCh / 10) % 10;
numGrp[7] = wetCh % 10;
if((keyPress[2] == 1) && (wetCh < 99)){
wetCh ++;
}
else if((keyPress[3] == 1) && (wetCh > 0)){
wetCh --;
}
if(keyPress[1] == 1){
wetLine = wetCh;
EEPROMWrite(0x01, wetLine);
wetMod = 0;
}
}
if(keyPress[0] == 1){
mod = 1;
}
}
else if(mod == 1){
if(preMod == 0){
openP2(4);P0 = ~(0x02);openP2(0);
preMod = 1;
}
readTime();
wet = (ADRead(0x03) * 99) / 255;
//数码管显示
numGrp[0] = (nowTime[2] / 10) % 10;
numGrp[1] = nowTime[2] % 10;
numGrp[2] = 10;
numGrp[3] = (nowTime[1] / 10) % 10;
numGrp[4] = nowTime[1] % 10;
numGrp[5] = 11;
numGrp[6] = (wet / 10) % 10;
numGrp[7] = wet % 10;
if(buzzOn == 1){
if(wet >= wetLine){
if(buzz == 1){
openP2(5);P0 = 0x00;openP2(0);
buzz = 0;
}
}
else{
if(buzz == 0){
openP2(5);P0 = 0x40;openP2(0);
buzz = 1;
}
}
}
else{
if(buzz == 1){
openP2(5);P0 = 0x00;openP2(0);
buzz = 0;
}
}
if(keyPress[1] == 1){
buzzOn = !buzzOn;
}
if(keyPress[2] == 1){
openP2(5);P0 = 0x10;openP2(0);
L10 = 1;
}
if(keyPress[3] == 1){
openP2(5);P0 = 0x00;openP2(0);
L10 = 0;
}
if(keyPress[0] == 1){
mod = 0;
}
}
showNum();
}
}