2024年第15届蓝桥杯省赛电子类单片机组程序设计题解

新模板,当时要是会写就好了
感谢小范同学提供的技术指导。

1. 程序设计题目





2. main

main.c

#include "main.h"

#define MENU_MAX 6 // 最大界面数量

unsigned char menu = 0; // 当前显示界面
unsigned char display[MENU_MAX][8]; // 当前界面数码管显示值

unsigned long count_ne555 = 0; // NE555计数值
unsigned int count_onesec = 0; // 用于定时一秒的计数值
long frequency = 0; // 测量并校准后的频率
unsigned int fre_over = 2000; // 频率超限值
int fre_calibration = 0; // 频率校准值

long max_frequency = 0; // 回显 最大频率
unsigned char maxfre_time[3] = { 0 }; // 回显 最大频率发生时间

unsigned char led_status = 0xff; // LED状态
unsigned int count_0point2sec = 0; // 用于定时0.2秒的计数值

unsigned char dac_value = 0; // DAC输出值

// 设置138译码器,选择573锁存器
void set138(unsigned char channel){
    P2 = (P2 & 0x1f) | (channel << 5); 
}

// 向P0口写入数据
void write_channel(unsigned char channel, unsigned char dat) {
    set138(channel);
    P0 = dat;
    set138(0);
}

void interrupt_init(){
    EA = 1;
}

void Timer0_Init(){
    AUXR &= 0x7F;            //定时器时钟12T模式
    TMOD &= 0xF0;            //设置定时器模式
    TMOD |= 0x06;            //设置定时器模式 // 计数模式
    TL0 = 0xFF;                //设置定时初始值
    TH0 = 0xFF;                //设置定时重载值
    TF0 = 0;                //清除TF0标志
    TR0 = 1;                //定时器0开始计时
    ET0 = 1;                //使能定时器0中断
}

void Timer2_Init(){            //2毫秒@12.000MHz        
    AUXR &= 0xFB;            //定时器时钟12T模式
    T2L = 0x30;                //设置定时初始值
    T2H = 0xF8;                //设置定时初始值
    AUXR |= 0x10;            //定时器2开始计时
    IE2 |= 0x04;            //使能定时器2中断
}

void scanseg(){
    static unsigned char digit = 0;
    write_channel(6, 1 << digit);
    write_channel(7, display[menu][digit]);
    digit = (digit + 1) % 8;
}

// DS1302初始化
void ds1302_init() {
    Write_Ds1302_Byte(0x8E, 0X00);
    Write_Ds1302_Byte(0x80, 0x05); 
    Write_Ds1302_Byte(0x82, 0x03);
    Write_Ds1302_Byte(0x84, 0x13); // 13-03-05
    Write_Ds1302_Byte(0x8E, 0X80);
}

void scankeys(){
    unsigned char i;
    unsigned char tmpstate[4] = 0;
    ROW3 = 0; ROW4 = 1;
    COL1 = COL2 = 1;
    if(COL1) CLR_TMPSTATE(0); else SET_TMPSTATE(0);
    if(COL2) CLR_TMPSTATE(1); else SET_TMPSTATE(1);
    
    ROW3 = 1; ROW4 = 0;
    COL1 = COL2 = 1;
    if(COL1) CLR_TMPSTATE(2); else SET_TMPSTATE(2);
    if(COL2) CLR_TMPSTATE(3); else SET_TMPSTATE(3);
    
    for(i = 0; i < 4; i++)
    {
        if(tmpstate[i] == 0){
            keycount[i] = 0; // 如果松手,计数值清零
        } else {
            keycount[i] += (keycount[i] >= MAXCOUNT) ? 0 : 1; // 如果达到最大计数值,+0,否则+1
        }
        if(keycount[i] >= MAXCOUNT) {
            SET_KEYSTATE(i);
        } else {
            CLR_KEYSTATE(i);
        }
    }
}

// 判断上升沿(让按键第一次按下有效) | 界面跳转
void risingedge() {
    static unsigned char pristate[4] = { 0 }; // 先前的状态,先前为0当前为1才触发,并只触发一次

    if(keystate[2] == 1 && pristate[2] == 0 && menu != 4 && menu != 5) 
        menu = (menu + 1) % 4;
    else if(keystate[2] == 1 && pristate[2] == 0 && menu == 4)
        menu = 2;
    else if(keystate[2] == 1 && pristate[2] == 0 && menu == 5)
        menu = 0;
    pristate[2] = keystate[2]; // 写入状态,防止多次触发
    
    if(keystate[0] == 1 && pristate[0] == 0 && menu == 1)
        menu = 4;
    else if(keystate[0] == 1 && pristate[0] == 0 && menu == 4)
        menu = 1;
    else if(keystate[0] == 1 && pristate[0] == 0 && menu == 3)
        menu = 5;
    else if(keystate[0] == 1 && pristate[0] == 0 && menu == 5)
        menu = 3;
    pristate[0] = keystate[0];
    
    if(keystate[1] == 1 && pristate[1] == 0 && menu == 1)
    {
        if(fre_over > 1000)
            fre_over -= 1000;
    }
    else if(keystate[1] == 1 && pristate[1] == 0 && menu == 4)
    {
        if(fre_calibration > -900)
            fre_calibration -=100;
    }
    pristate[1] = keystate[1];
        
    if(keystate[3] == 1 && pristate[3] == 0 && menu == 1)
    {
        if(fre_over < 9000)
            fre_over += 1000;
    }
    else if(keystate[3] == 1 && pristate[3] == 0 && menu == 4)
    {
        if(fre_calibration < 900)
            fre_calibration +=100;
    }    
    pristate[3] = keystate[3];
}

void Timer0_Isr() interrupt 1{
    count_ne555++;
}

void Timer2_Isr() interrupt 12{
    scanseg();
    
    scankeys();
    risingedge();
    
    count_onesec++;
    if(count_onesec == 500){
        frequency = count_ne555;
        frequency += fre_calibration;
        if(frequency > max_frequency){
            max_frequency = frequency;
            maxfre_time[0] = Read_Ds1302_Byte(0x85);
            maxfre_time[1] = Read_Ds1302_Byte(0x83);
            maxfre_time[2] = Read_Ds1302_Byte(0x81);
        }
        count_ne555 = 0;
        count_onesec = 0;
    }
    
    count_0point2sec++;
    if (count_0point2sec == 200){
        count_0point2sec = 0;
        if(menu == 0)
            led_status ^= 0x01; // 翻转L1状态
        if(frequency > fre_over)
            led_status ^= 0x02; // 翻转L2状态
    }
    LED();
}

void menu_init(){
    
    // 频率界面F 0
    display[0][0] = Seg_Table[15];
    display[0][1] = SEGOFF;
    display[0][2] = SEGOFF;
    if(frequency < 0){
        display[0][3] = SEGOFF;
        display[0][4] = SEGOFF;
        display[0][5] = SEGOFF;
        display[0][6] = 0xC7; // L
        display[0][7] = 0xC7;
    }
    else if(frequency > 0){
        display[0][3] = frequency >= 10000 ? Seg_Table[frequency / 10000 % 10] : SEGOFF;
        display[0][4] = frequency >= 1000 ? Seg_Table[frequency / 1000 % 10] : SEGOFF;
        display[0][5] = frequency >= 100 ? Seg_Table[frequency / 100 % 10] : SEGOFF;
        display[0][6] = frequency >= 10 ? Seg_Table[frequency / 10 % 10] : SEGOFF;
        display[0][7] = frequency >= 1 ? Seg_Table[frequency / 1 % 10] : SEGOFF;
    }
    else if(frequency == 0){
        display[0][3] = SEGOFF;
        display[0][4] = SEGOFF;
        display[0][5] = SEGOFF;
        display[0][6] = SEGOFF;
        display[0][7] = Seg_Table[0];
    }
    
    // 超限参数P1 1
    display[1][0] = 0x8C; // P
    display[1][1] = Seg_Table[1];
    display[1][2] = SEGOFF;
    display[1][3] = SEGOFF;
    display[1][4] = fre_over >= 1000 ? Seg_Table[fre_over / 1000 % 10] : SEGOFF;
    display[1][5] = fre_over >= 100 ? Seg_Table[fre_over / 100 % 10] : SEGOFF;
    display[1][6] = fre_over >= 10 ? Seg_Table[fre_over / 10 % 10] : SEGOFF;
    display[1][7] = fre_over >= 1 ? Seg_Table[fre_over / 1 % 10] : SEGOFF;
    
    // 校准值P2 4
    display[4][0] = 0x8C; // P
    display[4][1] = Seg_Table[2];
    display[4][2] = SEGOFF;
    display[4][3] = SEGOFF;
    if(fre_calibration > 0){
        display[4][4] = SEGOFF;
        display[4][5] = fre_calibration >= 100 ? Seg_Table[fre_calibration / 100 % 10] : SEGOFF;
        display[4][6] = fre_calibration >= 10 ? Seg_Table[fre_calibration / 10 % 10] : SEGOFF;
        display[4][7] = fre_calibration >= 1 ? Seg_Table[fre_calibration / 1 % 10] : SEGOFF;
    }
    if(fre_calibration < 0){
        fre_calibration = 0 - fre_calibration;
        display[4][4] = 0xBF; // -
        display[4][5] = fre_calibration >= 100 ? Seg_Table[fre_calibration / 100 % 10] : SEGOFF;
        display[4][6] = fre_calibration >= 10 ? Seg_Table[fre_calibration / 10 % 10] : SEGOFF;
        display[4][7] = fre_calibration >= 1 ? Seg_Table[fre_calibration / 1 % 10] : SEGOFF;
        fre_calibration = 0 - fre_calibration;
    }
    if(fre_calibration == 0){
        display[4][4] = display[4][5] = display[4][6] = SEGOFF;
        display[4][7] = Seg_Table[0];
    }
    
    // 时间 2
    display[2][0] = Seg_Table[(Read_Ds1302_Byte(0x85) / 16)];
    display[2][1] = Seg_Table[(Read_Ds1302_Byte(0x85) & 0x0F)];
    display[2][2] = 0xBF; // -
    display[2][3] = Seg_Table[(Read_Ds1302_Byte(0x83) / 16)];
    display[2][4] = Seg_Table[(Read_Ds1302_Byte(0x83) & 0x0F)];
    display[2][5] = 0xBF; // -
    display[2][6] = Seg_Table[(Read_Ds1302_Byte(0x81) / 16)];
    display[2][7] = Seg_Table[(Read_Ds1302_Byte(0x81) & 0x0F)];
    
    // 最大频率界面HF 3
    display[3][0] = 0x89;
    display[3][1] = Seg_Table[15];
    display[3][2] = SEGOFF;
    if(frequency < 0){
        display[3][3] = SEGOFF;
        display[3][4] = SEGOFF;
        display[3][5] = SEGOFF;
        display[3][6] = 0xC7; // L
        display[3][7] = 0xC7;
    }
    else if(frequency > 0){
        display[3][3] = max_frequency >= 10000 ? Seg_Table[max_frequency / 10000 % 10] : SEGOFF;
        display[3][4] = max_frequency >= 1000 ? Seg_Table[max_frequency / 1000 % 10] : SEGOFF;
        display[3][5] = max_frequency >= 100 ? Seg_Table[max_frequency / 100 % 10] : SEGOFF;
        display[3][6] = max_frequency >= 10 ? Seg_Table[max_frequency / 10 % 10] : SEGOFF;
        display[3][7] = max_frequency >= 1 ? Seg_Table[max_frequency / 1 % 10] : SEGOFF;
    }
    else if(frequency == 0){
        display[3][3] = SEGOFF;
        display[3][4] = SEGOFF;
        display[3][5] = SEGOFF;
        display[3][6] = SEGOFF;
        display[3][7] = Seg_Table[0];
    }
    
    // 最大频率时间 5
    display[5][0] = Seg_Table[maxfre_time[0]/ 16];
    display[5][1] = Seg_Table[maxfre_time[0]& 0x0F];
    display[5][2] = 0xBF; // -
    display[5][3] = Seg_Table[maxfre_time[1] / 16];
    display[5][4] = Seg_Table[maxfre_time[1] & 0x0F];
    display[5][5] = 0xBF; // -
    display[5][6] = Seg_Table[maxfre_time[2] / 16];
    display[5][7] = Seg_Table[maxfre_time[2] & 0x0F];
}

void LED() {
    write_channel(4, led_status);
    if(menu != 0) // 退出频率界面,L1灭
        led_status |= 0x01;
    if(frequency <= fre_over && frequency >= 0) // 低于超限值,且频率大于零,L2灭
        led_status |= 0x02;
    if(frequency < 0) // 频率为负数,L2常量
        led_status &= ~0x02;

}

void dac(){
    if(frequency < 0)
        dac_value = 0;
    else if(frequency >= 0 && frequency <= 500)
        dac_value = 51; // 1V
    else if(frequency > 500 && frequency < fre_over)
        dac_value = frequency * 255 / fre_over;
    else if(frequency >= fre_over)
        dac_value = 255; // 5V
    PCF8591_DAC(dac_value);
}

void main(){
    write_channel(4, 0xFF); // 关LED
    write_channel(5, 0x00); // 关蜂鸣器,继电器
    menu_init();
    interrupt_init();
    Timer2_Init();
    Timer0_Init();
    ds1302_init();
    while(1){
        menu_init();
        dac(); 
    }
}

main.h

#ifndef _MAIN_H
#define _MAIN_H

#include <STC15F2K60S2.H>
#include "ds1302.h"
#include "iic.h"

#define SEGOFF 0xFF

#define ROW3 P32
#define ROW4 P33
#define COL1 P44
#define COL2 P42

#define SET_TMPSTATE(i) (tmpstate[i] = 1)
#define CLR_TMPSTATE(i) (tmpstate[i] = 0)

#define SET_KEYSTATE(i) (keystate[i] = 1)  
#define CLR_KEYSTATE(i) (keystate[i] = 0) 

#define SET_FINSTATE(i) (finstate[i] = 1) 
#define CLR_FINSTATE(i) (finstate[i] = 0)  

#define MAXCOUNT 50    

unsigned char keystate[4] = 0;
unsigned char keycount[4] = { 0 };

unsigned char code Seg_Table[16] = { 
    0xc0,  
    0xf9,  
    0xa4,  
    0xb0,  
    0x99,  
    0x92,  
    0x82,  
    0xf8,  
    0x80,  
    0x90,  
    0x88,  
    0x83,  
    0xc6,  
    0xa1,  
    0x86,  
    0x8e // F 15 
}; 

void set138(unsigned char channel);
void write_channel(unsigned char channel, unsigned char dat);
void interrupt_init();
void Timer0_Init();
void Timer2_Init();
void scanseg();
void ds1302_init();
void menu_init();
void scankeys();
void risingedge();
void LED();
void dac();

#endif

3. DS1302

ds1302.c

#include <STC15F2K60S2.H>
#include <intrins.h>

#define SCK P17
#define SDA P23
#define RST P13                                            

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);            
}

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 Write_Ds1302(unsigned char temp);
void Write_Ds1302_Byte(unsigned char address,unsigned char dat);
unsigned char Read_Ds1302_Byte (unsigned char address );

#endif

4. PFC8591(I2C)

iic.c

#include <STC15F2K60S2.H>
#include "intrins.h"

#define DELAY_TIME    10

#define sda P21
#define scl P20

static void I2C_Delay(unsigned char n)
{
    do
    {
        _nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();        
    }
    while(n--);          
}

void I2C_Start(void)
{
    sda = 1;
    scl = 1;
    I2C_Delay(DELAY_TIME);
    sda = 0;
    I2C_Delay(DELAY_TIME);
    scl = 0;    
}

void I2C_Stop(void)
{
    sda = 0;
    scl = 1;
    I2C_Delay(DELAY_TIME);
    sda = 1;
    I2C_Delay(DELAY_TIME);
}

void I2C_SendByte(unsigned char byt)
{
    unsigned char i;
    
    for(i=0; i<8; i++){
        scl = 0;
        I2C_Delay(DELAY_TIME);
        if(byt & 0x80){
            sda = 1;
        }
        else{
            sda = 0;
        }
        I2C_Delay(DELAY_TIME);
        scl = 1;
        byt <<= 1;
        I2C_Delay(DELAY_TIME);
    }
    
    scl = 0;  
}

unsigned char I2C_WaitAck(void)
{
    unsigned char ackbit;
    
    scl = 1;
    I2C_Delay(DELAY_TIME);
    ackbit = sda; 
    scl = 0;
    I2C_Delay(DELAY_TIME);
    
    return ackbit;
}

void PCF8591_DAC(unsigned char dat)
{
    I2C_Start();                    
    I2C_SendByte(0x90);        
    I2C_WaitAck();    
    I2C_SendByte(0x43);     
    I2C_WaitAck();                                 
    I2C_SendByte(dat);         
    I2C_WaitAck();                                         
    I2C_Stop();
}

iic.h

#ifndef _IIC_H
#define _IIC_H

static void I2C_Delay(unsigned char n);
void I2C_Start(void);
void I2C_Stop(void);
void I2C_SendByte(unsigned char byt);
unsigned char I2C_WaitAck(void);

void PCF8591_DAC(unsigned char dat);

#endif

5. 一些想法

  做完去年的题,三个收获,一是二维数组界面,二是定时器消抖,三是用一个char表示LED状态。我之前切换界面是用的一维数组,这样子写太分散了而且不方便切换(上次就栽在这),而二维数组就很好的解决了这个问题,直接改变menu的值即可。定时器消抖更是好用,之前消抖是延时几毫秒,看看按键是不是仍然按下,可我定时器扫描按键才2ms,这显然不现实。用一个char字节表示八位LED状态然后直接写P0口,这样子就完美避免了其余的灯误操作的问题。

添加新评论