蓝侨杯单片机编程笔记胡昶威.docx
《蓝侨杯单片机编程笔记胡昶威.docx》由会员分享,可在线阅读,更多相关《蓝侨杯单片机编程笔记胡昶威.docx(36页珍藏版)》请在冰豆网上搜索。
蓝侨杯单片机编程笔记胡昶威
蓝桥杯单片机编程笔记
一点说明
本笔记为准备第六届蓝桥杯单片机组省赛时写下,其中知识点不乏有所错漏、理解偏颇,但都是经实际测试代码,仅用于应付比赛使用。
作者:
胡昶威
一、IO口编程1
二、数码管动态扫描和定时器3
三、矩阵键盘5
四、串口通讯和串口中断9
五、外部中断的使用14
六、实时时钟DS1302的使用14
七、PCF8591与IIC总线的使用16
八、DS18B20温度芯片的使用22
九、超声波传感器的使用23
十、步进电机与直流电机的使用26
十一、扩展:
宏定义编程方法(推荐)29
十二、注意事项(常见编程错误)33
一、IO口编程
IO编程,该开发板使用了573锁存器,通过P2口的5,6,7位连接3-8译码器,扩展出了8个口,其中4个口分别连接4个573锁存器,这里以LED的锁存器来举例:
原理图573:
分析代码:
P2=((P2&0x1f)|0x80);
其中0x1f=0001 1111,P2与0x1f进行与运算,高三位清零,其余位保持原来状态,不改变,即把控制3-8译码器的高三位留出来:
接着再或上0x80;容易发现0x80=1000 0000;或运算,与1或结果为1,与0或结果不变,所以或上0x80只需看P2的高三位,则高三位为100,对应3-8译码器的话,P2^7=1;P2^6=0;P2^5=0;
所以输出Y4=0;Y4再经过与非运算,看下图示:
则输出Y4C=1;即LED对应的锁存器的片选信号被选中,锁存器打通,接下来就可以对P0口进行操作,操作完之后,
P2=P2&0x1f;P2高三位直接清零,此时Y4C=0,则把锁存器锁上了。
类似的方法,数码管、蜂鸣器等都是如此操作,
选中锁存器代码:
P2=((P2&0x1f)|(这里填对应锁存器的位移号))。
二、数码管动态扫描和定时器
数码管显示分为段选和位选,
数码管定义和显示函数:
codeunsignedchartab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
unsignedchardspbuf[]={10,10,10,10,10,10,10,10};
unsignedchardspcom=0;
voiddisplay()
{
//段选,消隐
P2=((P2&0x1f)|0xe0);
P0=0xff;
P2=P2&0x1f;
//位选
P2=((P2&0x1f)|0xc0);
P0=(1<P2=P2&0x1f;//段码输入P2=((P2&0x1f)|0xe0);P0=tab[dspbuf[dspcom]];P2=P2&0x1f;if(++dspcom==8)dspcom=0;}注意:这里1左移dspcom位,刚开始dspcom=0,则1左移dspcom位依旧为1,接着dspcom每次自增1,1对应二进制00000001,即把1每次向左移,每次都比上一次多移一位,直至8位移完,对应8个数码管。定时器配置:这里只需记住定时器的配置,知道怎么使用就可以了。首先有两个定时器,T0和T1,(也有的单片机有T2),定时器有4种工作方式0,1,2,3;其中最常用的是方式1(16位),其次是方式2(8位自动重装,串口通讯中断会用到)。定时器需要配置:TMOD|=0x01;配置成使用定时器0,工作方式为1;同理使用定时器1工作方式1:TMOD|=0x10;则同时使用两个定时器且工作方式为1,那么可以:TMOD|=0x11;定时器1配置成工作方式2:TMOD|=0x20;接着配置(以定时器0举例):TH0=(65535-2000)/256;//配置初值TL0=(65535-2000)%256;ET0=1;TR0=1;//定时0中断EA=1;//总中断定时器1也是同理的,只不过0要改成1.接着定时中断函数和优先级:定时器0voidisr_timer_0(void)interrupt1//默认中断优先级1{TH0=(65536-2000)/256;TL0=(65536-2000)%256;//定时器重载display();}定时器1:voidisr_timer_1(void)interrupt3//默认中断优先级3{TH0=(65536-2000)/256;TL0=(65536-2000)%256;//定时器重载display();}注意:定时器0优先级为1,定时器1为3,串口中断优先级为4,总共有5个中断源,后面还会介绍外部中断和串口中断。数码管动态扫描,显示函数放在定时中断函数里面,2ms扫一次是最稳定的!!三、矩阵键盘矩阵键盘需要死记了!这里不再讲独立键盘。第二种单片机键盘扫描代码(没有消抖):sfrP4^4=0xC0;//键盘定义sbitr1=P3^0;//4行sbitr2=P3^1;sbitr3=P3^2;sbitr4=P3^3;//4列sbitc1=P4^4;sbitc2=P4^2;sbitc3=P3^5;sbitc4=P3^4;//读取矩阵键盘键值unsignedcharkey_scan(){unsignedcharkey_value;r1=0;r2=r3=r4=1;c1=c2=c3=c4=1;if(!c1)key_value=0;elseif(!c2)key_value=1;elseif(!c3)key_value=2;elseif(!c4)key_value=3;r2=0;r1=r3=r4=1;c1=c2=c3=c4=1;if(!c1)key_value=4;elseif(!c2)key_value=5;elseif(!c3)key_value=6;elseif(!c4)key_value=7;r3=0;r2=r1=r4=1;c1=c2=c3=c4=1;if(!c1)key_value=8;elseif(!c2)key_value=9;elseif(!c3)key_value=10;elseif(!c4)key_value=11;r4=0;r2=r3=r1=1;c1=c2=c3=c4=1;if(!c1)key_value=12;elseif(!c2)key_value=13;elseif(!c3)key_value=14;elseif(!c4)key_value=15;returnkey_value;}四、串口通讯和串口中断串口中断配置只需记住几个寄存器就行了,初始化:SCON=0x50;//串口配置成模式1TMOD|=0x20;//定时器1,方式2,8位自动重装TH1=256-(unsigbedchar)(SYSTEMCLOK/BAUDRATE/384+0.5);//定时初值ES=1;//串口中断打开TR1=1;//启动定时器1EA=1;//总中断打开这里必须使用定时器1,不能用定时器0.下面是模块化的函数:voidUart_Init(){SCON=0x50;TMOD|=0x20;TH1=256-(SYSREMCLOCK/BAUDRATE/384+0.5);ES=1;TR1=1;EA=1;}voidUartSend(unsignedchar*pBuff,intlength){unsignedcharc;inti=0;for(i=0;i{c=pBuff[i];SBUF=c;while(TI==0);TI=0;}}接收数据可以这样写:定义全局变量:unsignedcharuart_buf[100];//串口缓冲区unsignedintuart_Count=0;//串口数据长度voiduart_inte()interrupt4{unsignedcharc;if(RI){RI=0;c=SBUF;uart_buf[uart_Count]=c;uart_Count++;}}如果可以指定的接收,可以这样写//串口中断服务函数voidisr_uart(void)interrupt4{if(RI){RI=0;//清除接收标志位rxbuf[rxcnt]=SBUF;if(rxbuf[rxcnt]=='\n'){rxcnt=0;rx_over=1;ES=0;//回车为接收结束标志,检测到回车符后,关闭串口中断}else{rxcnt++;}}}当接收完一帧数据时关闭串口中断,设一个标志位,处理完之后再打开。#include"reg51.h"#include"intrins.h"typedefunsignedcharBYTE;typedefunsignedintWORD;BYTEcode_tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff};chararry[10]="ICANPLAY";unsignedcharx;#defineBAUD115200//串口波特率#defineNONE_PARITY0//无校验#defineODD_PARITY1//奇校验#defineEVEN_PARITY2//偶校验#defineMARK_PARITY3//标记校验#defineSPACE_PARITY4//空白校验#definePARITYBITNONE_PARITY//定义校验位sfrAUXR=0x8e;//辅助寄存器sfrP_SW1=0xA2;//外设功能切换寄存器1#defineS1_S00x40//P_SW1.6#defineS1_S10x80//P_SW1.7sbitP22=P2^2;bitbusy;voidSendData(BYTEdat);voidSendString(char*s);voidmain(){ACC=P_SW1;ACC&=~(S1_S0|S1_S1);//S1_S0=0S1_S1=0P_SW1=ACC;//(P3.0/RxD,P3.1/TxD)//ACC=P_SW1;//ACC&=~(S1_S0|S1_S1);//S1_S0=1S1_S1=0//ACC|=S1_S0;//(P3.6/RxD_2,P3.7/TxD_2)//P_SW1=ACC;////ACC=P_SW1;//ACC&=~(S1_S0|S1_S1);//S1_S0=0S1_S1=1//ACC|=S1_S1;//(P1.6/RxD_3,P1.7/TxD_3)//P_SW1=ACC;//#if(PARITYBIT==NONE_PARITY)SCON=0x50;//8位可变波特率//#elif(PARITYBIT==ODD_PARITY)||(PARITYBIT==EVEN_PARITY)||(PARITYBIT==MARK_PARITY)//SCON=0xda;//9位可变波特率,校验位初始为1//#elif(PARITYBIT==SPACE_PARITY)//SCON=0xd2;//9位可变波特率,校验位初始为0//#endifAUXR=0x40;//定时器1为1T模式TMOD=0x20;//定时器1为模式2(8位自动重载)TL1=(256-(FOSC/32/BAUD));//设置波特率重装值TH1=(256-(FOSC/32/BAUD));TR1=1;//定时器1开始工作ES=1;//使能串口中断EA=1;while(1){//SendString(arry);SendString("ICANPLAY~~\r\n");//上位机显示接收文本模式//SendData(x);}}/*----------------------------UART中断服务程序-----------------------------*/voidUart()interrupt4using1{if(RI)//单片机接收数据,发送数字0~9,可在数码管上显示,发送hex模式{RI=0;//清除RI位//P0=SBUF;x=SBUF;//将缓存器的数据赋值给xP0=0xff;//消隐P2|=0xe0;P2&=0x1f;P0=code_tab[x];//段选P2|=0xe0;P2&=0x1f;P0=0x01;//位选第一位P2|=0xc0;P2&=0x3f;}if(TI){TI=0;//清除TI位busy=0;//清忙标志}}/*----------------------------发送串口数据----------------------------*/voidSendData(BYTEdat){while(busy);//等待前面的数据发送完成ACC=dat;//获取校验位P(PSW.0)if(P)//根据P来设置校验位{#if(PARITYBIT==ODD_PARITY)TB8=0;//设置校验位为0#elif(PARITYBIT==EVEN_PARITY)TB8=1;//设置校验位为1#endif}else{#if(PARITYBIT==ODD_PARITY)TB8=1;//设置校验位为1#elif(PARITYBIT==EVEN_PARITY)TB8=0;//设置校验位为0#endif}busy=1;SBUF=ACC;//写数据到UART数据寄存器}/*----------------------------发送字符串----------------------------*/voidSendString(char*s){while(*s)//检测字符串结束标志{SendData(*s++);//发送当前字符}}记不住可以看手册!!#include"reg51.h"#include"intrins.h"typedefunsignedcharBYTE;typedefunsignedintWORD;#defineBAUD115200sfrAUXR=0x8e;//辅助寄存器sbitP22=P2^2;bitbusy;voidSendData(BYTEdat);voidSendString(char*s);voidmain(){SCON=0x50;AUXR=0x40;//设置定时器T1为1T,即一个机器周期模式TMOD=0x20;TL1=(256-(FOSC/32/BAUD));TH1=(256-(FOSC/32/BAUD));TR1=1;ES=1;EA=1;SendString("Hello");while(1);}voidUart()interrupt4using1{if(RI){RI=0;P0=SBUF;}if(TI){TI=0;busy=0;}}voidSendData(BYTEdat){while(busy);busy=1;SBUF=dat;}voidSendString(char*s){while(*s){SendData(*s++);}}五、外部中断的使用#includesbitL1=P0^0;intmain(){IT0=1;//IT0=1,下降沿触发外部中断0,IT0=0边沿触发EX0=1;//使用外部中断0EA=1;while(1){}}voidEx_int0()interrupt0//外部中断优先级最高{P2=((P2&0x1f)|0x80);L1=~L1;P2=(P2&0x1f);}其中,外部中断的引脚控制是P3^2,P3^3,即对应独立按键的S5,S4。六、实时时钟DS1302的使用蓝桥杯提供函数,解释为:里面的命令和写入的数据可以看芯片手册:左侧的READ、WRITE分别是读写的命令,BIT7-BIT0是要写入的数据,根据需要进行配置。DS1302只需记住这两个函数即可:Write_Ds1302(,)与Read_Ds1302(x),配置看手册。重点:芯片表说明:第一行:秒->因为秒的范围是0-59,所以6,5,4位表示秒的十位,3,2,1,0表示个位,十位最大是5,所以三位即可。第二行:跟上面一样;第三行:7位:1为12小时制,0为24小时制;5位:12小时制时为0表示上午,1表示下午,24小时制时,和4位一起表示小时的十位;其余的时间一样的表示。倒数第二行:只看7位:为1时禁止写数据,所以开始写数据时必须置0;读数时:!!需要加“写操作这一行代码”。读的话直接按照命令读即可。DS1302进阶(BCD码转换):解决之前60秒不能进位的问题。1)写入初始值时,要把10十进制数转换为BCD码,例:写入时间->17:58:50Ds1302_Single_Byte_Write(0x8e,0x00);//写操作Ds1302_Single_Byte_Write(0x85,((17/10)<<4|(17%10)));//写时Ds1302_Single_Byte_Write(0x83,((58/10)<<4|(58%10)));//写分Ds1302_Single_Byte_Write(0x81,((50/10)<<4|(50%10)));//写秒Ds1302_Single_Byte_Write(0x8e,0x80);//写保护即转换的公式是:((Value/10)<<4|(Value%10)),可以写一个settime()函数。2)读数:读回来的数要进行转换成十进制数((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);八进制转十进制->ReadValue=Ds1302_Single_Byte_Read(0x85);hour=((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);!!(这句一定不要省)Ds1302_Single_Byte_Write(0x00,0x00);//写操作ReadValue=Ds1302_Single_Byte_Read(0x83);minute=((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);Ds1302_Single_Byte_Write(0x00,0x00);//写操作ReadValue=Ds1302_Single_Byte_Read(0x81);sec=((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);Ds1302_Single_Byte_Write(0x00,0x00);//写操作显示:dspbuf[0]=hour/10;dspbuf[1]=hour%10;dspbuf[2]=minute/10;dspbuf[3]=minute%10;dspbuf[4]=sec/10;dspbuf[5]=sec%10;七、PCF8591与IIC总线的使用(1)IIC总线的使用:比赛提供了IIC的两个库文件,IIC.h;IIC.c,其中需要注意的函数是:其中,该函数是初始化的,当使用AD转换的时候需要在main函数开始时调用,该函数内部只需看这句代码即可:i2c_sendbyte(0x03);//ADC通道3,板上有4个模拟输入口,分别为0,1,2,3;设置哪一个模拟输入口就是根据这句代码,0x03表示通道3,这是根据芯片手册配置的,如图:8位前6位不用管,都为0,最后两位就是配置选择哪一个通道的。第二个函数:读取AD转换后的数值,这个函数直接调用就可以了,函数内部如何实现不用管,但是需要注意的是:该函数扫描调用最好是100ms。第三个函数,上面的都是AD转换,即模拟信号转数字信号,下面这个函数是DA转换,数字信号转换成模拟信号,就是单片机输出数字信号,用万能表去量单片机引出的引脚,量一下电压大小,这个估计比赛不会考,不过预防万一:该函数和上面两个函数分离开来的,一、二函数是要在一起使用,初始化后之后才能调用,第三个加入头文件,直接调用即可,比较简单!!!!上面说法有误,A/D转换的初始化函数和读取转换后的数值都需要自己写。这里了解一下PCF8591只需根据时序格式发送地址字节和控制字节:,这是地址字节,其中A2,A1,A0硬件已经接地,故都为0,最低位表示的是你要从IIC总线上读数还是写数据,1表示读,0表示写,即读数据发的地址是:0x91;写数据发的地址是0x90;控制字节:由芯片资料知,控制字节有8位,有两位固定是0,除了第0、1位需要自己设置,其他的我们都设为0,那些位都是一些具体的功能,我们暂时用不着,不用管先,第0、1位是模拟通道选择,PCF8591上提供了4路模拟通道,根据需求进行选择,如选择通道3即发送控制字节:0x03;地址字节和控制字节都明白了,接下来根据时序要求进行配置,A/D转换需要一个初始化函数:Init_ADpcf8591();
//段码输入
P0=tab[dspbuf[dspcom]];
if(++dspcom==8)
dspcom=0;
}注意:
这里1左移dspcom位,刚开始dspcom=0,则1左移dspcom位依旧为1,接着dspcom每次自增1,1对应二进制00000001,即把1每次向左移,每次都比上一次多移一位,直至8位移完,对应8个数码管。
定时器配置:
这里只需记住定时器的配置,知道怎么使用就可以了。
首先有两个定时器,T0和T1,(也有的单片机有T2),定时器有4种工作方式0,1,2,3;其中最常用的是方式1(16位),其次是方式2(8位自动重装,串口通讯中断会用到)。
定时器需要配置:
TMOD|=0x01;配置成使用定时器0,工作方式为1;同理使用定时器1工作方式1:
TMOD|=0x10;则同时使用两个定时器且工作方式为1,那么可以:
TMOD|=0x11;
定时器1配置成工作方式2:
TMOD|=0x20;
接着配置(以定时器0举例):
TH0=(65535-2000)/256;//配置初值
TL0=(65535-2000)%256;
ET0=1;
TR0=1;//定时0中断
EA=1;//总中断
定时器1也是同理的,只不过0要改成1.
接着定时中断函数和优先级:
定时器0
voidisr_timer_0(void)interrupt1//默认中断优先级1
TH0=(65536-2000)/256;
TL0=(65536-2000)%256;//定时器重载
display();
}
定时器1:
voidisr_timer_1(void)interrupt3//默认中断优先级3
注意:
定时器0优先级为1,定时器1为3,串口中断优先级为4,总共有5个中断源,后面还会介绍外部中断和串口中断。
数码管动态扫描,显示函数放在定时中断函数里面,2ms扫一次是最稳定的!
!
三、矩阵键盘
矩阵键盘需要死记了!
这里不再讲独立键盘。
第二种单片机键盘扫描代码(没有消抖):
sfrP4^4=0xC0;
//键盘定义
sbitr1=P3^0;//4行
sbitr2=P3^1;
sbitr3=P3^2;
sbitr4=P3^3;
//4列
sbitc1=P4^4;
sbitc2=P4^2;
sbitc3=P3^5;
sbitc4=P3^4;
//读取矩阵键盘键值
unsignedcharkey_scan()
unsignedcharkey_value;
r1=0;
r2=r3=r4=1;
c1=c2=c3=c4=1;
if(!
c1)key_value=0;
elseif(!
c2)key_value=1;
c3)key_value=2;
c4)key_value=3;
r2=0;
r1=r3=r4=1;
c1)key_value=4;
c2)key_value=5;
c3)key_value=6;
c4)key_value=7;
r3=0;
r2=r1=r4=1;
c1)key_value=8;
c2)key_value=9;
c3)key_value=10;
c4)key_value=11;
r4=0;
r2=r3=r1=1;
c1)key_value=12;
c2)key_value=13;
c3)key_value=14;
c4)key_value=15;
returnkey_value;
四、串口通讯和串口中断
串口中断配置只需记住几个寄存器就行了,
初始化:
SCON=0x50;//串口配置成模式1
TMOD|=0x20;//定时器1,方式2,8位自动重装
TH1=256-(unsigbedchar)(SYSTEMCLOK/BAUDRATE/384+0.5);//定时初值
ES=1;//串口中断打开
TR1=1;//启动定时器1
EA=1;//总中断打开
这里必须使用定时器1,不能用定时器0.
下面是模块化的函数:
voidUart_Init()
SCON=0x50;
TH1=256-(SYSREMCLOCK/BAUDRATE/384+0.5);
ES=1;
TR1=1;
EA=1;
voidUartSend(unsignedchar*pBuff,intlength)
unsignedcharc;
inti=0;
for(i=0;i{c=pBuff[i];SBUF=c;while(TI==0);TI=0;}}接收数据可以这样写:定义全局变量:unsignedcharuart_buf[100];//串口缓冲区unsignedintuart_Count=0;//串口数据长度voiduart_inte()interrupt4{unsignedcharc;if(RI){RI=0;c=SBUF;uart_buf[uart_Count]=c;uart_Count++;}}如果可以指定的接收,可以这样写//串口中断服务函数voidisr_uart(void)interrupt4{if(RI){RI=0;//清除接收标志位rxbuf[rxcnt]=SBUF;if(rxbuf[rxcnt]=='\n'){rxcnt=0;rx_over=1;ES=0;//回车为接收结束标志,检测到回车符后,关闭串口中断}else{rxcnt++;}}}当接收完一帧数据时关闭串口中断,设一个标志位,处理完之后再打开。#include"reg51.h"#include"intrins.h"typedefunsignedcharBYTE;typedefunsignedintWORD;BYTEcode_tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff};chararry[10]="ICANPLAY";unsignedcharx;#defineBAUD115200//串口波特率#defineNONE_PARITY0//无校验#defineODD_PARITY1//奇校验#defineEVEN_PARITY2//偶校验#defineMARK_PARITY3//标记校验#defineSPACE_PARITY4//空白校验#definePARITYBITNONE_PARITY//定义校验位sfrAUXR=0x8e;//辅助寄存器sfrP_SW1=0xA2;//外设功能切换寄存器1#defineS1_S00x40//P_SW1.6#defineS1_S10x80//P_SW1.7sbitP22=P2^2;bitbusy;voidSendData(BYTEdat);voidSendString(char*s);voidmain(){ACC=P_SW1;ACC&=~(S1_S0|S1_S1);//S1_S0=0S1_S1=0P_SW1=ACC;//(P3.0/RxD,P3.1/TxD)//ACC=P_SW1;//ACC&=~(S1_S0|S1_S1);//S1_S0=1S1_S1=0//ACC|=S1_S0;//(P3.6/RxD_2,P3.7/TxD_2)//P_SW1=ACC;////ACC=P_SW1;//ACC&=~(S1_S0|S1_S1);//S1_S0=0S1_S1=1//ACC|=S1_S1;//(P1.6/RxD_3,P1.7/TxD_3)//P_SW1=ACC;//#if(PARITYBIT==NONE_PARITY)SCON=0x50;//8位可变波特率//#elif(PARITYBIT==ODD_PARITY)||(PARITYBIT==EVEN_PARITY)||(PARITYBIT==MARK_PARITY)//SCON=0xda;//9位可变波特率,校验位初始为1//#elif(PARITYBIT==SPACE_PARITY)//SCON=0xd2;//9位可变波特率,校验位初始为0//#endifAUXR=0x40;//定时器1为1T模式TMOD=0x20;//定时器1为模式2(8位自动重载)TL1=(256-(FOSC/32/BAUD));//设置波特率重装值TH1=(256-(FOSC/32/BAUD));TR1=1;//定时器1开始工作ES=1;//使能串口中断EA=1;while(1){//SendString(arry);SendString("ICANPLAY~~\r\n");//上位机显示接收文本模式//SendData(x);}}/*----------------------------UART中断服务程序-----------------------------*/voidUart()interrupt4using1{if(RI)//单片机接收数据,发送数字0~9,可在数码管上显示,发送hex模式{RI=0;//清除RI位//P0=SBUF;x=SBUF;//将缓存器的数据赋值给xP0=0xff;//消隐P2|=0xe0;P2&=0x1f;P0=code_tab[x];//段选P2|=0xe0;P2&=0x1f;P0=0x01;//位选第一位P2|=0xc0;P2&=0x3f;}if(TI){TI=0;//清除TI位busy=0;//清忙标志}}/*----------------------------发送串口数据----------------------------*/voidSendData(BYTEdat){while(busy);//等待前面的数据发送完成ACC=dat;//获取校验位P(PSW.0)if(P)//根据P来设置校验位{#if(PARITYBIT==ODD_PARITY)TB8=0;//设置校验位为0#elif(PARITYBIT==EVEN_PARITY)TB8=1;//设置校验位为1#endif}else{#if(PARITYBIT==ODD_PARITY)TB8=1;//设置校验位为1#elif(PARITYBIT==EVEN_PARITY)TB8=0;//设置校验位为0#endif}busy=1;SBUF=ACC;//写数据到UART数据寄存器}/*----------------------------发送字符串----------------------------*/voidSendString(char*s){while(*s)//检测字符串结束标志{SendData(*s++);//发送当前字符}}记不住可以看手册!!#include"reg51.h"#include"intrins.h"typedefunsignedcharBYTE;typedefunsignedintWORD;#defineBAUD115200sfrAUXR=0x8e;//辅助寄存器sbitP22=P2^2;bitbusy;voidSendData(BYTEdat);voidSendString(char*s);voidmain(){SCON=0x50;AUXR=0x40;//设置定时器T1为1T,即一个机器周期模式TMOD=0x20;TL1=(256-(FOSC/32/BAUD));TH1=(256-(FOSC/32/BAUD));TR1=1;ES=1;EA=1;SendString("Hello");while(1);}voidUart()interrupt4using1{if(RI){RI=0;P0=SBUF;}if(TI){TI=0;busy=0;}}voidSendData(BYTEdat){while(busy);busy=1;SBUF=dat;}voidSendString(char*s){while(*s){SendData(*s++);}}五、外部中断的使用#includesbitL1=P0^0;intmain(){IT0=1;//IT0=1,下降沿触发外部中断0,IT0=0边沿触发EX0=1;//使用外部中断0EA=1;while(1){}}voidEx_int0()interrupt0//外部中断优先级最高{P2=((P2&0x1f)|0x80);L1=~L1;P2=(P2&0x1f);}其中,外部中断的引脚控制是P3^2,P3^3,即对应独立按键的S5,S4。六、实时时钟DS1302的使用蓝桥杯提供函数,解释为:里面的命令和写入的数据可以看芯片手册:左侧的READ、WRITE分别是读写的命令,BIT7-BIT0是要写入的数据,根据需要进行配置。DS1302只需记住这两个函数即可:Write_Ds1302(,)与Read_Ds1302(x),配置看手册。重点:芯片表说明:第一行:秒->因为秒的范围是0-59,所以6,5,4位表示秒的十位,3,2,1,0表示个位,十位最大是5,所以三位即可。第二行:跟上面一样;第三行:7位:1为12小时制,0为24小时制;5位:12小时制时为0表示上午,1表示下午,24小时制时,和4位一起表示小时的十位;其余的时间一样的表示。倒数第二行:只看7位:为1时禁止写数据,所以开始写数据时必须置0;读数时:!!需要加“写操作这一行代码”。读的话直接按照命令读即可。DS1302进阶(BCD码转换):解决之前60秒不能进位的问题。1)写入初始值时,要把10十进制数转换为BCD码,例:写入时间->17:58:50Ds1302_Single_Byte_Write(0x8e,0x00);//写操作Ds1302_Single_Byte_Write(0x85,((17/10)<<4|(17%10)));//写时Ds1302_Single_Byte_Write(0x83,((58/10)<<4|(58%10)));//写分Ds1302_Single_Byte_Write(0x81,((50/10)<<4|(50%10)));//写秒Ds1302_Single_Byte_Write(0x8e,0x80);//写保护即转换的公式是:((Value/10)<<4|(Value%10)),可以写一个settime()函数。2)读数:读回来的数要进行转换成十进制数((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);八进制转十进制->ReadValue=Ds1302_Single_Byte_Read(0x85);hour=((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);!!(这句一定不要省)Ds1302_Single_Byte_Write(0x00,0x00);//写操作ReadValue=Ds1302_Single_Byte_Read(0x83);minute=((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);Ds1302_Single_Byte_Write(0x00,0x00);//写操作ReadValue=Ds1302_Single_Byte_Read(0x81);sec=((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);Ds1302_Single_Byte_Write(0x00,0x00);//写操作显示:dspbuf[0]=hour/10;dspbuf[1]=hour%10;dspbuf[2]=minute/10;dspbuf[3]=minute%10;dspbuf[4]=sec/10;dspbuf[5]=sec%10;七、PCF8591与IIC总线的使用(1)IIC总线的使用:比赛提供了IIC的两个库文件,IIC.h;IIC.c,其中需要注意的函数是:其中,该函数是初始化的,当使用AD转换的时候需要在main函数开始时调用,该函数内部只需看这句代码即可:i2c_sendbyte(0x03);//ADC通道3,板上有4个模拟输入口,分别为0,1,2,3;设置哪一个模拟输入口就是根据这句代码,0x03表示通道3,这是根据芯片手册配置的,如图:8位前6位不用管,都为0,最后两位就是配置选择哪一个通道的。第二个函数:读取AD转换后的数值,这个函数直接调用就可以了,函数内部如何实现不用管,但是需要注意的是:该函数扫描调用最好是100ms。第三个函数,上面的都是AD转换,即模拟信号转数字信号,下面这个函数是DA转换,数字信号转换成模拟信号,就是单片机输出数字信号,用万能表去量单片机引出的引脚,量一下电压大小,这个估计比赛不会考,不过预防万一:该函数和上面两个函数分离开来的,一、二函数是要在一起使用,初始化后之后才能调用,第三个加入头文件,直接调用即可,比较简单!!!!上面说法有误,A/D转换的初始化函数和读取转换后的数值都需要自己写。这里了解一下PCF8591只需根据时序格式发送地址字节和控制字节:,这是地址字节,其中A2,A1,A0硬件已经接地,故都为0,最低位表示的是你要从IIC总线上读数还是写数据,1表示读,0表示写,即读数据发的地址是:0x91;写数据发的地址是0x90;控制字节:由芯片资料知,控制字节有8位,有两位固定是0,除了第0、1位需要自己设置,其他的我们都设为0,那些位都是一些具体的功能,我们暂时用不着,不用管先,第0、1位是模拟通道选择,PCF8591上提供了4路模拟通道,根据需求进行选择,如选择通道3即发送控制字节:0x03;地址字节和控制字节都明白了,接下来根据时序要求进行配置,A/D转换需要一个初始化函数:Init_ADpcf8591();
c=pBuff[i];
SBUF=c;
while(TI==0);
TI=0;
接收数据可以这样写:
定义全局变量:
unsignedcharuart_buf[100];//串口缓冲区
unsignedintuart_Count=0;//串口数据长度
voiduart_inte()interrupt4
if(RI)
RI=0;
c=SBUF;
uart_buf[uart_Count]=c;
uart_Count++;
如果可以指定的接收,可以这样写
//串口中断服务函数
voidisr_uart(void)interrupt4{
if(RI){
RI=0;//清除接收标志位
rxbuf[rxcnt]=SBUF;
if(rxbuf[rxcnt]=='\n'){
rxcnt=0;
rx_over=1;
ES=0;
//回车为接收结束标志,检测到回车符后,关闭串口中断
else{
rxcnt++;
当接收完一帧数据时关闭串口中断,设一个标志位,处理完之后再打开。
#include"reg51.h"
#include"intrins.h"
typedefunsignedcharBYTE;
typedefunsignedintWORD;
BYTEcode_tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff};
chararry[10]="ICANPLAY";
unsignedcharx;
#defineBAUD115200//串口波特率
#defineNONE_PARITY0//无校验
#defineODD_PARITY1//奇校验
#defineEVEN_PARITY2//偶校验
#defineMARK_PARITY3//标记校验
#defineSPACE_PARITY4//空白校验
#definePARITYBITNONE_PARITY//定义校验位
sfrAUXR=0x8e;//辅助寄存器
sfrP_SW1=0xA2;//外设功能切换寄存器1
#defineS1_S00x40//P_SW1.6
#defineS1_S10x80//P_SW1.7
sbitP22=P2^2;
bitbusy;
voidSendData(BYTEdat);
voidSendString(char*s);
voidmain()
ACC=P_SW1;
ACC&=~(S1_S0|S1_S1);//S1_S0=0S1_S1=0
P_SW1=ACC;//(P3.0/RxD,P3.1/TxD)
//ACC=P_SW1;
//ACC&=~(S1_S0|S1_S1);//S1_S0=1S1_S1=0
//ACC|=S1_S0;//(P3.6/RxD_2,P3.7/TxD_2)
//P_SW1=ACC;
//
//ACC&=~(S1_S0|S1_S1);//S1_S0=0S1_S1=1
//ACC|=S1_S1;//(P1.6/RxD_3,P1.7/TxD_3)
//#if(PARITYBIT==NONE_PARITY)
SCON=0x50;//8位可变波特率
//#elif(PARITYBIT==ODD_PARITY)||(PARITYBIT==EVEN_PARITY)||(PARITYBIT==MARK_PARITY)
//SCON=0xda;//9位可变波特率,校验位初始为1
//#elif(PARITYBIT==SPACE_PARITY)
//SCON=0xd2;//9位可变波特率,校验位初始为0
//#endif
AUXR=0x40;//定时器1为1T模式
TMOD=0x20;//定时器1为模式2(8位自动重载)
TL1=(256-(FOSC/32/BAUD));//设置波特率重装值
TH1=(256-(FOSC/32/BAUD));
TR1=1;//定时器1开始工作
ES=1;//使能串口中断
while
(1)
//SendString(arry);
SendString("ICANPLAY~~\r\n");//上位机显示接收文本模式
//SendData(x);
/*----------------------------
UART中断服务程序
-----------------------------*/
voidUart()interrupt4using1
if(RI)//单片机接收数据,发送数字0~9,可在数码管上显示,发送hex模式
RI=0;//清除RI位
//P0=SBUF;
x=SBUF;//将缓存器的数据赋值给x
P0=0xff;//消隐
P2|=0xe0;
P2&=0x1f;
P0=code_tab[x];//段选
P0=0x01;//位选第一位
P2|=0xc0;
P2&=0x3f;
if(TI)
TI=0;//清除TI位
busy=0;//清忙标志
发送串口数据
----------------------------*/
voidSendData(BYTEdat)
while(busy);//等待前面的数据发送完成
ACC=dat;//获取校验位P(PSW.0)
if(P)//根据P来设置校验位
#if(PARITYBIT==ODD_PARITY)
TB8=0;//设置校验位为0
#elif(PARITYBIT==EVEN_PARITY)
TB8=1;//设置校验位为1
#endif
else
busy=1;
SBUF=ACC;//写数据到UART数据寄存器
发送字符串
voidSendString(char*s)
while(*s)//检测字符串结束标志
SendData(*s++);//发送当前字符
记不住可以看手册!
#defineBAUD115200
AUXR=0x40;//设置定时器T1为1T,即一个机器周期模式
TMOD=0x20;
TL1=(256-(FOSC/32/BAUD));
SendString("Hello");
(1);
P0=SBUF;
busy=0;
while(busy);
SBUF=dat;
while(*s)
SendData(*s++);
五、外部中断的使用
#include
sbitL1=P0^0;
intmain(){
IT0=1;//IT0=1,下降沿触发外部中断0,IT0=0边沿触发
EX0=1;//使用外部中断0
(1){
voidEx_int0()interrupt0//外部中断优先级最高
P2=((P2&0x1f)|0x80);
L1=~L1;
P2=(P2&0x1f);
其中,外部中断的引脚控制是P3^2,P3^3,即对应独立按键的S5,S4。
六、实时时钟DS1302的使用
蓝桥杯提供函数,解释为:
里面的命令和写入的数据可以看芯片手册:
左侧的READ、WRITE分别是读写的命令,BIT7-BIT0是要写入的数据,根据需要进行配置。
DS1302只需记住这两个函数即可:
Write_Ds1302(,)与Read_Ds1302(x),配置看手册。
重点:
芯片表说明:
第一行:
秒->因为秒的范围是0-59,所以6,5,4位表示秒的十位,3,2,1,0表示个位,十位最大是5,所以三位即可。
第二行:
跟上面一样;
第三行:
7位:
1为12小时制,0为24小时制;5位:
12小时制时为0表示上午,1表示下午,24小时制时,和4位一起表示小时的十位;
其余的时间一样的表示。
倒数第二行:
只看7位:
为1时禁止写数据,所以开始写数据时必须置0;
读数时:
需要加“写操作这一行代码”。
读的话直接按照命令读即可。
DS1302进阶(BCD码转换):
解决之前60秒不能进位的问题。
1)写入初始值时,要把10十进制数转换为BCD码,
例:
写入时间->17:
58:
50
Ds1302_Single_Byte_Write(0x8e,0x00);//写操作
Ds1302_Single_Byte_Write(0x85,((17/10)<<4|(17%10)));//写时
Ds1302_Single_Byte_Write(0x83,((58/10)<<4|(58%10)));//写分
Ds1302_Single_Byte_Write(0x81,((50/10)<<4|(50%10)));//写秒
Ds1302_Single_Byte_Write(0x8e,0x80);//写保护
即转换的公式是:
((Value/10)<<4|(Value%10)),可以写一个settime()函数。
2)读数:
读回来的数要进行转换成十进制数
((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);
八进制转十进制->
ReadValue=Ds1302_Single_Byte_Read(0x85);
hour=((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);
(这句一定不要省)Ds1302_Single_Byte_Write(0x00,0x00);//写操作
ReadValue=Ds1302_Single_Byte_Read(0x83);
minute=((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);
Ds1302_Single_Byte_Write(0x00,0x00);//写操作
ReadValue=Ds1302_Single_Byte_Read(0x81);
sec=((ReadValue&0x70)>>4)*10+(ReadValue&0x0F);
显示:
dspbuf[0]=hour/10;
dspbuf[1]=hour%10;
dspbuf[2]=minute/10;
dspbuf[3]=minute%10;
dspbuf[4]=sec/10;
dspbuf[5]=sec%10;
七、PCF8591与IIC总线的使用
(1)IIC总线的使用:
比赛提供了IIC的两个库文件,IIC.h;IIC.c,其中需要注意的函数是:
其中,该函数是初始化的,当使用AD转换的时候需要在main函数开始时调用,该函数内部只需看这句代码即可:
i2c_sendbyte(0x03);//ADC通道3,板上有4个模拟输入口,分别为0,1,2,3;设置哪一个模拟输入口就是根据这句代码,0x03表示通道3,这是根据芯片手册配置的,如图:
8位前6位不用管,都为0,最后两位就是配置选择哪一个通道的。
第二个函数:
读取AD转换后的数值,这个函数直接调用就可以了,函数内部如何实现不用管,但是需要注意的是:
该函数扫描调用最好是100ms。
第三个函数,上面的都是AD转换,即模拟信号转数字信号,下面这个函数是DA转换,数字信号转换成模拟信号,就是单片机输出数字信号,用万能表去量单片机引出的引脚,量一下电压大小,这个估计比赛不会考,不过预防万一:
该函数和上面两个函数分离开来的,一、二函数是要在一起使用,初始化后之后才能调用,第三个加入头文件,直接调用即可,比较简单!
!
上面说法有误,A/D转换的初始化函数和读取转换后的数值都需要自己写。
这里了解一下PCF8591只需根据时序格式发送地址字节和控制字节:
,这是地址字节,其中A2,A1,A0硬件已经接地,故都为0,最低位表示的是你要从IIC总线上读数还是写数据,1表示读,0表示写,即读数据发的地址是:
0x91;写数据发的地址是0x90;
控制字节:
由芯片资料知,控制字节有8位,有两位固定是0,除了第0、1位需要自己设置,其他的我们都设为0,那些位都是一些具体的功能,我们暂时用不着,不用管先,第0、1位是模拟通道选择,PCF8591上提供了4路模拟通道,根据需求进行选择,如选择通道3即发送控制字节:
0x03;
地址字节和控制字节都明白了,接下来根据时序要求进行配置,A/D转换需要一个初始化函数:
Init_ADpcf8591();
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1