比赛难度不大,主要在以下几个细节容易出错
1.计入频率及频率的处理值时应选择尽可能大的类型,又因为会变成负数,所以不能使用unsigned int
同时也应注意unsigned int 和int 类型进行计算时会输出一个unsigned int 类型的值
2.为了保证DS1302数据的收发正常,要在前后加上EA=0;EA=1;防止中断干扰进程,所以不能将DS1302函数放在中断内扫描,会导致程序bug
3.DS1302读写之后应该清空SDA的状态,防止影响下一次的收发数据
/*-----------------------------------------------
文件功能:第十五届蓝桥杯单片机省赛程序设计代码
主要功能:频率测量与校准、超限报警、时间显示、DAC输出
硬件平台:IAP15F2K61S2单片机,12MHz时钟,CT107D实训平台
作者:minuodeer
版本:1.1
修改说明:修复负数显示问题,优化代码结构
-----------------------------------------------*/
/******************** 头文件包含 ********************/
#include "ds1302.h" // DS1302实时时钟驱动
#include "iic.h" // I2C通信协议驱动
#include <intrins.h> // 提供_nop_()函数
/******************** 硬件引脚定义 ********************/
sbit L1 = P0^0; // 界面状态指示灯(频率界面闪烁)
sbit L2 = P0^1; // 报警指示灯(超限/错误状态)
sbit R3 = P3^2; // 矩阵键盘行选择线1
sbit R4 = P3^3; // 矩阵键盘行选择线2
sbit K2 = P4^2; // 矩阵键盘列输入2(S8/S9按键)
sbit K1 = P4^4; // 矩阵键盘列输入1(S4/S5按键)
/******************** 全局变量声明 ********************/
void smg_display(void); // 数码管显示函数声明
// 共阳极数码管段码表(0-9, A-F, 特殊符号)
unsigned char code led_nodot[] = {
0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8, // 0-7
0x80,0x98,0x88,0x83,0xC6,0xA1,0x86,0x8E, // 8-F
0xBF,0x8C,0xC7,0x89 // 特殊符号('-', 'L', 'P', 'H')
};
unsigned char state_S4 = 0; // 主界面状态(0-3: 频率/参数/时间/回显)
unsigned char state_S5 = 0; // 子界面状态(0-1: 参数子界面/回显子界面)
unsigned char s, m, h; // 当前时间(秒、分、时)
unsigned char s1, m1, h1; // 最大频率发生时间
unsigned int t = 0; // 定时器1中断计数
unsigned int f_check = 2000; // 超限阈值(范围:1000-9000Hz)
int c_adjust = 0; // 校准值(范围:-900~900Hz)
long int c_state = 0; // 校准后频率值
long int c_f = 0; // 原始频率计数值
long int c_fmax = 0; // 历史最大频率值
/******************** 数码管位选控制函数 ********************/
void choose(unsigned char n) {
/* 功能:选择数码管位选锁存器
参数:n-锁存器编号(0:关闭所有 4:位锁存 5:段锁存 6:位选 7:段选)*/
switch(n) {
case 4: P2 = (P2 & 0x1F) | 0x80; break; // Y4锁存器(位选择)
case 5: P2 = (P2 & 0x1F) | 0xA0; break; // Y5锁存器(段码)
case 6: P2 = (P2 & 0x1F) | 0xC0; break; // Y6锁存器(位选信号)
case 7: P2 = (P2 & 0x1F) | 0xE0; break; // Y7锁存器(段码信号)
case 0: P2 &= 0x1F; break; // 关闭所有锁存器
}
}
/******************** 延时函数 ********************/
void delay(unsigned int k) { //@11.0592MHz
/* 功能:产生约k毫秒延时
原理:嵌套循环实现粗略延时 */
unsigned char i, j;
_nop_(); _nop_(); _nop_(); // 3个空操作补偿
i = 11; j = 190;
while(k--) {
do { while (--j); } while (--i);
}
}
/******************** DS1302时间转换函数 ********************/
unsigned char s2sl(unsigned char dat) {
/* 功能:十进制转BCD码(用于DS1302写入) */
return (dat/10)*16 + dat%10;
}
unsigned char sl2s(unsigned char dat) {
/* 功能:BCD码转十进制(用于DS1302读取) */
return (dat/16)*10 + dat%16;
}
/******************** DS1302时间读取函数 ********************/
void DS1302_read(void) {
/* 功能:从DS1302读取当前时间 */
Ds1302_Single_Byte_Write(0x8E, 0x00); // 关闭写保护
s = sl2s(Ds1302_Single_Byte_Read(0x81)); // 读取秒寄存器
m = sl2s(Ds1302_Single_Byte_Read(0x83)); // 读取分寄存器
h = sl2s(Ds1302_Single_Byte_Read(0x85)); // 读取时寄存器
Ds1302_Single_Byte_Write(0x8E, 0x80); // 启用写保护
}
/******************** DS1302初始化函数 ********************/
void DS1302_Init(void) {
/* 功能:初始化DS1302时钟芯片(设定初始时间) */
Ds1302_Single_Byte_Write(0x8E, 0x00); // 关闭写保护
Ds1302_Single_Byte_Write(0x80, s2sl(54)); // 设置秒(54秒)
Ds1302_Single_Byte_Write(0x82, s2sl(35)); // 设置分(35分)
Ds1302_Single_Byte_Write(0x84, s2sl(12)); // 设置时(12时)
Ds1302_Single_Byte_Write(0x8E, 0x80); // 启用写保护
}
/******************** 定时器初始化函数 ********************/
void Timer_Init(void) {
/* 功能:配置定时器0和定时器1
T0:模式2,自动重装,用于脉冲计数(P3.4输入)
T1:模式1,50ms定时,用于系统时基 */
TMOD = 0x16; // T0: 模式2, T1: 模式1
TH0 = 0xFF; // T0初值(255)
TL0 = 0xFF; // 外部脉冲从0xFF开始计数
TH1 = (65535-50000)/256; // T1 50ms定时初值
TL1 = (65535-50000)%256;
ET0 = 1; // 允许T0中断
ET1 = 1; // 允许T1中断
TR0 = 1; // 启动T0
TR1 = 1; // 启动T1
EA = 1; // 开启总中断
}
/******************** 定时器0中断服务函数 ********************/
void timer0_serve(void) interrupt 1 using 1 {
/* 功能:T0中断服务函数(脉冲计数) */
c_f++; // 每个下降沿触发计数
}
/******************** 定时器1中断服务函数 ********************/
void timer1_serve(void) interrupt 3 using 1 {
/* 功能:T1中断服务函数(系统时基) */
TH1 = (65535-50000)/256; // 重装50ms初值
TL1 = (65535-50000)%256;
t++;
if(t >= 20) { // 1秒时间到(50ms×20)
t = 0;
c_state = c_f + c_adjust; // 计算校准后频率
c_f = 0; // 重置脉冲计数
// 更新最大频率记录
if(c_state >= c_fmax) {
c_fmax = c_state;
s1 = s; m1 = m; h1 = h; // 记录当前时间
}
}
// LED指示灯控制
choose(4); // 选择Y4锁存器
P0 = 0xFF; // 关闭所有LED
// L1:频率界面0.2秒闪烁
if(state_S4 == 0) L1 = !L1;
else L1 = 1;
// L2:报警逻辑
if(c_state < 0) L2 = 0; // 校准错误(常亮)
else if(c_state > f_check) L2 = !L2; // 超限(闪烁)
else L2 = 1; // 正常状态(熄灭)
choose(0); // 关闭锁存器
}
/******************** 数码管单管显示函数 ********************/
void smg_show(unsigned char Address, unsigned char dat) {
/* 功能:在指定位置显示单个数码管
参数:Address-位选地址(0-7), dat-段码值 */
choose(6); // 选择Y6(位选)
P0 = 0x01 << Address; // 设置位选信号
choose(7); // 选择Y7(段码)
P0 = dat; // 输出段码
choose(0); // 关闭锁存器
delay(1); // 显示保持1ms
}
/******************** 系统初始化函数 ********************/
void sys_Init(void) {
/* 功能:系统外设初始化 */
choose(4); // 选择Y4(LED锁存器)
P0 = 0xFF; // 关闭所有LED
choose(5); // 选择Y5(数码管段码锁存器)
P0 = 0x00; // 关闭数码管显示
Timer_Init(); // 定时器初始化
DS1302_Init(); // DS1302初始化
}
/******************** 数码管主显示函数 ********************/
void smg_display(void) {
/* 功能:根据当前界面状态更新数码管显示 */
switch(state_S4) {
case 0: // 频率界面
smg_show(0, led_nodot[15]); // 显示"F"
if(c_state >= 0) { // 正数显示
// 动态显示5位频率值(高位灭零)
if(c_state > 9999) smg_show(3, led_nodot[c_state/10000]);
if(c_state > 999) smg_show(4, led_nodot[c_state/1000%10]);
if(c_state > 99) smg_show(5, led_nodot[c_state/100%10]);
if(c_state > 9) smg_show(6, led_nodot[c_state/10%10]);
smg_show(7, led_nodot[c_state%10]);
} else { // 负数显示
smg_show(6, led_nodot[16]); // 显示"-"
smg_show(7, led_nodot[18]); // 显示"L"(错误状态)
}
break;
case 1: // 参数界面
smg_show(0, led_nodot[17]); // 显示"P"
if(state_S5 == 1) { // 校准值子界面
smg_show(1, led_nodot[2]); // 显示"2"
if(c_adjust < 0) { // 负校准值
smg_show(4, led_nodot[16]); // "-"
smg_show(5, led_nodot[(-c_adjust)/100]); // 百位
} else if(c_adjust == 0) {
smg_show(7, led_nodot[0]); // 显示"0"
} else { // 正校准值
smg_show(5, led_nodot[c_adjust/100]); // 百位
}
smg_show(6, led_nodot[0]); // 十位固定0
smg_show(7, led_nodot[0]); // 个位固定0
} else { // 超限参数子界面
smg_show(1, led_nodot[1]); // 显示"1"
smg_show(4, led_nodot[f_check/1000]); // 千位
smg_show(5, led_nodot[0]); // 固定0
smg_show(6, led_nodot[0]); // 固定0
smg_show(7, led_nodot[0]); // 固定0
}
break;
case 2: // 时间界面
// 显示格式:HH-MM-SS
smg_show(0, led_nodot[h/10]); // 小时十位
smg_show(1, led_nodot[h%10]); // 小时个位
smg_show(2, led_nodot[16]); // "-"
smg_show(3, led_nodot[m/10]); // 分钟十位
smg_show(4, led_nodot[m%10]); // 分钟个位
smg_show(5, led_nodot[16]); // "-"
smg_show(6, led_nodot[s/10]); // 秒十位
smg_show(7, led_nodot[s%10]); // 秒个位
break;
case 3: // 回显界面
if(state_S5 == 0) { // 频率回显
smg_show(0, led_nodot[19]); // "H"
smg_show(1, led_nodot[15]); // "F"
// 显示最大频率值(高位灭零)
if(c_fmax > 9999) smg_show(3, led_nodot[c_fmax/10000]);
if(c_fmax > 999) smg_show(4, led_nodot[c_fmax/1000%10]);
if(c_fmax > 99) smg_show(5, led_nodot[c_fmax/100%10]);
if(c_fmax > 9) smg_show(6, led_nodot[c_fmax/10%10]);
smg_show(7, led_nodot[c_fmax%10]);
} else { // 时间回显
smg_show(0, led_nodot[19]); // "H"
smg_show(1, led_nodot[10]); // "R"
// 显示最大频率发生时间
smg_show(2, led_nodot[h1/10]);
smg_show(3, led_nodot[h1%10]);
smg_show(4, led_nodot[m1/10]);
smg_show(5, led_nodot[m1%10]);
smg_show(6, led_nodot[s1/10]);
smg_show(7, led_nodot[s1%10]);
}
break;
}
}
/******************** 按键检测函数 ********************/
void key_check(void) {
/* 功能:矩阵键盘扫描与按键处理 */
// S4按键检测(界面切换)
R3 = 1; R4 = 0; // 扫描第一行
K1 = K2 = 1;
if(K1 == 0) {
delay(5); // 消抖
if(K1 == 0) {
state_S4 = (state_S4 + 1) % 4; // 循环切换0-3
state_S5 = 0; // 切回默认子界面
while(!K1) smg_display(); // 等待释放
}
}
// S5按键检测(子界面切换)
R4 = 1; R3 = 0; // 扫描第二行
K1 = K2 = 1;
if(K1 == 0) {
delay(5);
if(K1 == 0) {
state_S5 = (state_S5 + 1) % 2; // 切换0-1
while(!K1) smg_display();
}
}
// S8按键检测(参数增加)
R3 = 1; R4 = 0;
K1 = K2 = 1;
if(K2 == 0) {
delay(5);
if(K2 == 0) {
if(state_S5 == 0) { // 调整超限参数
if(f_check + 1000 <= 9000) f_check += 1000;
} else { // 调整校准值
if(c_adjust + 100 <= 900) c_adjust += 100;
}
while(!K2) smg_display();
}
}
// S9按键检测(参数减少)
R4 = 1; R3 = 0;
K1 = K2 = 1;
if(K2 == 0) {
delay(5);
if(K2 == 0) {
if(state_S5 == 0) { // 调整超限参数
if(f_check - 1000 >= 1000) f_check -= 1000;
} else { // 调整校准值
if(c_adjust - 100 >= -900) c_adjust -= 100;
}
while(!K2) smg_display();
}
}
}
/******************** DAC输出控制函数 ********************/
void PCF8591_out(unsigned char dat) {
/* 功能:通过I2C向PCF8591写入DAC值 */
IIC_Start();
IIC_SendByte(0x90); // 器件地址+写模式
IIC_WaitAck();
IIC_SendByte(0x43); // 控制字:启用DAC输出
IIC_WaitAck();
IIC_SendByte(dat); // DAC输出值
IIC_WaitAck();
IIC_Stop();
}
void PCF8591_check(void) {
/* 功能:根据频率状态更新DAC输出 */
if(c_state < 0) {
PCF8591_out(0); // 错误状态输出0V
} else if(c_state < 500) {
PCF8591_out(1); // 低频率区间
} else if(c_state >= 500 && c_state < f_check) {
// 线性区间:500Hz~f_check对应1~5V
PCF8591_out(1 + 4*(c_state - 500)/(f_check - 500));
} else {
PCF8591_out(5); // 超限输出5V
}
}
/******************** 主函数 ********************/
void main() {
sys_Init(); // 系统初始化
while(1) {
smg_display(); // 刷新数码管
DS1302_read(); // 更新时间数据
key_check(); // 检测按键
PCF8591_check(); // 更新DAC输出
}
}
ds1302.c
#include "ds1302.h"
#include <reg52.h>
/********************************************************************/
/*եؖޚдɫһؖޚ˽ߝ*/
void Write_Ds1302_Byte(unsigned char dat)
{
unsigned char i;
SCK = 0;
for (i=0;i<8;i++)
{
if (dat & 0x01) // ֈݛԚif((addr & 0x01) ==1)
{
SDA_SET; //#define SDA_SET SDA=1 /*֧ƽ׃ٟ*/
}
else
{
SDA_CLR; //#define SDA_CLR SDA=0 /*֧ƽ׃֍*/
}
SCK_SET;
SCK_CLR;
dat = dat >> 1;
}
}
/********************************************************************/
/*եؖޚׁԶһؖޚ˽ߝ*/
unsigned char Read_Ds1302_Byte(void)
{
unsigned char i, dat=0;
for (i=0;i<8;i++)
{
dat = dat >> 1;
if (SDA_R) //ֈݛԚif(SDA_R==1) #define SDA_R SDA /*֧ƽׁȡ*/
{
dat |= 0x80;
}
else
{
dat &= 0x7F;
}
SCK_SET;
SCK_CLR;
}
return dat;
}
/********************************************************************/
/*вDS1302 եؖޚдɫһؖޚ˽ߝ*/
void Ds1302_Single_Byte_Write(unsigned char addr, unsigned char dat)
{
EA=0;
RST_CLR; /*RSTޅ׃֍ìʵЖDS1302քԵʼۯ*/
SCK_CLR; /*SCKޅ׃֍ìʵЖDS1302քԵʼۯ*/
RST_SET; /*Ǵ֯DS1302П,RST=1֧ƽ׃ٟ */
addr = addr & 0xFE;
Write_Ds1302_Byte(addr); /*дɫĿҪַ֘úaddr,ѣ֤ˇдәط,д֮ǰݫخ֍λ׃£*/
Write_Ds1302_Byte(dat); /*дɫ˽ߝúdat*/
RST_CLR; /*ֹͣDS1302П*/
SDA_CLR;
EA=1;
}
/********************************************************************/
/*ՓDS1302եؖޚׁԶһؖޚ˽ߝ*/
unsigned char Ds1302_Single_Byte_Read(unsigned char addr)
{
unsigned char temp;
EA=0;
RST_CLR; /*RSTޅ׃֍ìʵЖDS1302քԵʼۯ*/
SCK_CLR; /*SCKޅ׃֍ìʵЖDS1302քԵʼۯ*/
RST_SET; /*Ǵ֯DS1302П,RST=1֧ƽ׃ٟ */
addr = addr | 0x01;
Write_Ds1302_Byte(addr); /*дɫĿҪַ֘úaddr,ѣ֤ˇׁәط,д֮ǰݫخ֍λ׃ٟ*/
temp=Read_Ds1302_Byte(); /*ՓDS1302אׁԶһٶؖޚք˽ߝ*/
RST_CLR; /*ֹͣDS1302П*/
SDA_CLR;
EA=1;
return temp;
}
ds1302.h
#ifndef __DS1302_H__
#define __DS1302_H__
#include "reg52.h"
#include<intrins.h>
/********************************************************************/
sbit SCK=P1^7;
sbit SD=P2^3;
sbit RST=P1^3;
/********************************************************************/
/*شλޅ*/
#define RST_CLR RST=0 /*֧ƽ׃֍*/
#define RST_SET RST=1 /*֧ƽ׃ٟ*/
/*˫в˽ߝ*/
#define SDA_CLR SD=0 /*֧ƽ׃֍*/
#define SDA_SET SD=1 /*֧ƽ׃ٟ*/
#define SDA_R SD /*֧ƽׁȡ*/
/*ʱדхۅ*/
#define SCK_CLR SCK=0 /*ʱדхۅ*/
#define SCK_SET SCK=1 /*֧ƽ׃ٟ*/
/********************************************************************/
#define ds1302_sec_addr 0x80 //ī˽ߝַ֘
#define ds1302_min_addr 0x82 //ؖ˽ߝַ֘
#define ds1302_hr_addr 0x84 //ʱ˽ߝַ֘
#define ds1302_date_addr 0x86 //ɕ˽ߝַ֘
#define ds1302_month_addr 0x88 //Ղ˽ߝַ֘
#define ds1302_day_addr 0x8A //чǚ˽ߝַ֘
#define ds1302_year_addr 0x8C //Ū˽ߝַ֘
#define ds1302_control_addr 0x8E //дѣۤļ®ؖեԪַ֘
#define ds1302_charger_addr 0x90 //丵肷Ԥ֧ļ®ַؖ֘
#define ds1302_clkburst_addr 0xBE //ɕzbʱדͻעģʽļ®ַؖ֘
/********************************************************************/
/********************************************************************/
/*եؖޚдɫһؖޚ˽ߝ*/
extern void Write_Ds1302_Byte(unsigned char dat);
/********************************************************************/
/*եؖޚׁԶһؖޚ˽ߝ*/
extern unsigned char Read_Ds1302_Byte(void);
/********************************************************************/
/********************************************************************/
/*вDS1302եؖޚдɫһؖޚ˽ߝ*/
extern void Ds1302_Single_Byte_Write(unsigned char addr, unsigned char dat);
/********************************************************************/
/*ՓDS1302եؖޚׁԶһؖޚ˽ߝ*/
extern unsigned char Ds1302_Single_Byte_Read(unsigned char addr);
#endif
/********************************************************************/
// aaaaa END FILE
/********************************************************************/
iic.c
/*
ԌѲ˵ķ: IICПȽ֯ԌѲ
ɭݾ۷: Keil uVision 4.10
Ӳݾ۷: CT107եƬܺ؛ۏʵѵƽ̨(12MHz)
ɕ ǚ: 2011-8-9
*/
#include "iic.h"
//ПǴ֯͵ݾ
void IIC_Start(void)
{
SDA = 1;
SCL = 1;
somenop;
SDA = 0;
somenop;
SCL = 0;
}
//Пֹͣ͵ݾ
void IIC_Stop(void)
{
SDA = 0;
SCL = 1;
somenop;
SDA = 1;
}
//ֈսӦհ
bit IIC_WaitAck(void)
{
SDA = 1;
somenop;
SCL = 1;
somenop;
if(SDA)
{
SCL = 0;
IIC_Stop();
return 0;
}
else
{
SCL = 0;
return 1;
}
}
//ͨڽI2CПע̍˽ߝ
void IIC_SendByte(unsigned char byt)
{
unsigned char i;
for(i=0;i<8;i++)
{
if(byt&0x80)
{
SDA = 1;
}
else
{
SDA = 0;
}
somenop;
SCL = 1;
byt <<= 1;
somenop;
SCL = 0;
}
}
//ՓI2CПʏޓ˕˽ߝ
iic.h
#ifndef _IIC_H
#define _IIC_H
#include "intrins.h"
#include "ds1302.h"
#define somenop {_nop_();_nop_();_nop_();_nop_();_nop_();}
#define SlaveAddrW 0xA0
#define SlaveAddrR 0xA1
//Пӽޅ֨ӥ
sbit SDA = P2^1; /* ˽ߝП */
sbit SCL = P2^0; /* ʱדП */
//گ˽ʹķ
void IIC_Start(void);
void IIC_Stop(void);
//void IIC_Ack(unsigned char ackbit);
void IIC_SendByte(unsigned char byt);
bit IIC_WaitAck(void);
//unsigned char IIC_RecByte(void);
#endif