文章目录
前言
功能全部实现,实话说,这届国赛题的串口和eeprom还是很有挑战的,关于这部分需要多加练习和注意;对于常见的一些操作要多加总结,才能够在比赛的时候拿出来就用。在保存信息的时候,一定不要用保存字符串的方式!直接采用保存变量的方式(uchar)即可。我就想提几个需要注意的地方吧。
- 第一个是要注意strcpy和memcpy的区别,一个是字符串的复制,一个是内存的复制。字符串的复制不能用于数组的复制,因为一旦数组中出现了0,那么就会被识别的空字符!
- 注意数组的大小是否能够装的下格式化后的字符串!
- 第二个是eeprom的处理,eeprom分为32页,一页有8Byte,因此可以采用页写的方式来保存。比如你起始地址为0即为第一页,起始位置为8则为第二页,我发现采用页写的方式后,写和读都还是有点问题,因此采用了特殊的方式来进行处理。这部分可以看程序来理解
- 串口数据的接收和判断不要写死了,比如我发送的数据不一定是6位,如果数据宽度不一样,写死的程序势必会造成错误。这个也可以看我串口的处理部分来理解一下。
- 题目我就不发了,网上都有。
效果
说了这么多,看一下效果吧。
<iframe allowfullscreen="true" data-mediaembed="bilibili" id="jkQ87QzS-1622286668733" src="https://player.bilibili.com/player.html?aid=715811384"></iframe>蓝桥杯单片机第五届国赛题目-多功能事件记录器演示
视频链接
代码树
init.c
#include "init.h"
extern uchar bf[8];
void SL(uchar _dev, uchar _data)
{
P0 = _data; SEL(_dev);
}
void Timer1Init(void) //2毫秒@12.000MHz
{
AUXR |= 0x40; //定时器时钟1T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x40; //设置定时初值
TH1 = 0xA2; //设置定时初值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1;
EA = 1;
}
uchar GetKey()
{
if(K3 == 0) return 5;
if(K4 == 0) return 4;
return 0;
}
void TimeRun(t_delay* time)
{
if(time->cnt++ < time->max);
else
{
time->cnt = 0;
time->ok = 1;
}
}
uchar FR(float _data, uchar _dig)
{
uint i = 1;
while(_dig--)
{
i = i*10;
}
return((uint)_data/i%10);
}
void Bf(uchar _0, uchar _1, uchar _2, uchar _3,
uchar _4, uchar _5, uchar _6, uchar _7)
{
bf[0] = _0; bf[1] = _1; bf[2] = _2; bf[3] = _3;
bf[4] = _4; bf[5] = _5; bf[6] = _6; bf[7] = _7;
}
void UartInit(void) //1200bps@12.000MHz
{
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x01; //串口1选择定时器2为波特率发生器
AUXR |= 0x04; //定时器2时钟为Fosc,即1T
T2L = 0x3C; //设定定时初值
T2H = 0xF6; //设定定时初值
AUXR |= 0x10; //启动定时器2
ES = 1;
}
void print(uchar* string)
{
ES = 0; TI = 1;
printf("%s", string);
TI = 0; ES = 1;
}
uchar LedChange(uchar _data, uchar _dig, uchar _state)
{
//LED的状态是反着来的
if(!_state)
{
_data |= 1 << _dig;
}
else
{
_data &= ~(1 << _dig);
}
SL(4, _data);
return _data;
}
main.c
#include "init.h"
enum{LED=4, EXT, SEL, CODE};
code uchar CA[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99,
0x92, 0x82, 0xf8, 0x80, 0x90,
0xff, 0xc6, 0x89, 0xbf};
uchar bf[] = {1, 2, 3, 4, 5, 6, 7, 8};
uchar curDig = 0;
uchar enprint = 0;
uchar key, tmpKey, keyCnt;
enum{KS_GT, KS_AS, KS_WA};keyState=KS_GT;
t_delay delay_200 = {200, 0, 0};
t_delay tx_delay = {50, 0, 0};
t_delay cnt_65535 = {65535, 0, 0};
uchar index = 0;
uchar xdata rxBuf[20] = "0";
//发送缓冲不能小了,要避免出现其他异常情况将发送缓冲装溢出了
uchar xdata txBuf[30] = "0";
uchar revCorrect = 0;
uchar time[3];
uchar jieJin = 0;
uchar Second = 255;
//事件接近的时间
uchar timeCnt = 0;
//模式
enum{S_AUTOT, S_AUTOR}gloSta = S_AUTOT;
//显示模式
enum{DIS_WSD, DIS_TIME, DIS_ITEM}disMode = DIS_WSD;
uchar ledData = 0xfe;
float adcVal, adc1Val;
uchar storeData[5][7] = {0};
//调试使用
//uchar storeData1[5][6] = {{1,2,3, 4 ,5 ,25},
// {1,2,3, 4 ,5 ,25},
// {1,2,3, 4 ,5 ,25},
// {1,2,3, 4 ,5 ,25},
// {1,2,3, 4 ,5 ,25}};
void Timer1Hanle() interrupt 3
{
SL(CODE, 0xff);SL(SEL, 1 << curDig); SL(CODE, CA[bf[curDig]]);
curDig = (curDig + 1)%8;
switch(keyState)
{
case KS_GT:
keyCnt = 0; tmpKey =GetKey(); keyState = KS_AS;
break;
case KS_AS:
if(keyCnt++ < 10);
else if(tmpKey == GetKey())
{
if(tmpKey != key)
{
key = tmpKey;keyState = KS_WA;
}
else
{
keyState = KS_GT;
}
}
else
{
keyState = KS_GT;
}
break;
}
TimeRun(&delay_200);
if(tx_delay.ok == 2)
TimeRun(&tx_delay);
//如果允许计算时间
if(cnt_65535.ok == 2)
TimeRun(&cnt_65535);
}
void Sensor()
{
if(delay_200.ok == 1)
{
delay_200.ok = 0;
Temp_Read();
adcVal = ADC_Read(0x43, 0);
adc1Val = ADC_Read(0x41, 0);
if(adc1Val < 50)
{
jieJin = 1;
ledData = LedChange(ledData, 2, 1);
//如果计时没有开始,那么让计时开始
if(cnt_65535.cnt == 0)
cnt_65535.ok = 2;
}
else
{
jieJin = 0;
ledData = LedChange(ledData, 2, 0);
//如果已经开始计时,那么停止
if(cnt_65535.cnt != 0)
{
uchar tmpString[7];
cnt_65535.ok = 0;
timeCnt = cnt_65535.cnt*2/1000;
tmpString[0] = (uint)tempVal/100;
tmpString[1] = (uint)adcVal;
tmpString[2] = time[2];
tmpString[3] = time[1];
tmpString[4] = time[0];
tmpString[5] = timeCnt;
stack(tmpString);
cnt_65535.cnt = 0;
}
}
DS1302_Read();
}
}
void UartHandle() interrupt 4
{
if(TI) TI = 0;
if(RI)
{
RI = 0;
//如果进来的时候是第一次,因此开启计时
if(tx_delay.ok == 0)
tx_delay.ok = 2;
//进来是因为串口接收中断
if(index < 20)
{
rxBuf[index++] = SBUF;
}
}
}
void UartProcess()
{
//如果接收超时,触发检查事件
//如果进来的时候是因为计时结束
if(tx_delay.ok == 1)
{
//结束标志清零
tx_delay.ok = 0;
//如果接收的数据是6位
if(index == 6)
{
//如果口令正确
if(strncmp(rxBuf, "AAASSS", 6) == 0)
{
enprint = 1;
}
}
//如果口令不是正确的,那么就是错误的
else
{
enprint = 2;
}
//不管哪种情况,都需要将index清空
index = 0;
}
//如果口令正确
if(enprint == 1)
{
uchar i;
enprint = 0;
//根据状态来选择串口的输出
switch(gloSta)
{
case S_AUTOT:
//开启串口输出
revCorrect = 1;
break;
case S_AUTOR:
for(i = 0; i < 5; i++)
{
uchar temp[25] = {0};
sprintf(temp, "{%bu-%bu%%}-{%bu-%bu-%bu}{%bu}\r\n",
storeData[i][0], storeData[i][1], storeData[i][2], storeData[i][3], storeData[i][4], storeData[i][5]);
print(temp);
//很重要,不然会联结
Delay8ms();
}
break;
}
//如果口令不正确关闭输出
}
else if(enprint == 2)
{
enprint = 0;
revCorrect = 0;
}
}
void EventProcess()
{
if(revCorrect)
{
//如果一秒到了
if(Second != time[0])
{
//发送相应命令
Second = time[0];
sprintf(txBuf, "{%.0f-%.0f%%}-{%bu-%bu-%bu}{%bu}\r\n",
tempVal/100.0, adcVal, time[2], time[1], time[0], jieJin);
print(txBuf);
}
}
}
//按键处理
void keyProcess()
{
switch(key)
{
case 4:
gloSta = (gloSta == S_AUTOT)?S_AUTOR:S_AUTOT;
//互锁的先关闭所有,再根据条件开启响应的led
ledData = LedChange(ledData, 1, 0);
ledData = LedChange(ledData, 0, 0);
if(gloSta == S_AUTOR)
{
revCorrect = 0;
ledData = LedChange(ledData, 1, 1);
}
else
{
ledData = LedChange(ledData, 0, 1);
}
break;
case 5:
if(disMode == DIS_WSD)
disMode = DIS_TIME;
else if(disMode == DIS_TIME)
disMode = DIS_ITEM;
else
disMode = DIS_WSD;
break;
}
}
void DisProcess()
{
switch(disMode)
{
case DIS_WSD:
Bf(FR(tempVal, 3), FR(tempVal, 2), F_CC, F_C, F_C, FR(adcVal, 1), FR(adcVal, 0), F_H);
break;
case DIS_TIME:
Bf(FR(time[2], 1), FR(time[2], 0), F_SEP,
FR(time[1], 1), FR(time[1], 0), F_SEP,
FR(time[0], 1), FR(time[0], 0));
break;
case DIS_ITEM:
Bf(F_C, F_C, F_C,
F_SEP, FR(timeCnt, 3), FR(timeCnt, 2),
FR(timeCnt, 1), FR(timeCnt, 0));
break;
}
}
//非常重要
void Delay8ms() //@12.000MHz
{
unsigned char i, j;
i = 94;
j = 95;
do
{
while (--j);
} while (--i);
}
void stack(uchar* string)
{
uchar i = 0;
for(; i < 4; i++)
{
memcpy(storeData[i], storeData[i+1], 6);
}
memcpy(storeData[i], string, 6);
for(i = 0; i < 5; i++)
{
EEPROM_Write(i*8, storeData[i], 6);
EEPROM_Write(i*8, storeData[i], 6);
EEPROM_Write(i*8, storeData[i], 6);
}
}
void StoreInit()
{
uchar i = 0;
//调试使用
// for(i = 0; i < 5; i++)
// {
// EEPROM_Write(i*8, storeData1[i], 7);
// EEPROM_Write(i*8, storeData1[i], 7);
// EEPROM_Write(i*8, storeData1[i], 7);
// }
for(i = 0; i < 5; i++)
{
uchar tmpData[6];
EEPROM_Read(i*8, 6, storeData[i]);
EEPROM_Read(i*8, 6, tmpData);
while(strncmp(tmpData,storeData[i],6) != 0)
{
EEPROM_Read(i*8, 6, storeData[i]);
EEPROM_Read(i*8, 6, tmpData);
}
}
//调试使用
// for(i = 0; i < 5; i++)
// {
// uchar temp[20] = {0};
// sprintf(temp, "%bu%bu%bu%bu%bu%bu\r\n", storeData[i][0],storeData[i][1],storeData[i][2],storeData[i][3],storeData[i][4],storeData[i][5]);
// print(temp);
// //很重要,不然会联结
// Delay8ms();
// }
}
void main()
{
//初始化
BPOFF;RLOFF;
SL(LED, ledData);
//清屏
memset(bf, F_C, 8);
Timer1Init();
UartInit();
DS1302_init(23, 59, 55);
StoreInit();
while(1)
{
Sensor();
UartProcess();
EventProcess();
DisProcess();
if(keyState == KS_WA)
{
keyProcess();
keyState = KS_GT;
}
}
}
onewire.c
/*
程序说明: 单总线驱动程序
软件环境: Keil uVision 4.10
硬件环境: CT107单片机综合实训平台(外部晶振12MHz) STC89C52RC单片机
日 期: 2011-8-9
*/
#include "reg52.h"
#include "type.h"
sbit DQ = P1^4; //单总线接口
float tempVal;
//单总线延时函数
void Delay_OneWire(unsigned int t) //STC89C52RC
{
t = t * 10;
while(t--);
}
//通过单总线向DS18B20写一个字节
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1;
}
Delay_OneWire(5);
}
//从DS18B20读取一个字节
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
Delay_OneWire(1);
DQ = 1;
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(1);
}
return dat;
}
//DS18B20设备初始化
bit init_ds18b20(void)
{
bit initflag = 0;
DQ = 1;
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);
DQ = 1;
Delay_OneWire(8);
initflag = DQ;
Delay_OneWire(5);
return initflag;
}
void Temp_Read()
{
uint tmpVal;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
while(!DQ);
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
tmpVal = Read_DS18B20();
tmpVal = ((uint)Read_DS18B20() << 8) | tmpVal;
//防止乱跳
if(tmpVal*6.25 < 5000)
tempVal = tmpVal*6.25;
}
iic.c
/*
程序说明: IIC总线驱动程序
软件环境: Keil uVision 4.10
硬件环境: CT107单片机综合实训平台 8051,12MHz
日 期: 2011-8-9
*/
#include "reg52.h"
#include "intrins.h"
#include "type.h"
#define DELAY_TIME 5
#define SlaveAddrW 0xA0
#define SlaveAddrR 0xA1
//总线引脚定义
sbit SDA = P2^1; /* 数据线 */
sbit SCL = P2^0; /* 时钟线 */
void IIC_Delay(unsigned char i)
{
do{_nop_();}
while(i--);
}
//总线启动条件
void IIC_Start(void)
{
SDA = 1;
SCL = 1;
IIC_Delay(DELAY_TIME);
SDA = 0;
IIC_Delay(DELAY_TIME);
SCL = 0;
}
//总线停止条件
void IIC_Stop(void)
{
SDA = 0;
SCL = 1;
IIC_Delay(DELAY_TIME);
SDA = 1;
IIC_Delay(DELAY_TIME);
}
//发送应答
void IIC_SendAck(bit ackbit)
{
SCL = 0;
SDA = ackbit; // 0:应答,1:非应答
IIC_Delay(DELAY_TIME);
SCL = 1;
IIC_Delay(DELAY_TIME);
SCL = 0;
SDA = 1;
IIC_Delay(DELAY_TIME);
}
//等待应答
bit IIC_WaitAck(void)
{
bit ackbit;
SCL = 1;
IIC_Delay(DELAY_TIME);
ackbit = SDA;
SCL = 0;
IIC_Delay(DELAY_TIME);
return ackbit;
}
//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
unsigned char i;
for(i=0; i<8; i++)
{
SCL = 0;
IIC_Delay(DELAY_TIME);
if(byt & 0x80) SDA = 1;
else SDA = 0;
IIC_Delay(DELAY_TIME);
SCL = 1;
byt <<= 1;
IIC_Delay(DELAY_TIME);
}
SCL = 0;
}
//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
unsigned char i, da;
for(i=0; i<8; i++)
{
SCL = 1;
IIC_Delay(DELAY_TIME);
da <<= 1;
if(SDA) da |= 1;
SCL = 0;
IIC_Delay(DELAY_TIME);
}
return da;
}
float ADC_Read(uchar _addr, uchar _data)
{
uchar tmpVal;
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(_addr);
IIC_WaitAck();
IIC_SendByte(_data);
IIC_WaitAck();
IIC_Stop();
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
tmpVal = IIC_RecByte();//每次一开始读的是128
IIC_SendAck(0);
return(IIC_RecByte()*99.0/255);
//光敏是阳光越大,电压越大,电阻越小
}
void EEPROM_Write(uchar _addr, uchar* _data, uchar _dig)
{
EA = 0;
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(_addr);
IIC_WaitAck();
while(_dig--)
{
IIC_SendByte(*_data++);
IIC_WaitAck();
}
IIC_Stop();
EA = 1;
}
void EEPROM_Read(uchar _addr, uchar _dig, uchar* _des)
{
EA = 0;
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(_addr);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0xa1);
IIC_WaitAck();
_dig--;
while(_dig--)
{
*_des++ = IIC_RecByte();
IIC_SendAck(0);
}
*_des++ = IIC_RecByte();
IIC_Stop();
EA = 1;
}
ds1302.c
/*
程序说明: DS1302驱动程序
软件环境: Keil uVision 4.10
硬件环境: CT107单片机综合实训平台 8051,12MHz
日 期: 2011-8-9
*/
#include <reg52.h>
#include <intrins.h>
#include "type.h"
sbit SCK=P1^7;
sbit SDA=P2^3;
sbit RST = P1^3; // DS1302复位
extern uchar time[3];
void Write_Ds1302(unsigned char temp)
{
unsigned char i;
for (i=0;i<8;i++)
{
SCK=0;
SDA=temp&0x01;
temp>>=1;
SCK=1;
}
}
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )
{
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
Write_Ds1302(dat);
RST=0;
}
//unsigned char Read_Ds1302_Byte ( unsigned char address )
//{
// unsigned char i,temp=0x00;
// RST=0; _nop_();
// SCK=0; _nop_();
// RST=1; _nop_();
// Write_Ds1302(address);
// for (i=0;i<8;i++)
// {
// SCK=0;
// temp>>=1;
// if(SDA)
// temp|=0x80;
// SCK=1;
// }
// RST=0; _nop_();
// SCK=0; _nop_();
// SCK=1; _nop_();
// SDA=0; _nop_();
// SDA=1; _nop_();
// return (temp);
//}
uchar Read(){
unsigned char i,temp=0x00;
for (i=0;i<8;i++){
SCK=0;
temp>>=1;
if(SDA)
temp|=0x80;
SCK=1;
}
return (temp);
}
void DS1302_init(uchar _h, uchar _m, uchar _s){
uchar i;
_h = (_h/10%10 << 4) | _h%10;
_m = (_m/10%10 << 4) | _m%10;
_s = (_s/10%10 << 4) | _s%10;
Write_Ds1302_Byte(0x8e, 0);
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(0xbe);
Write_Ds1302(_s);
Write_Ds1302(_m);
Write_Ds1302(_h);
for(i = 0; i < 5; i++){
Write_Ds1302(0);
}
RST=0;
Write_Ds1302_Byte(0x8e, 0x80);
}
void DS1302_Read(){
Write_Ds1302_Byte(0x8e, 0);
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(0xbf);
time[0] = Read();
time[1] = Read();
time[2] = Read();
time[0] = (time[0] >> 4)*10 + (time[0] & 0x0f);
time[1] = (time[1] >> 4)*10 + (time[1] & 0x0f);
time[2] = (time[2] >> 4)*10 + (time[2] & 0x0f);
RST=0; _nop_();
SCK=0; _nop_();
SCK=1; _nop_();
SDA=0; _nop_();
SDA=1; _nop_();
Write_Ds1302_Byte(0x8e, 0x80);
}
init.h
#ifndef _INIT_H
#define _INIT_H
#include "stc15f2k60s2.h"
#include "stdio.h"
#include "intrins.h"
#include "type.h"
#include "onewire.h"
#include "iic.h"
#include "string.h"
#include "ds1302.h"
#define SEL(x) P2=P2&0x1f|x<<5;P2=P2&0x1f
sbit BP = P0^6; sbit RL = P0^4;
#define BPON BP = 1; SEL(5)
#define BPOFF BP = 0; SEL(5)
#define RLON RL = 1; SEL(5)
#define RLOFF RL = 0; SEL(5)
#define F_C 10
#define F_CC 11
#define F_H 12
#define F_SEP 13
sbit K1 = P3^0; sbit K2 = P3^1; sbit K3 = P3^2; sbit K4 = P3^3;
typedef struct delay
{
uint max;
uint cnt;
uchar ok;
}t_delay;
void SL(uchar _dev, uchar _data);
void Timer1Init(void);
uchar GetKey();
void TimeRun(t_delay* time);
uchar FR(float _data, uchar _dig);
void Bf(uchar _0, uchar _1, uchar _2, uchar _3,
uchar _4, uchar _5, uchar _6, uchar _7);
void UartInit(void);
void print(uchar* string);
uchar LedChange(uchar _data, uchar _dig, uchar _state);
void stack(uchar* string);
void Delay8ms(); //@12.000MHz
#endif
onewire.h
#ifndef __ONEWIRE_H
#define __ONEWIRE_H
extern float tempVal;
void Temp_Read();
#endif
iic.h
#ifndef _IIC_H
#define _IIC_H
void IIC_Start(void);
void IIC_Stop(void);
bit IIC_WaitAck(void);
void IIC_SendAck(bit ackbit);
void IIC_SendByte(unsigned char byt);
unsigned char IIC_RecByte(void);
float ADC_Read(uchar _addr, uchar _data);
void EEPROM_Write(uchar _addr, uchar* _data, uchar _dig);
void EEPROM_Read(uchar _addr, uchar _dig, uchar* _des);
#endif
ds1302.h
#ifndef __DS1302_H
#define __DS1302_H
void Write_Ds1302(unsigned char temp);
void Write_Ds1302_Byte( unsigned char address,unsigned char dat );
unsigned char Read_Ds1302_Byte( unsigned char address );
void DS1302_Read();
void DS1302_init(uchar _h, uchar _m, uchar _s);
#endif
type.h
#ifndef _TYPE_H
#define _TYPE_H
#define uchar unsigned char
#define uint unsigned int
#endif