第十二届蓝桥杯单片机组程序题第一批

题目 :

蓝桥杯大赛历届真题 - 第十二届蓝桥杯单片机程序设计题 - 蓝桥云课 (lanqiao.cn)

        好几个月前写的了,很多东西不记得,整体来说这一套很简单。

        按键动作不影响其它操作是非常常见的要求,这就要求按键通过定时器中断扫描。

        LED看似简单,但是要各种亮灭不影响其它灯,实现起来很多人反而不会,这里使用结构体和共用体来实现。可以同时实现对位的控制和整个端口的控制,且不同位相互不受影响。

        i2c和单总线部分使用官方的驱动程序。

代码部分
main.h 
#ifndef _MAIN_H_
#define _MAIN_H_
#include <STC15F2K60S2.H>
unsigned char key_read();
unsigned char key_loop();
void P2andP0(unsigned char p2,p0);
void smg_set(unsigned char position,words,points);
void smg_loop();
float ds18b20_read();
typedef struct
{
	unsigned char b0:1;
	unsigned char b1:1;
	unsigned char b2:1;
	unsigned char b3:1;
	unsigned char b4:1;
	unsigned char b5:1;
	unsigned char b6:1;
	unsigned char b7:1;
}bits;
typedef union
{
	bits b;
	unsigned char hex;
}hextobin;
hextobin led,buzz;
void dac(float datas);
#endif
main.c
#include <main.H>

// 定义全局变量
unsigned char mode = 0, mode1 = 0, tflag = 0, dacflag = 0; // 各种标志位
unsigned int heat; // 存储温度值,单位为0.01°C
char para = 20; // 设定值
float t = 0, voltage; // 当前温度和输出电压

// 定时器0中断服务程序
void Timer0_Isr(void) interrupt 1 {
    static unsigned int T0 = 0, T1 = 0, T2 = 0, T3 = 0; // 四个计数器

    // 每次中断时计数器递增
    T0++; T1++; T2++; T3++;

    // 每1ms触发一次,用于更新显示
    if (T0 > 1) {
        T0 = 0;
        smg_loop(); // 更新数码管显示
    }

    // 每20ms触发一次,用于读取按键
    if (T1 > 20) {
        T1 = 0;
        key_loop(); // 读取按键
    }

    // 每900ms触发一次,用于读取温度
    if (T2 > 900) {
        T2 = 0;
        tflag = 1; // 标记温度读取完成
    }

    // 每400ms触发一次,用于更新DAC输出
    if (T3 > 400) {
        dacflag = 1; // 标记DAC需要更新
        T3 = 0;
    }
}

// 定时器0初始化
void Timer0_Init(void) { // 1毫秒@12.000MHz
    AUXR |= 0x80; // 定时器时钟1T模式
    TMOD &= 0xF0; // 设置定时器模式
    TL0 = 0x20; // 设置定时初始值
    TH0 = 0xD1; // 设置定时初始值
    TF0 = 0; // 清除TF0标志
    TR0 = 1; // 开始计时
    ET0 = 1; // 使能定时器0中断
    EA = 1; // 开启总中断
}

// 显示更新函数
void view() {
    switch (mode) {
        case 0: // 显示温度
            smg_set(0, 12, 0); // 设置第一位显示内容
            smg_set(1, 17, 0); // 设置第二位显示内容
            smg_set(2, 17, 0); // 设置第三位显示内容
            smg_set(3, 17, 0); // 设置第四位显示内容
            smg_set(4, heat / 1000 % 10, 0); // 设置第五位显示内容
            smg_set(5, heat / 100 % 10 + 20, 0); // 设置第六位显示内容
            smg_set(6, heat / 10 % 10, 0); // 设置第七位显示内容
            smg_set(7, heat / 1 % 10, 0); // 设置第八位显示内容
            break;

        case 1: // 显示设定值
            smg_set(0, 19, 0); // 设置第一位显示内容
            smg_set(1, 17, 0); // 设置第二位显示内容
            smg_set(2, 17, 0); // 设置第三位显示内容
            smg_set(3, 17, 0); // 设置第四位显示内容
            smg_set(4, 17, 0); // 设置第五位显示内容
            smg_set(5, 17, 0); // 设置第六位显示内容
            smg_set(6, para / 10, 0); // 设置第七位显示内容
            smg_set(7, para % 10, 0); // 设置第八位显示内容
            break;

        case 2: // 显示电压
            smg_set(0, 10, 0); // 设置第一位显示内容
            smg_set(1, 17, 0); // 设置第二位显示内容
            smg_set(2, 17, 0); // 设置第三位显示内容
            smg_set(3, 17, 0); // 设置第四位显示内容
            smg_set(4, 17, 0); // 设置第五位显示内容
            smg_set(5, (unsigned int)(voltage * 100) / 100 % 10, 0); // 设置第六位显示内容
            smg_set(6, (unsigned int)(voltage * 100) / 10 % 10, 0); // 设置第七位显示内容
            smg_set(7, (unsigned int)(voltage * 100) % 10, 0); // 设置第八位显示内容
            break;
    }   
}

// 数模转换控制函数
void pcf8591_dac() {
    if (mode1 == 0) {
        if (t < para) {
            voltage = 0; // 如果温度低于设定值,则输出0V
            dac(0); // 设置DAC输出为0
        } else {
            dac(5); // 如果温度高于设定值,则输出5V
            voltage = 5; // 设置电压值为5V
        }
    } else if (mode1 == 1) {
        if (t < 20) {
            voltage = 1.0; // 如果温度低于20°C,则输出1V
            dac(1.0); // 设置DAC输出为1V
        } else if (t > 40) {
            dac(4.0); // 如果温度高于40°C,则输出4V
            voltage = 4.0; // 设置电压值为4V
        } else {
            dac(0.15 * t - 2); // 在20°C到40°C之间,线性调整输出电压
            voltage = 0.15 * t - 2; // 设置电压值
        }
    }
}

// 主函数
void main() {
    unsigned char value = 0, v = 0;

    // 初始化定时器0
    Timer0_Init();

    // 初始化LED和P2及P0端口
    led.hex = 0xFF;
    P2andP0(0xA0, 0);

    // 设置LED的初始状态
    led.b.b1 = 0;
    led.b.b0 = 0;

    // 更新P2和P0端口
    P2andP0(0x80, led.hex);

    // 主循环
    while (1) {
        // 读取按键值
        value = key_read();

        // 按键值为4时改变显示模式
        if (value == 4) {
            mode++;
            mode = mode % 3; // 确保模式在0-2之间

            // 根据模式更新LED状态
            if (mode == 0) {
                led.b.b1 = 0;
                led.b.b2 = 1;
                led.b.b3 = 1;
                P2andP0(0x80, led.hex);
            } else if (mode == 1) {
                led.b.b1 = 1;
                led.b.b2 = 0;
                led.b.b3 = 1;
                P2andP0(0x80, led.hex);
            } else if (mode == 2) {
                led.b.b1 = 1;
                led.b.b2 = 1;
                led.b.b3 = 0;
                P2andP0(0x80, led.hex);
            }
        }

        // 按键值为5时改变DAC模式
        if (value == 5) {
            mode1++;
            mode1 = mode1 % 2; // 确保模式在0-1之间

            // 根据DAC模式更新LED状态
            if (mode1 == 0) {
                led.b.b0 = 0;
                P2andP0(0x80, led.hex);
            } else {
                led.b.b0 = 1;
                P2andP0(0x80, led.hex);
            }
        }

        // 按键值为8时减小设定值
        if (value == 8 && mode == 1) {
            para--;
            if (para < 0) para = 0; // 确保设定值不低于0
        }

        // 按键值为9时增加设定值
        if (value == 9 && mode == 1) {
            para++;
            if (para > 99) para = 99; // 确保设定值不高于99
        }

        // 更新显示
        view();

        // 如果温度读取完成,更新温度值
        if (tflag == 1) {
            t = ds18b20_read(); // 读取温度
            heat = (unsigned int)(t * 100); // 转换温度值为整数
            tflag = 0; // 清除标志位
        }

        // 如果需要更新DAC输出,调用相应的函数
        if (dacflag == 1) {
            pcf8591_dac(); // 更新DAC输出
            dacflag = 0; // 清除标志位
        }
    }
}
key.c
#include <STC15F2K60S2.H>
unsigned char key_get()
{
	unsigned char keynum=0;
	P44=0;P42=1;
	if(P33==0)keynum=4;
	if(P32==0)keynum=5;
	P44=1;P42=0;
	if(P33==0)keynum=8;
	if(P32==0)keynum=9;
	return keynum;
}

unsigned char key_loop()
{
	static unsigned char now=0,last=0;
	unsigned char keynum=0;
	last=now;
	now=key_get();
	if(last==0&&now==4)keynum=4;
	if(last==0&&now==5)keynum=5;
	if(last==0&&now==8)keynum=8;
	if(last==0&&now==9)keynum=9;
	return keynum;
}

unsigned char key_read()
{
	unsigned char temp=0;
	temp=key_loop();
	return temp;
}
smg.c
#include <STC15F2K60S2.H>
void P2andP0(unsigned char p2,p0);
code unsigned char Seg_Table[] = 
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
0x88, //A  10
0x83, //b  11
0xc6, //C  12
0xa1, //d  13
0x86, //E  14
0x8e, //F   15
0xBF,  //-  16
0xFF,	//none 17
0xC1, //U   18
0x8C,//P  19 1000 1100
0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10,0x08,0x03,0x46,0x21,0x06,0x0e
};
unsigned char word[8]={17,17,17,17,17,17,17,17},point=0;
void smg_set(unsigned char position,words,points)
{
	word[position]=words;
	point=points;
}
void smg_loop()
{
	static unsigned char i=0;
	P2andP0(0xC0,0x01<<i);
//	if(point==1)P2andP0(0xE0,word[i]!=17?Seg_Table[word[i]]&0x7F:Seg_Table[word[i]]);
//	else 
		P2andP0(0xE0,Seg_Table[word[i]]);
	word[i]=17;
	i++;
	if(i>7)i=0;
}
P2andP0.c
#include <STC15F2K60S2.H>
void P2andP0(unsigned char p2,p0)
{
	P0=p0;
	P2=P2&0x1F;
	P2=P2|p2;
	P2=P2&0x1F;
}
ds18b20.c
void Write_DS18B20(unsigned char dat);
unsigned char Read_DS18B20(void);
bit init_ds18b20(void);
#define skip 0xCC
#define read 0xBE
#define write 0x44
float ds18b20_read()
{
	unsigned char i=0;
	int t0,tl,th;
	float t=0;
	i=init_ds18b20();
	Write_DS18B20(skip);
	Write_DS18B20(write);
	i=init_ds18b20();
	Write_DS18B20(skip);
	Write_DS18B20(read);
	tl=Read_DS18B20();
	th=Read_DS18B20();
	t0=(th<<8)|tl;
	t=t0/16.0;
	return t;
}
pcf8591.c
void I2CStart(void);
void I2CStop(void);
void I2CSendByte(unsigned char byt);
unsigned char I2CReceiveByte(void);
unsigned char I2CWaitAck(void);
void I2CSendAck(unsigned char ackbit);
void dac(float datas)
{
	unsigned char i;
	I2CStart();
	I2CSendByte(0x90);
	i=I2CWaitAck();
	I2CSendByte(0x40);
	i=I2CWaitAck();
	I2CSendByte(datas);
	i=I2CWaitAck();
	I2CStop();
}
解释部分
主要功能:


1. 定时器0中断服务程序 (Timer0_Isr): 这个函数处理定时器0的中断,用于周期性地执行不同的任务。
2. 定时器0初始化 (Timer0_Init): 设置定时器0的配置参数以实现1毫秒的定时。
3. 显示更新 (view): 根据当前模式更新显示的内容,显示可以是温度、设定值或电压。
4. 数模转换控制 (pcf8591_dac): 根据不同的模式设置DAC输出电压。
5. 主函数 (main): 控制程序的流程,读取按键输入并相应地更新显示和DAC输出。

 关键部分解析:

 定时器0中断服务程序
 使用四个递增的计数器 T0, T1, T2, T3 来调度不同频率的任务。
 T0 每1ms触发一次,用于调用 smg_loop() 更新显示。
 T1 每20ms触发一次,用于调用 key_loop() 读取按键状态。
 T2 每900ms触发一次,用于调用 ds18b20_read() 获取温度值。
 T3 每400ms触发一次,用于调用 pcf8591_dac() 更新DAC输出。

 显示更新
 根据 mode 变量的不同值,更新显示不同的内容。
   mode == 0: 显示温度。
   mode == 1: 显示设定值 para。
   mode == 2: 显示电压 voltage。

 数模转换控制
 根据 mode1 的值,选择不同的DAC输出策略。
   mode1 == 0: 如果温度低于设定值,则输出0V;否则输出5V。
   mode1 == 1: 根据温度线性调节输出电压,范围为1V至4V之间。

 主函数
 初始化定时器0。
 无限循环中读取按键,并根据按键值改变模式、调整设定值或更新显示。
 调用 view() 和条件判断来更新显示。
 调用 ds18b20_read() 和 pcf8591_dac() 来更新温度值和DAC输出。

 其他函数
 按键读取 (key_read): 用于检测是否有按键按下,并返回相应的按键值。
 温度读取 (ds18b20_read): 读取DS18B20温度传感器的温度值。
 数模转换 (dac): 向PCF8591 DAC发送数据。

各函数和变量解释

 全局变量
 mode: 一个无符号字符型变量,表示当前显示模式,取值范围为0到2。
 mode1: 一个无符号字符型变量,表示DAC工作模式,取值范围为0到1。
 tflag: 一个无符号字符型变量,作为标志位,当温度读取完成时被置为1。
 dacflag: 一个无符号字符型变量,作为标志位,当需要更新DAC输出时被置为1。
 heat: 一个无符号整型变量,用于存储温度值(单位为0.01°C)。
 para: 一个字符型变量,用于存储设定值,范围为0到99。
 t: 一个浮点型变量,用于存储当前温度(单位为°C)。
 voltage: 一个浮点型变量,用于存储输出电压值。

 函数
 Timer0_Isr(): 定时器0的中断服务程序。该函数中包含了四个计数器,用于周期性地执行不同的任务:
   T0: 每1毫秒递增,达到1时触发 smg_loop() 更新显示。
   T1: 每20毫秒递增,达到20时触发 key_loop() 读取按键。
   T2: 每900毫秒递增,达到900时设置 tflag 为1,用于触发温度读取。
   T3: 每400毫秒递增,达到400时设置 dacflag 为1,用于触发DAC更新。
 Timer0_Init(): 初始化定时器0,设置定时器0为1毫秒定时。
 view(): 根据 mode 变量的值更新数码管显示内容。
 pcf8591_dac(): 根据 mode1 和当前温度 t 控制DAC输出电压。
 main(): 主函数,负责程序初始化和主循环,包括初始化定时器0、读取按键、更新显示和控制DAC输出。
 key_read(): 读取按键状态,返回对应的按键值。
 key_loop(): 连续读取按键状态,过滤按键抖动。
 P2andP0(): 设置P2和P0端口的值以控制数码管。
 smg_set(): 设置数码管显示内容的位置和数值。
 smg_loop(): 循环更新数码管的显示。
 ds18b20_read(): 读取DS18B20温度传感器的温度值。
 dac(): 通过I2C接口向DAC写入数据,控制输出电压。

 辅助定义
 Seg_Table[]: 数组,用于存储数码管显示的段码。
 word[8]: 数组,用于存储数码管各位置的显示内容。
 point: 变量,用于存储数码管小数点的状态。
 Write_DS18B20(): 写入DS18B20的命令字。
 Read_DS18B20(): 从DS18B20读取数据。
 init_ds18b20(): 初始化DS18B20。
 I2CStart(), I2CStop(), I2CSendByte(), I2CReceiveByte(), I2CWaitAck(), I2CSendAck(): I2C通信相关的函数。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值