蓝桥杯单片机代码编写总结
时间为t=(TH1<<8|TL1)us,超声波速度为v=340m/s=340 00cm/1000 000us=0.034cm/us。用作A/D转换时,高四位设置为0000,低四位根据需要,一般为0001和0011(光敏和RB2) ,直接写作1或3均可。同样的,adc(3);可实现RB2电压值的采集。用作D/A转换时,高四为设为0100,低四位0000即可,写作0x40。AIN1输入光敏电阻的电压信
目录
一,初始化

Y4控制LED;Y5控制蜂鸣器和继电器;Y6控制数码管位选;Y7控制数码管段选。
void init74hc138(unsigned char n){
P2=(P2&0x1f)|(n<<5);
P2&=0x1f;
}//选通LED、蜂鸣器和继电器、数码管段选和位选
void init(){
P0=0x00;
init74hc138(5);
//初始关闭蜂鸣器、继电器
P0=0xff;
init74hc138(4);
//初始熄灭LED
}
二、按键


COL2与P42相连
//---------矩阵按键---------
sbit row1=P3^0;
sbit row2=P3^1;
sbit row3=P3^2;
sbit row4=P3^3;
sbit col1=P4^4;
sbit col2=P4^2;
sbit col3=P3^5;
sbit col4=P3^4;
unsigned char ScanKey()
{
col1=0;col2=col3=col4=1;
if(row1==0)return 7;
if(row2==0)return 6;
if(row3==0)return 5;
if(row4==0)return 4;
col2=0;col1=col3=col4=1;
if(row1==0)return 11;
if(row2==0)return 10;
if(row3==0)return 9;
if(row4==0)return 8;
col3=0;col2=col1=col4=1;
if(row1==0)return 15;
if(row2==0)return 14;
if(row3==0)return 13;
if(row4==0)return 12;
col4=0;col2=col3=col1=1;
if(row1==0)return 19;
if(row2==0)return 18;
if(row3==0)return 17;
if(row4==0)return 16;
return 0;
}
//---------独立按键---------
sbit S4=P3^3;
sbit S5=P3^2;
sbit S6=P3^1;
sbit S7=P3^0;
unsigned char ScanKey()
{
if(S4==0) return 4;
if(S5==0) return 5;
if(S6==0) return 6;
if(S7==0) return 7;
return 0;
}
//---------公共部分---------
unsigned char keyVal=0,keyold=0,keyUp=0,keyDown=0;
void Key_Loop()
{
keyVal=ScanKey();
keyDown=keyVal&(keyold^keyVal);
keyUp=~keyVal&(keyold^keyVal);
//if(keyval = n && keyold != n) {按键操作};
//可以实现按下一次操作一次
keyold=keyVal;
}
三、数码管

Y6控制数码管位选;Y7控制数码管段选。

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
0xff //熄灭 16
};//共阳数码管段码表
//考试会给,即使不给也能推,使用电脑计算器的程序员模式很方便
unsigned char SegBuff[8]={16,16,16,16,16,16,16,16};
//每位数码管动态值
unsigned char point[8]={0,0,0,0,0,0,0,0};
//每位数码管小数点情况
void Seg(unsigned char addr,number,dot)
{
P0=0xff; //消隐
init74hc138(7);
P0=0x01<<addr; //数码管段选
init74hc138(6);
P0=Seg_Table[number]; //要显示的数
if(dot) //是否带小数点
P0&=0x7f;
init74hc138(7);
}//数码管显示
void Seg_Loop()
{
static unsigned char i=0;
Seg(i,SegBuff[i],point[i]);
i++;
if(i==8)i=0;
}//数码管刷新
四、单总线协议(onewire.c 的使用)
使用DS18B20获取温度数据,其采用单总线协议。

官方提供的代码:
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;
}
需自己添加的部分:
sbit DQ=P1^4;
//记得加上引脚定义
float getT(){
unsigned int temp;
float temperature;
unsigned char teH,teL;
init_ds18b20();
Write_DS18B20(0xcc);//跳过ROM操作
Write_DS18B20(0x44);//启动一次温度转换
init_ds18b20();
Write_DS18B20(0xcc);//跳过ROM操作
Write_DS18B20(0xbe);//发送读指令
teL=Read_DS18B20();//读取温度值的低字节
teH=Read_DS18B20();//读取温度值的高字节
temp=teH<<8|teL;//温度值合并为16位数
//低四位为小数部分,故需要右移四位,即乘以1/16=0.0625
temperature=temp*0.0625;
return temperature;
}
使用时,由于上电温度数值为85°C,可能影响测评结果,故可以在main()函数中进入while(1)循环之前添加语句:
while(getT()==85);
五、SPI协议(ds1302.c 的使用)
使用DS1302获取日历时钟数据(BCD编码),其采用SPI协议。

官方提供的代码:
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/10<<4|dat%10);
//原代码为Write_Ds1302(dat);需要自己修改
RST=0;
}
unsigned char Read_Ds1302_Byte ( unsigned char address )
{
unsigned char i,temp=0x00;
// unsigned char dat1,dat2;
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_();
//下面这段以及上面的变量定义需要自己写
// dat1=temp/16;
// dat2=temp%16;
// temp=dat1*10+dat2;
return (temp);
}

需自己添加的部分:
(原官方代码中亦有需要更改的地方)
unsigned char time[]={50,59,23,0,0,0,0};
//假设初始值为:0年-星期一-0月0日23时59分50秒
void w_ds1302(void){
unsigned char i,addr=0x80;
Write_Ds1302_Byte(0x8e,0x00);//关掉写保护
for(i=0;i<7;i++){
Write_Ds1302_Byte(addr,time[i]);
addr=addr+2;
}
Write_Ds1302_Byte(0x8e,0x80);//开启写保护
}//写入数据,用于设置初始时间
void r_ds1302(void){
unsigned char i,addr=0x81;
for(i=0;i<7;i++){
time[i]=Read_Ds1302_Byte(addr);
addr=addr+2;
}
}获取日历时钟数据
六、IIC协议(iic.c 的使用)

数模/模数转换,以及EEPROM的读写均采用IIC协议。
官方提供的代码:
#define DELAY_TIME 10
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);//t>4.7us
sda = 0;
I2C_Delay(DELAY_TIME);
scl = 0;
}//SCL为高电平时,SDA由高电平向低电平变化
//总线停止
void I2CStop(void)
{
sda = 0;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 1;
I2C_Delay(DELAY_TIME);
}//SCL为高电平时,SDA由低电平向高电平变化
//通过 I2C 总线发送一个字节(8 位)的数据
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;
}
//通过 I2C 总线接收一个字节(8 位)的数据
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);
}
需自己添加的代码:
sbit sda=P2^1;
sbit scl=P2^0;
首先是引脚定义,其余代码根据需求不同进行编写,如下:
1、PCB8591:DA/AD转换

设备地址为1001,即9
使用时,0x90为写;0x91为读。
用作D/A转换时,高四为设为0100,低四位0000即可,写作0x40。
用作A/D转换时,高四位设置为0000,低四位根据需要,一般为0001和0011(光敏和RB2) ,直接写作1或3均可。

AIN1输入光敏电阻的电压信号,AIN3输入RB2可调电阻的电压采集信号。
两者均需要实现AD模数转换,基本函数一致。
需自己添加的代码:
//从设备读取模拟转换后的数据。
unsigned char adc(unsigned char addr)
{
unsigned char date;
// EA=0;
I2CStart(); //S
I2CSendByte(0x90); //写入时最低为是0
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CStart(); //S
I2CSendByte(0x91); //ADDRESS+1;PCB8591的设备地址为1001=9
I2CWaitAck(); //A;主机等待PCB8591的应答信号
date=I2CReceiveByte(); //DATA BYTE
I2CSendAck(1); //A;主机发送应答信号,由PCB8591接收
I2CStop(); //P
// EA=1;
return date;
}
adc(1);可实现对光敏电阻电压值的采集;同样的,adc(3);可实现RB2电压值的采集。
需注意,返回的date是unsigned char型,表示最小分压的个数,故电压值为:
V=(float)adc(n)/255*5.0;
15引脚OUT是模拟信号输出,故需要实现DA数模转换。
需自己添加代码:
//向 DAC 设备写入数据以输出模拟信号。
void DAC(unsigned char DAC_date)
{
// EA=0;
I2CStart(); //S
I2CSendByte(0x90); //ADDRESS+0;发送设备地址及读写状态,0为写
I2CWaitAck(); //A;主机等待来自PCB8591的应答信号
I2CSendByte(0x40); //CONTROL BYTE;写入控制字,
I2CWaitAck(); //A;主机等待来自PCB8591的应答信号
I2CSendByte(DAC_date); //DATA BYTE;写入数据
I2CWaitAck(); //A;主机等待来自PCB8591的应答信号
//根据协议,可以多次写入数据
I2CStop(); //P
// EA=1;
}
使用时,若要输出电压为V,则应令参数DAC_date为:
DAC_date=(unsigned char)(V/5.0)*255;
测量时,将电表表笔分别连接板子右侧D/A引脚和GND引脚即可。

2、AT24C02:EEPROM读写
直接拿图:

需自己添加的代码:
//向 EEPROM 写入数据
void w_eeprom(unsigned char addr,unsigned char date)
{
// EA=0;
I2CStart();
I2CSendByte(0xA0);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CSendByte(date);
I2CWaitAck();
I2CStop();
// EA=1;
}
//从 EEPROM 读取数据
unsigned char r_eeprom(unsigned char addr)
{
unsigned char date;
// EA=0;
I2CStart();
I2CSendByte(0xA0);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CStart();
I2CSendByte(0xA1);
I2CWaitAck();
date=I2CReceiveByte();
I2CSendAck(1);
I2CStop();
// EA=1;
return date;
}
PCB8591和AT24C02均使用iic协议,代码类似,具体区别于设备地址。如记不住,考试时可以查两者的手册。手册里也包含了iic协议规定的读写时序,可自行查看。
七、超声波
使用超声波模块,首先要将J2跳线帽接1-3和2-4。
#include "intrins.h"
float distance;//距离
//----------引脚定义----------
sbit TX=P1^0;
sbit RX=P1^1;
//-------超声波发送延时-------
void delay(){
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
}
//---------发送超声波脉冲---------
void send_wave(){
unsigned char i=8;
do{
TX=1;
delay();
TX=0;
}while(i--);
}
时间为t=(TH1<<8|TL1)us,超声波速度为v=340m/s=340 00cm/1000 000us=0.034cm/us。则距离distance = t * v / 2 (cm);
//----------接收超声波并计算距离----------
void re_wave(){
send_wave();
TH1=0;
TL1=0;
TR1=1; //启动定时器1,开始计时
while((RX==1)&&(TF1==0));
TR1=0; //停止定时器1,结束计时
// 检查定时器1是否溢出
if(TF1){
TF1=0;
distance=0.0;
}else{
distance=(float)(TH1<<8|TL1)*0.017;
if((int)distance>500) distance=0.0;
}
}
在main()里需要添加语句:
TMOD|=0x10;
//如果使用定时器0做计数器,则TMOD|=0x01;
八、NE555

注意使用时需要将跳线帽连接NET_SIG和SIG_OUT
(SIG_OUT即P42,相当于用外部中断计数)
ne555模块用到了定时器0的计数器模式,对ne555输出的脉冲进行计数。计数器可以利用stc-isp软件生成的代码进行修改。

因为计数器不需要时钟,所以我们删除 AUXR |= 0x80即可;
同时将TH0和TL0都清零。
TMOD寄存器中的低四位是用来控制定时器0的模式的,我们需要将TMOD.1/TMOD.0置为01,将TMOD.2置为0。即在 TMOD &= 0xf0; 后添加TMOD |= 0x05;
在代码最后加上 ET0 = 0关中断;
void Timer0_Init(void) //100微秒@12.000MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x05;
TL0 = 0x00; //设置定时初始值
TH0 = 0x00; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 0; //禁止使能定时器0中断
}
读取计数器的数值
//读计数器计数值
unsigned int get_count(){
unsigned int temp;
temp=TH0<<8|TL0;
TH0=0X00;
TL0=0X00;
if(TF0){//如果检测到T0溢出,证明了频率超过了65535
return 0xffff;
}else{
return temp;
}
}
使用T1作为计时器,每秒读取一次,则读取的数值即为频率。
unsigned int ne555_count;
void Timer1_Init(void) //1毫秒@12.000MHz
{
AUXR |= 0x40; //定时器时钟1T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x20; //设置定时初始值
TH1 = 0xD1; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1; //使能定时器1中断
EA=1;
}
void Timer1_Isr(void) interrupt 3
{
static unsigned int count1=0;
count1++;
if(count1==1000){
ne555_count=get_count();
count1=0;
}
}
九、串口
直接使用烧录软件生成即可

//串口初始化设置
void UartInit(void) //9600bps@12.000MHz
{
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x40; //定时器时钟1T模式
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //设置定时器模式
TL1 = 0xC7; //设置定时初始值
TH1 = 0xFE; //设置定时初始值
ET1 = 0; //禁止定时器中断
TR1 = 1; //定时器1开始计时
//下面需要自行添加
// ES =1; //中断入口
EA =1;
}
unsigned char rdat;
//串口发送一个字符
void uart_send_byte(unsigned char date){
SBUF = date;//发送数据
while(TI==0);//等待数据发送完成
TI=0;
}
//串口发送一个字符串
void uart_send_str(unsigned char *str){
while(*str != '\0'){
uart_send_byte(*str++);
}
}
//串口中断服务函数
void isr_uart() interrupt 4
{
//如果接收到数据
if(RI){
RI=0;
rdat=SBUF;
}
}
还有另一种发送字符串的写法:
使用了stdio.h的printf()标准输出函数,由于其与库中的putchar()相关联,故需要在main.c里对putchar()函数进行重载:
//添加头文件
#include "stdio.h"
char putchar(char ch){
SBUF=ch;
while(!TI);
TI=0;
return ch;
}
使用示例:
float Vrb2;
...
void main(){
...
while(1){
...
printf("电压值为:%.2f \r\n",Vrb2);
//串口发送电压值,保留两位小数
...
}
}
我个人的代码编写习惯或者说模板
sys.h
#ifndef __SYS_H__
#define __SYS_H__
#include <STC15F2K61S2>
//sys.c
//seg_key.c
//iic.c
//ds1302.c
//onewire.c
//以上.c文件的变量和函数均在sys.h中声明一编
...//extern 声明外部变量
...//函数声明
#endif
main.c
#include "sys.h"
...//相关变量或标志定义
...//中断、串口等初始化函数
void main(){
...//初始化
while(1){
...
}
}
...//中断服务函数和串口服务
sys.c
#include "sys.h"
...//整个项目需要用到的一些变量
void init74hc138(unsigned char n){
...
}//74HC138译码器选通控制
void init(){
...
}//系统初始化
void led(unsigned char n){
...
}//选择LED指示灯点亮
void buzz(bit flag){
...
}//蜂鸣器
void relay(bit flag){
...
}//继电器
...
seg_key.c
#include "sys.h"
code unsigned char Seg_Table[]={};//数码表
unsigned char Seg_Buff[8]={};//数码管缓存
unsigned char keyval=0,keyold=0,keyup,keydown;//按键相关
void key_scan(){
...
}
void Key_Loop(){
...
}
void seg(unsigned char addr,num){
...
}
void Seg_Loop(){
...
}
void ui0(){
Seg_Buff[7]=N;
...
Seg_Buff[0]=M;
}
........
void uin(){
Seg_Buff[7]=N;
...
Seg_Buff[0]=M;
}
void seg_ui(){
switch(UI){
case 0: ui0(); break;
...
case n: uin(); break;
}
}//界面选择
更多推荐



所有评论(0)