第十五届蓝桥杯嵌入式组代码

比赛难度不大,主要在以下几个细节容易出错

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值