新模板,当时要是会写就好了
感谢小范同学提供的技术指导。
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口,这样子就完美避免了其余的灯误操作的问题。