蓝侨杯单片机编程笔记胡昶威.docx

上传人:b****8 文档编号:10979681 上传时间:2023-02-24 格式:DOCX 页数:36 大小:158.62KB
下载 相关 举报
蓝侨杯单片机编程笔记胡昶威.docx_第1页
第1页 / 共36页
蓝侨杯单片机编程笔记胡昶威.docx_第2页
第2页 / 共36页
蓝侨杯单片机编程笔记胡昶威.docx_第3页
第3页 / 共36页
蓝侨杯单片机编程笔记胡昶威.docx_第4页
第4页 / 共36页
蓝侨杯单片机编程笔记胡昶威.docx_第5页
第5页 / 共36页
点击查看更多>>
下载资源
资源描述

蓝侨杯单片机编程笔记胡昶威.docx

《蓝侨杯单片机编程笔记胡昶威.docx》由会员分享,可在线阅读,更多相关《蓝侨杯单片机编程笔记胡昶威.docx(36页珍藏版)》请在冰豆网上搜索。

蓝侨杯单片机编程笔记胡昶威.docx

蓝侨杯单片机编程笔记胡昶威

蓝桥杯单片机编程笔记

一点说明

本笔记为准备第六届蓝桥杯单片机组省赛时写下,其中知识点不乏有所错漏、理解偏颇,但都是经实际测试代码,仅用于应付比赛使用。

作者:

胡昶威

一、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.

接着定时中断函数和优先级:

定时器0

voidisr_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;//串口配置成模式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;

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.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=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

//#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;//使能串口中断

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;//将缓存器的数据赋值给x

P0=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;

#defineBAUD115200

sfrAUXR=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++);

}

}

五、外部中断的使用

#include

sbitL1=P0^0;

intmain(){

IT0=1;//IT0=1,下降沿触发外部中断0,IT0=0边沿触发

EX0=1;//使用外部中断0

EA=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:

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);

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();

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 幼儿教育 > 育儿知识

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1