蓝桥杯单片机模板(全考也不怕)

基于西风模板格式,在此感谢西风等up主。

定时器0用于Ne555测频率

定时器1用于主程序控制

定时器2用于串口通信

PCA定时器用于超声波

注意:不要死记模板,理解记忆,基础最重要。

上代码,文末有 某度网盘链接 直接下载。

Led.c文件,包含初始化,led、beep和Relay相关函数

static unsigned char temp = 0x00;          //目的共用蜂鸣器和继电器
static unsigned char temp_Old = 0xff;
void System_Init()
{
    P0 =0xff;
    P2 = P2 & 0x1f | 0x80;
    P2 = P2 & 0x1f;
    P0 =0x00;
    P2 = P2 & 0x1f | 0xa0;
    P2 = P2 & 0x1f;
}

void Led_Disp(unsigned char addr, Enable)
{
    static unsigned char temp = 0x00;
    static unsigned char temp_Old = 0xff;
    
    if(Enable) 
        temp |= (0x01 << addr);
    else 
        temp &= ~(0x01 << addr);
    if(temp != temp_Old)
    {
        P0 = ~temp;
        P2 = P2 & 0x1f | 0x80;
        P2 = P2 & 0x1f;
        temp_Old = temp;
    }
}

void Beep(unsigned char Enable)
{
    if(Enable) 
        temp |= 0x40;
    else 
        temp &= ~(0x40);
    if(temp != temp_Old)
    {
        P0 = temp;
        P2 = P2 & 0x1f | 0xa0;     //Y5C
        P2 = P2 & 0x1f;
        temp_Old = temp;
    }
}
void Relay(unsigned char Enable)
{
    if(Enable) 
        temp |= 0x10;
    else 
        temp &= ~(0x10);
    if(temp != temp_Old)
    {
        P0 = temp;
        P2 = P2 & 0x1f | 0xa0;
        P2 = P2 & 0x1f;
        temp_Old = temp;
    }
}

Key.c 按键扫描函数,判断多按键可将temp定义为16位,然后按位标志来实现

比如temp = 4,就可以写为temp = temp | (0x01 << 0),依次类推即可。

关闭T2R为了避免串口和按键冲突,最后记得再次打开。

(在串口初始化函数中有,可isp生成)

(不用的按键在比赛时记得注释掉,用不上的尽量不敲,比如可能会影响NE555的测量的准确率)

unsigned char Key_Scan()
{
    unsigned char temp = 0;       //必须有赋值为0
    AUXR &= 0xEF;                 //关闭T2R 即 定时器2 (串口波特率发生所用的定时器)
    P44=0; P42=1; P35=1 ;P34=1;
    if(P33 == 0) temp=4;
    if(P32 == 0) temp=5;
    if(P31 == 0) temp=6;
    if(P30 == 0) temp=7;
    P44=1; P42=0; P35=1 ;P34=1;
    if(P33 == 0) temp=8;
    if(P32 == 0) temp=9;
    if(P31 == 0) temp=10;
    if(P30 == 0) temp=11;
    P44=1; P42=1; P35=0 ;P34=1;
    if(P33 == 0) temp=12;
    if(P32 == 0) temp=13;
    if(P31 == 0) temp=14;
    if(P30 == 0) temp=15;
    P44=1; P42=1; P35=1 ;P34=0;
    if(P33 == 0) temp=16;
    if(P32 == 0) temp=17;
    if(P31 == 0) temp=18;
    if(P30 == 0) temp=19;
    P3 = 0xff;
    AUXR |= 0x10; //打开T2R 即 定时器2 (串口波特率发生所用的定时器)
    return temp;
}

Seg.c文件,包含数码管动态显示函数

段码数组duala[],不用记,比赛的时候,直接从赛点资源包拷贝,还自带注释,干嘛要自己敲。


unsigned char code weila[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
unsigned char code duala[] = 
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
0xff, //
0x88, //A
0x83, //b
0xc6, //C
0xa1, //d
0x86, //E
0x8e  //F
};


void Seg_Disp(unsigned char wei,dua,point)
{
    //消隐
    P0 = 0xff;
    P2 = P2 & 0x1f |0xe0;
    P2 &= 0x1f;    
    //给位码
    P0 = weila[wei];
    P2 = P2 & 0x1f |0xc0;
    P2 &= 0x1f;    
    //给段码
    P0 = duala[dua];
    if(point)
        P0 &= 0x7f;//小数点
    P2 = P2 & 0x1f | 0xe0;
    P2 &= 0x1f;
}

 iic.c文件,EEPROM和ADC都要用到IIC。

在这里,EEPROM我没选择用块写,按字节写的话,这两个模块的读写程序一样。

a0和a1时用在EEPROM的器件写读地址,90和91是ADC的器件写读地址;

void AT24C02_Write(unsigned char addr, dat)//addr取值0-255,也就是写的位置
{
    I2CStart();
    I2CSendByte(0xa0);
    I2CWaitAck();
    I2CSendByte(addr);
    I2CWaitAck();
    
    I2CSendByte(dat);
    I2CWaitAck();
    I2CStop();
    I2C_Delay(5);
}
unsigned char AT24C02_Read(unsigned char addr)//addr取值0-255,
{
    unsigned char dat;
    I2CStart();
    I2CSendByte(0xa0);
    I2CWaitAck();
    I2CSendByte(addr);
    I2CWaitAck();
    
    I2CStart();
    I2CSendByte(0xa1);
    I2CWaitAck();

    dat = I2CReceiveByte();
    I2CSendAck(1);
    I2CStop();
    return dat;
}

void Da_Write(unsigned char dat)
{
    I2CStart();
    I2CSendByte(0x90);
    I2CWaitAck();
    I2CSendByte(0x41);              //0x41为开启数模转换通道,0x0100 0001,第7位是1就可
    I2CWaitAck();
    
    I2CSendByte(dat);
    I2CWaitAck();
    I2CStop();
}
unsigned char Ad_Read(unsigned char addr) //addr为模数转换的地址,光敏0x41和变阻器0x43
{
    unsigned char temp;
    I2CStart();
    I2CSendByte(0x90);
    I2CWaitAck();
    I2CSendByte(addr);
    I2CWaitAck();
    
    I2CStart();
    I2CSendByte(0x91);
    I2CWaitAck();

    temp = I2CReceiveByte();
    I2CSendAck(1);
    I2CStop();
    return temp;
}

onewire.c   DS18B20,没啥说的,记得就行。

float DS18B20_Read()
{
    unsigned char low,high;
    init_ds18b20();
    Write_DS18B20(0xcc);
    Write_DS18B20(0x44);
    init_ds18b20();
    Write_DS18B20(0xcc);
    Write_DS18B20(0xbe);
    
    low  = Read_DS18B20();
    high = Read_DS18B20();
    
    return  ((high<<8)|low)/16.0;
    
}

DS1302,需要写两个函数,写,读函数

void RTC_Set(unsigned char* Rtc)
{
    unsigned char i = 0;
    Write_Ds1302_Byte(0x8e,0x00);
    for(i=0;i++;i<3)
    {
        Write_Ds1302_Byte(0x84-2*i,Rtc[i]); //16进制写,RTC[]数组也为16进制写
      //Write_Ds1302_Byte(0x84-2*i,Rtc[i]/10*16+Rtc[i]%10);//10进制写,RTC[]数组也为10进制写
        
    }
    Write_Ds1302_Byte(0x8e,0x80);
    
}

void RTC_Read(unsigned char* Rtc)
{
    unsigned char i = 0;
    for(i=0;i++;i<3)
    {
        Rtc[i] = Read_Ds1302_Byte(0x85-2*i);//16进制读
        //Rtc[i] = Rtc[i]/16*10+Rtc[i]%16;  //10进制
    }
}

超声波模块distance.c

#include "Distance.h"
sbit TX_D = P1^0;
sbit RX_D = P1^1;

void Delay12us()        //@12.000MHz
{
    unsigned char i;
    i = 38;     //isp上出来是33,改为38好用一点
    while (--i);
}

void Wave_Init()
{
    unsigned char i;
    for(i=0;i<8;i++)
    {
        TX_D = 1;
        Delay12us();
        TX_D = 0;
        Delay12us();
    }
}

unsigned char Get_Distance()
{
    unsigned char distance;
    CMOD = 0x00;
    CH=CL=0;
    Wave_Init();
    CR = 1;
    while(CF == 0 && RX_D == 1);//等待,RX_D收到为0
    CR = 0;
    if(CF == 1)
    {
        distance = 255;
        CF = 0;
    }
    else 
    {
        distance = (CH << 8 | CL) * 0.017;   //CH和CL单位为us,转换单位时要用到
    }
    return distance;
}

串口模块,非常重要,如果出串口,串口在程序中的中作用能和按键一样,控制界面等;

#include "Uart.h"

void UartInit(void)             //9600bps@12.000MHz
{
    SCON = 0x50;                //8位数据,可变波特率
    AUXR |= 0x01;               //串口1选择定时器2为波特率发生器
    AUXR &= 0xFB;               //定时器2时钟为Fosc/12,即12T
    T2L = 0xE6;                 //设定定时初值
    T2H = 0xFF;                 //设定定时初值
    AUXR |= 0x10;               //启动定时器2
    ES = 1;                     //生成没有以下两句,启动串口中断
    EA = 1;
}

void SendData(unsigned char dat)
{

    SBUF = dat;                 //写数据到UART数据寄存器
    while(TI == 0);
    TI = 0;
}
void SendString(char *s)
{
    while (*s)                  //检测字符串结束标志
    {
        SendData(*s++);         //发送当前字符
    }
}
extern char putchar(char dat)   //使用stdio库,printf打印函数
{
    SBUF = dat;                 //写数据到UART数据寄存器
    while(TI == 0);
    TI = 0;
    return dat;                 // 就是SendData函数加return,然后在.h中声明就可
}

main函数,直接上吧,不想改了,我自己用的测试。

发送love,返回距离和频率。

按下按键,在第0个数码管显示键值。

#include <STC15F2K60S2.H>
#include <stdio.h>
#include "Distance.h"
#include "iic.h"
#include "ds1302.h"
#include "onewire.h"
#include "Key.h"
#include "Seg.h"
#include "Led.h"
#include "Uart.h"
#define uint8 unsigned char 
#define uint16 unsigned int 
    
unsigned char Key_Old, Key_Val, Key_Down, Key_Up;
unsigned char Seg_Pos;
unsigned char Seg_Buf[8] = {10,10,10,10,10,10,10,10};
unsigned char Seg_Poi[8] = {0,0,0,0,0,0,0,0};
unsigned char Led_Buf[8] = {0,0,0,0,0,0,0,0};
unsigned char Uart_Buf[6];
unsigned char Uart_Buf_Index = 0;
unsigned char Rtc_Buf[3] = {0x23,0x59,0x55};
unsigned int Key_Slow,Seg_Slow,Uart_Slow;
unsigned int Freq,Freq_Time;
int Distance;


bit Beep_Flag;

void Key_Proc()
{
    if(Key_Slow) return;
    Key_Slow = 1;
    
    Key_Val  = Key_Scan();
    Key_Down = Key_Val & (Key_Old ^ Key_Val);
    Key_Up   = ~Key_Val & (Key_Old ^ Key_Val);
    Key_Old  = Key_Val;
    
    if(Key_Down) Seg_Buf[0] = Key_Down;
    
}

void Seg_Proc()
{
    if(Seg_Slow) return;
    Seg_Slow = 1;
    
    Distance = Get_Distance();
    
    
    Seg_Buf[0] = Distance / 100;
    Seg_Buf[1] = Distance / 10 % 10;
    Seg_Buf[2] = Distance % 10;    
    
    Seg_Buf[3] = (Freq / 10000) ;
    Seg_Buf[4] = (Freq / 1000 % 10) ;
    Seg_Buf[5] = (Freq / 100 % 10) ;
    Seg_Buf[6] = (Freq / 10 % 10);
    Seg_Buf[7] = (Freq % 10);
}


void Led_Proc()
{
    Led_Buf[0] = 1;
    Beep(Beep_Flag);
    Relay(Beep_Flag);
}

void Uart_Proc()
{
    if(Uart_Slow) return;
    Uart_Slow = 1;
    //串口数据三次判断
    if(Uart_Buf_Index != 0)      //判断有无数据     
    {
        if(Uart_Buf_Index == 4)  //判断字符个数是否为目的字符串个数
        {
            if(Uart_Buf[0] == 'l' && Uart_Buf[1] == 'o' && Uart_Buf[2] == 'v' && Uart_Buf[3] == 'e')              //判断该字符串
            {
                Led_Buf[1] = ~Led_Buf[1];
                Beep_Flag = ~Beep_Flag;
                printf("距离为:%d\r\n",Distance);
                printf("频率为:%d\r\n",Freq);
                SendString("I love you too.\r\n");    
                SendString("send over\r\n");
            }
        }    
        Uart_Buf_Index = 0;     //注意将串口数组清零,
    }
    
}
void Timer0Init(void)        //1毫秒@12.000MHz
{
    AUXR &= 0x7F;        //定时器时钟12T模式
    TMOD &= 0xF0;        //设置定时器模式
    TMOD |= 0x05;        //设置定时器模式
    TL0 = 0;        //设置定时初值
    TH0 = 0;        //设置定时初值
    TF0 = 0;        //清除TF0标志
    TR0 = 1;        //定时器0开始计时
}


void Timer1Init(void)        //1微秒@12.000MHz
{
    AUXR &= 0xBF;        //定时器时钟12T模式
    TMOD &= 0x0F;        //设置定时器模式
    TL1 = 0x18;          //设置定时初值
    TH1 = 0xFC;          //设置定时初值
    TF1 = 0;             //清除TF1标志
    TR1 = 1;             //定时器1开始计时
    ET1 = 1;
    EA = 1;
}

void Timer1Service()  interrupt 3
{
    if(++Key_Slow == 30)  Key_Slow = 0;
    if(++Seg_Slow == 500) Seg_Slow = 0;
    if(++Uart_Slow == 200) Uart_Slow = 0;
    if(++Freq_Time == 1000)
    {
        Freq_Time = 0;
        TR0 = 0;
        Freq = (TH0<<8)| TL0;
        TL0 = 0;        //设置定时初值
        TH0 = 0;        //设置定时初值
        TR0 = 1;
    }
    if(++Seg_Pos == 8)  Seg_Pos = 0;
    Led_Disp(Seg_Pos,Led_Buf[Seg_Pos]);
    Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos], Seg_Poi[Seg_Pos]);
    
}

void Uart1Service() interrupt 4    //必须要写
{
    if (RI == 1)
    {
        Uart_Buf[Uart_Buf_Index] = SBUF;              
        Uart_Buf_Index++;
        RI = 0;                 //清除RI位
    }
//    if(Uart_Buf_Index > 5) Uart_Buf_Index = 0;
}


void main()
{
    System_Init();
    Timer0Init();
    Timer1Init();
    UartInit();
    while(1)
    {

        Key_Proc();
        Seg_Proc();
        Led_Proc();
        Uart_Proc();
    }
}

附上工程链接,水平有限,如有错误,请谅解。

百度网盘链接:https://pan.baidu.com/s/100JJPNd_FLn9G6_vOX90aQ?pwd=8888 

再次感谢B站 西风   等up主。

祝愿大家都能有所收获。

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值