蓝桥杯单片机模板(自用)——基于柳离风模板
文章目录
一、工程结构
二、BSP文件夹
1、sys
存放系统初始化函数和公用函数、定义等,包括hc573锁存器操作、上电关闭蜂鸣器继电器函数、延时函数、顶层头文件和宏定义等。
sys.c
#include "sys.h"/*** @brief 实现led,继电器蜂鸣器,数码管通道的选择* * @param channel 4:led,5:继电器蜂鸣器,6:位选,7:段选*/
void hc573(uchar channel)
{switch(channel){case 4: P2 = P2 & 0x1f | 0x80;break;case 5: P2 = P2 & 0x1f | 0xa0;break;case 6: P2 = P2 & 0x1f | 0xc0;break;case 7: P2 = P2 & 0x1f | 0xe0;break;}P2 = P2 & 0x1f;
}/**
* @brief 系统初始化,关闭led、蜂鸣器继电器,交换位置解决数码管上电闪烁的问题* */
void sys_init(void)
{P0 = 0xff;hc573(4);P0 = 0x00;hc573(5);}/*** @brief ms级延时* * @param xms 延时ms数*/
void delay_ms(uint xms) //@12.000MHz
{unsigned char data i, j;while(xms--){i = 12;j = 169;do{while (--j);} while (--i);}}/*** @brief us级延时* * @param xus 延时us数*/
void delay_us(uint xus) //@12.000MHz
{while(xus--){_nop_();_nop_();_nop_();_nop_();}
}
sys.h
#ifndef __SYS_H
#define __SYS_H#include <STC15F2K60S2.H>
#include "intrins.h"
#include "stdio.h"#define uchar unsigned char
#define uint unsigned intvoid hc573(uchar channel);
void sys_init(void);
void delay_ms(uint xms);
void delay_us(uint xus);#endif
2、display
存放显示驱动函数,包括LED,数码管,继电器,蜂鸣器等外设的控制。
display.c
#include "display.h"// 共阳数码管段码表
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, //A10
0x83, //b11
0xc6, //C12
0xa1, //d13
0x86, //E14
0x8e, //F15
0x89, //H16
0x8c, //P17
0xc7, //L18
0xbf, //-19
0xff //灭20
};/*** @brief led显示* * @param LED led数组首地址* @param pos 数组索引*/
void led(uchar * LED,uchar pos)
{static uchar temp = 0xff;if(LED[pos])temp &= ~(0x01 << pos);else temp |= 0x01 << pos;P0 = temp;hc573(4);
}/*** @brief 数码管显示* * @param SMG 数码管数组首地址* @param dot 小数点数组首地址* @param pos 数组索引*/
void smg(uchar * SMG,uchar * dot,uchar pos)
{P0 = 0xff; //消隐hc573(7);P0 = 0x01 << pos; //位选hc573(6);if(dot[pos]) //段选P0 = Seg_Table[SMG[pos]] & 0x7f;elseP0 = Seg_Table[SMG[pos]];hc573(7);
}/*** @brief 继电器控制* * @param state_relay 0关1开*/
void relay(uchar state_relay)
{uchar temp;if(state_relay)temp |= 0x10;elsetemp &= ~0x10;P0 = temp;hc573(5);
}/*** @brief 蜂鸣器控制* * @param state_beep 0关1开*/
void beep(uchar state_beep)
{uchar temp;if(state_beep)temp |= 0x40;elsetemp &= ~0x40;P0 = temp;hc573(5);
}
display.h
#ifndef __DISPLAY_H
#define __DISPLAY_H#include "sys.h"void led(uchar * LED,uchar pos);
void smg(uchar * temp,uchar * dot,uchar pos);
void relay(uchar state_relay);
void beep(uchar state_beep);#endif
3、key
存放按键扫描函数,包括独立按键和矩阵键盘,这里返回值为按键在开发板上的元件位号,使用时注意和串口,NE555冲突的引脚。
key.c
#include "key.h"/*** @brief 独立按键扫描函数* * @return uchar 键值对应的元件位号*/
uchar key_scan1(void)
{uchar key = 0; //按键值static bit key_up = 1; //按键松开标志,1松开0按下if((key_up == 1) && (P30 == 0 || P31 == 0 || P32 == 0 || P33 == 0)) //与后面的内容可以省略,因为里面又判断了一次{key_up = 0;if(P30 == 0) key = 7;else if(P31 == 0) key = 6;else if(P32 == 0) key = 5;else if(P33 == 0) key = 4;return key;}else if(P30 == 1 && P31 == 1 && P32 == 1 && P33 == 1)key_up = 1;return 0;
}/*** @brief 矩阵键盘扫描函数* * @return uchar 键值对应的元件位号*/
uchar key_scan2(void)
{uchar key = 0; //按键值static bit key_up = 1; //按键松开标志,1松开0按下P44 = P42 = 1; //列扫描P32 = P33 = 0;if((key_up == 1)&&(P44 == 0 || P42 == 0)) //与后面的内容可以省略,因为里面又判断了一次{if(P44 == 0) key = 7;else if(P42 == 0) key = 11;else return 0;key_up = 0; //标志按下P44 = P42 = 0;P32 = P33 = 1; //行扫描if(P32 == 0) key = key - 2;else if(P33 == 0) key = key - 3;return key;}else if(P44 == 1 && P42 == 1)key_up = 1; //松开return 0;
}
key.h
#ifndef __KEY_H
#define __KEY_H#include "sys.h"uchar key_scan1(void);
uchar key_scan2(void);#endif
4、timer
存放定时器初始化和中断函数,包括定时器0测NE555频率、定时器1中断1ms任务调度,使用时注意与main.c的全局变量声明。
timer.c
#include "timer.h"unsigned long systick_ms; //滴答定时器
uchar key_dly; //按键任务调度
uint display_dly; //显示任务调度
uint collect_dly; //采集任务调度
uchar pos; //数码管led扫描位置
uint count_1s; //1s计时测频率
uint fre; //频率
bit state_relay; //继电器状态
bit state_beep; //蜂鸣器状态void Timer0_Init(void) //计数模式测方波频率@12.000MHz
{AUXR &= 0x7F; //定时器时钟12T模式TMOD &= 0xF0; //设置定时器模式TMOD |= 0x05; //设为计数模式TL0 = 0; //设置定时初始值TH0 = 0; //设置定时初始值TF0 = 0; //清除TF0标志TR0 = 1; //定时器0开始计时
}void Timer1_Init(void) //1毫秒@12.000MHz
{AUXR &= 0xBF; //定时器时钟12T模式TMOD &= 0x0F; //设置定时器模式TL1 = 0x18; //设置定时初始值TH1 = 0xFC; //设置定时初始值TF1 = 0; //清除TF1标志TR1 = 1; //定时器1开始计时ET1 = 1; //开启定时器1中断
// EA = 1; //开启总中断
}void TIMER1_IRQHandler(void) interrupt 3
{/* 任务调度处理 */systick_ms++;key_dly++;display_dly++;collect_dly++;/* 测频率 */if(++count_1s == 1000){fre = (TH0 << 8) | TL0;TH0 = 0;TL0 = 0;count_1s = 0;}/* 显示处理 */smg(SMG,dot,pos);led(LED,pos);if(++pos == 8) pos = 0;relay(state_relay);beep(state_beep);
}
timer.h
#ifndef __TIMER_H
#define __TIMER_H#include "sys.h"
#include "display.h"extern unsigned long systick_ms; //滴答定时器
extern uchar key_dly; //按键任务调度
extern uint display_dly; //显示任务调度
extern uint collect_dly; //采集任务调度
extern uchar pos; //数码管led扫描位置
extern uint count_1s; //1s计时测频率
extern uint fre; //频率
extern bit state_relay; //继电器状态
extern bit state_beep; //蜂鸣器状态extern uchar SMG[];
extern uchar dot[];
extern uchar LED[];void Timer0_Init(void);
void Timer1_Init(void);#endif
5、iic
存放iic驱动函数,包括AT24C02、PCF8591的读写操作,使用时注意PCF8591读到的结果是上一次转换的值,多通道时要把接收变量对调,在进行ADC线性转换时,表达式中乘除相关的常量加个.0变成浮点型,否则整数相除可能四舍五入为0,如51.0/adc,而51/adc当51小于adc时,结果可能恒为0。
iic.c
/* # I2C代码片段说明1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include "iic.h"#define DELAY_TIME 10sbit scl = P2^0; //时钟线
sbit sda = P2^1; //数据线//
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 I2CStart(void)
{sda = 1;scl = 1;I2C_Delay(DELAY_TIME);sda = 0;I2C_Delay(DELAY_TIME);scl = 0;
}//
void I2CStop(void)
{sda = 0;scl = 1;I2C_Delay(DELAY_TIME);sda = 1;I2C_Delay(DELAY_TIME);
}//
void I2CSendByte(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 I2CReceiveByte(void)
{unsigned char da;unsigned char i;for(i=0;i<8;i++){ scl = 1;I2C_Delay(DELAY_TIME);da <<= 1;if(sda) da |= 0x01;scl = 0;I2C_Delay(DELAY_TIME);}return da;
}//
unsigned char I2CWaitAck(void)
{unsigned char ackbit;scl = 1;I2C_Delay(DELAY_TIME);ackbit = sda; scl = 0;I2C_Delay(DELAY_TIME);return ackbit;
}//
void I2CSendAck(unsigned char ackbit)
{scl = 0;sda = ackbit; I2C_Delay(DELAY_TIME);scl = 1;I2C_Delay(DELAY_TIME);scl = 0; sda = 1;I2C_Delay(DELAY_TIME);
}/*** @brief AT24C02读一个字节* * @param addr 地址* @return uchar 数据*/
uchar AT24C02_read(uchar addr)
{uchar dat;I2CStart(); //起始信号I2CSendByte(0xa0); //发送器件地址(写)I2CWaitAck(); //等待应答I2CSendByte(addr); //发送通道地址I2CWaitAck(); //等待应答I2CStart(); //起始信号I2CSendByte(0xa1); //发送器件地址(读)I2CWaitAck(); //等待应答dat = I2CReceiveByte(); //读数据I2CSendAck(1); //不应答I2CStop(); //停止信号return dat;
}/*** @brief AT24C02页读,读取一页* * @param buf 接收数据数组首地址* @param addr 内存地址,必须是8的整数倍,可以写十进制* @param num 数据个数*/
void AT24C02_read_page(uchar * buf,uchar addr,uchar num)
{I2CStart(); //起始信号I2CSendByte(0xa0); //发送器件地址(写)I2CWaitAck(); //等待应答I2CSendByte(addr); //发送通道地址I2CWaitAck(); //等待应答I2CStart(); //起始信号I2CSendByte(0xa1); //发送器件地址(读)I2CWaitAck(); //等待应答while(num--){*buf++ = I2CReceiveByte();//接收数据if(num)I2CSendAck(0); //应答elseI2CSendAck(1); //不应答}I2CStop();
}/*** @brief AT24C02写入一个字节* * @param addr 地址* @param dat 数据*/
void AT24C02_write(uchar addr,uchar dat)
{I2CStart(); //起始信号I2CSendByte(0xa0); //发送器件地址(写)I2CWaitAck(); //等待应答I2CSendByte(addr); //发送通道地址I2CWaitAck(); //等待应答I2CSendByte(dat); //发送数据I2CWaitAck(); //等待应答I2CStop(); //停止信号
}/*** @brief AT24C02页写,写入一页数据* * @param buf 数据数组首地址* @param addr 内存地址,必须是8的整数倍,可以写十进制* @param num 数据个数*/
void AT24C02_write_page(uchar * buf,uchar addr,uchar num)
{I2CStart(); //起始信号I2CSendByte(0xa0); //发送器件地址(写)I2CWaitAck(); //等待应答I2CSendByte(addr); //发送通道地址I2CWaitAck(); //等待应答while(num--){I2CSendByte(*buf++); //发送数据I2CWaitAck(); //等待应答}I2CStop(); //停止信号
}/*** @brief PCF8591读AD* * @param control 0x00:AIN0,0x01:光敏,0x03:电位器* @return uchar adc量化值,读到的是上次转换的值,读两个外设时要交换位置*/
uchar PCF8591_read(uchar control)
{uchar dat;I2CStart(); //起始信号I2CSendByte(0x90); //发送器件地址(写)I2CWaitAck(); //等待应答I2CSendByte(control); //发送通道地址I2CWaitAck(); //等待应答I2CStart(); //起始信号I2CSendByte(0x91); //发送器件地址(读)I2CWaitAck(); //等待应答dat = I2CReceiveByte(); //读数据I2CSendAck(1); //不应答I2CStop(); //停止信号return dat;
}/*** @brief PCF8591写DA* * @param control 只进行DA:0x40,AD/DA同时:0x40AIN0,0x41光敏,0x43电位器* @param dat DA量化值,0-255对应0-5V*/
void PCF8591_write(uchar control,uchar dat)
{I2CStart(); //起始信号I2CSendByte(0x90); //发送器件地址(写)I2CWaitAck(); //等待应答I2CSendByte(control); //发送通道地址I2CWaitAck(); //等待应答I2CSendByte(dat); //发送数据I2CWaitAck(); //等待应答I2CStop(); //停止信号
}
iic.h
#ifndef __IIC_H
#define __IIC_H#include "sys.h"uchar AT24C02_read(uchar addr);
void AT24C02_read_page(uchar * buf,uchar addr,uchar num);
void AT24C02_write(uchar addr,uchar dat);
void AT24C02_write_page(uchar * buf,uchar addr,uchar num);uchar PCF8591_read(uchar control);
void PCF8591_write(uchar control,uchar dat);#endif
6、ds1302
存放ds1302驱动函数,包括ds1302的读写,只保留了秒分时。
ds1302.c
/* # DS1302代码片段说明1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include "ds1302.h"sbit SCK = P1^7; //时钟线
sbit SDA = P2^3; //数据线
sbit RST = P1^3; //复位线code uchar write_addr[3] = {0x80,0x82,0x84};//写地址
code uchar read_addr[3] = {0x81,0x83,0x85};//读地址//时间: 秒, 分, 时, 日, 月, 周, 年
uchar Time[3] = {0x00,0x00,0x09};//
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);
}/*** @brief 写日期* */
void ds1302_write(void)
{uchar i;Write_Ds1302_Byte(0x8e,0x00); //解除写保护for(i = 0;i < 3;i++) //写数据{Write_Ds1302_Byte(write_addr[i],Time[i]);}Write_Ds1302_Byte(0x8e,0x80); //写保护
}/*** @brief 读日期* */
void ds1302_read(void)
{uchar i;for(i = 0;i < 3;i++){Time[i] = Read_Ds1302_Byte(read_addr[i]);}
}
ds1302.h
#ifndef __DS1302_H
#define __DS1302_H#include "sys.h"extern uchar Time[];void ds1302_write(void);
void ds1302_read(void);#endif
7、onewire
存放ds18b20驱动函数,包括温度的读取,如果时序被中断频繁打乱可以在时序两端加上开关总中断(EA=0,1)。
onewire.c
/* # 单总线代码片段说明1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include "onewire.h"sbit DQ = P1^4; //数据线//
void Delay_OneWire(unsigned int t)
{unsigned char i;while(t--){for(i=0;i<12;i++);}
}//
void Write_DS18B20(unsigned char dat)
{unsigned char i;for(i=0;i<8;i++){DQ = 0;DQ = dat&0x01;Delay_OneWire(5);DQ = 1;dat >>= 1;}Delay_OneWire(5);
}//
unsigned char Read_DS18B20(void)
{unsigned char i;unsigned char dat;for(i=0;i<8;i++){DQ = 0;dat >>= 1;DQ = 1;if(DQ){dat |= 0x80;} Delay_OneWire(5);}return dat;
}//
bit init_ds18b20(void)
{bit initflag = 0;DQ = 1;Delay_OneWire(12);DQ = 0;Delay_OneWire(80);DQ = 1;Delay_OneWire(10); initflag = DQ; Delay_OneWire(5);return initflag;
}/*** @brief ds18b20温度读取* * @param dot 小数点位数,0:保留0位小数,1: 保留一位小数,2:保留两位小数* @return uint 温度值,要配合小数点位数使用*/
uint ds18b20_read(uchar dot)
{uint temp; //温度值uchar tml,tmh; //温度值低八位,高八位init_ds18b20(); //初始化Write_DS18B20(0xcc); //跳过ROMWrite_DS18B20(0x44); //开始温度转换init_ds18b20(); //初始化Write_DS18B20(0xcc); //跳过ROMWrite_DS18B20(0xbe); //读取暂存寄存器tml = Read_DS18B20(); //读取低八位tmh = Read_DS18B20(); //读取高八位temp = (tmh << 8) | tml; //合并数据switch (dot) //根据小数点位数设置精度{case 0: temp = temp * 0.0625;break;case 1: temp = temp * 0.625;break;case 2: temp = temp * 6.25;break;}return temp;
}
onewire.h
#ifndef __ONEWIRE_H
#define __ONEWIRE_H#include "sys.h"uint ds18b20_read(uchar dot);#endif
8、uart
存放串口驱动函数,包括串口初始化、收发、中断等。
uart.c
#include "uart.h"uchar uart_rxbuf[10]; //串口接收数组
uchar uart_rxindex; //串口接收数组索引void Uart1_Init(void) //9600bps@12.000MHz
{SCON = 0x50; //8位数据,可变波特率AUXR |= 0x01; //串口1选择定时器2为波特率发生器AUXR |= 0x04; //定时器时钟1T模式T2L = 0xC7; //设置定时初始值T2H = 0xFE; //设置定时初始值AUXR |= 0x10; //定时器2开始计时ES = 1; // 允许串口中断
}void UART1_IRQHandler(void) interrupt 4
{if(RI){if(uart_rxindex == 10)uart_rxindex = 0;uart_rxbuf[uart_rxindex++] = SBUF;RI = 0;}
}
char putchar(char ch)
{SBUF = ch;while (TI == 0); // 等待发送完成TI = 0; // 发送完成标志位置0return ch;
}
uart.h
#ifndef __UART_H
#define __UART_H#include "sys.h"extern uchar uart_rxbuf[];void Uart1_Init(void);#endif
9、ultrasound
存放超声波测距函数。
ultrasound.c
#include "ultrasound.h"sbit TX = P1^0; //发射引脚
sbit RX = P1^1; //接收引脚uchar read_distance(void)
{uchar distance,num = 10;// TX引脚发送40KHz方波信号驱动超声波发送探头TX = 0;CL = 0xF3; //设置定时初值CH = 0xFF; //设置定时初值CR = 1; //定时器计时while(num--){while(!CF);TX ^= 1;CL = 0xF3; //设置定时初值CH = 0xFF; //设置定时初值CF = 0;}//RX引脚接收时间计时计算距离CR = 0;CL = 0; //设置定时初值CH = 0; //设置定时初值CR = 1;while(RX && !CF); //等待收到脉冲CR = 0;if(CF) //发生溢出{CF = 0;distance = 255;}else //计算距离distance = ((CH<<8)+CL)*0.017;return distance;
}
ultrasound.h
#ifndef __ULTRASOUND_H
#define __ULTRASOUND_H#include "sys.h"uchar read_distance(void);#endif
三、USER文件夹
main.c
存放main函数,按键任务,采集任务,显示任务,主逻辑等。
main.c
#include <STC15F2K60S2.H>
#include "sys.h"
#include "display.h"
#include "key.h"
#include "timer.h"
#include "ds1302.h"
#include "iic.h"
#include "onewire.h"
#include "uart.h"
#include "ultrasound.h"/* 变量区 */
uchar SMG[8] = {20,20,20,20,20,20,20,20}; //数码管显示
uchar dot[8] = {0,0,0,0,0,0,0,0}; //小数点显示
uchar LED[8] = {0,0,0,0,0,0,0,0}; //led显示
uint temperature; //温度变量
uchar state_display = 5; //显示状态
bit state_led;/* 函数区 */
//显示任务
void display_task(void)
{/*LED任务*/LED[0] = state_led;//放在延迟判断前,这样闪烁间隔才正常LED[1] = 1;LED[2] = 0;LED[3] = 1;LED[4] = 0;LED[5] = 1;LED[6] = 0;LED[7] = 1;if(display_dly<100)return;display_dly = 0;/*数码管任务*/switch(state_display){case 0: /*频率显示*/SMG[0] = 15;SMG[1] = 20;SMG[2] = 20;SMG[3] = (fre<10000) ? 20:fre/10000;SMG[4] = (fre<1000) ? 20:fre/1000%10;SMG[5] = (fre<100) ? 20:fre/100%10;SMG[6] = (fre<10) ? 20:fre/10%10;SMG[7] = fre%10;break;}
}//采集任务
void collect_task(void)
{if(collect_dly<300)return;collect_dly = 0;temperature = ds18b20_read(0);
// printf("温度是:%d°C\r\n",(uint)temperature);}//按键处理任务
void key_task(void)
{uchar key_num;if(key_dly<10)return;key_dly = 0;
// key_num = key_scan1();key_num = key_scan2();switch (key_num){/* 独立按键 */
// case 4:
// break;
// case 5:
// break;
// case 6: state_relay = ~state_relay;
// break;
// case 7: state_beep = ~state_beep;
// break;/* 矩阵键盘 */case 4: break;case 5: break;case 8: state_relay = ~state_relay;break;case 9: state_led = ~state_led;break;}
}void main(void)
{sys_init(); //系统初始化-关闭蜂鸣器继电器Timer0_Init(); //定时器0-计数模式初始化Timer1_Init(); //定时器1-1ms中断任务调度初始化
// Uart1_Init(); //串口1初始化9600波特率,与矩阵键盘几个引脚冲突ds1302_write(); //ds1302时钟初始化-写入时间ds18b20_read(0); //ds18b20初始化-上电默认85度,先读一下,延时750ms,再读一下
// delay_ms(750);
// ds18b20_read(0);AT24C02_write(1,123); //EEPROM初始化-写入值EA = 1; //打开总中断while(1){key_task();collect_task();display_task();}
}
四、源码
通过网盘分享的文件:蓝桥杯单片机模板.zip
链接: https://pan.baidu.com/s/1s2PKVkAlBcU8-6SgwJgduw?pwd=sugi
提取码: sugi