如何玩转PIC16F87xA和LCD6903.docx
- 文档编号:5263726
- 上传时间:2022-12-14
- 格式:DOCX
- 页数:50
- 大小:154.86KB
如何玩转PIC16F87xA和LCD6903.docx
《如何玩转PIC16F87xA和LCD6903.docx》由会员分享,可在线阅读,更多相关《如何玩转PIC16F87xA和LCD6903.docx(50页珍藏版)》请在冰豆网上搜索。
如何玩转PIC16F87xA和LCD6903
学习玩转PIC16F87xA
今天开始介绍自己玩PIC16F87xA的一些体会。
我的学习方法不是最好的,有点象书呆子——实际上我就是在学校工作,不过这种方法非常扎实有效。
这种方法就是先看资料,再设计编程模块,下载调试,最后模块集成构成具有一定功能的小单片机系统。
本次整体的学习的目标是以PIC16F87XA开始,由I/O口控制到Timer、PWM、CCP、ADC等各个模块的应用最后到能驱动常用的LCD显示模块显示ADC的采样结果。
主要的资料是PIC16F87XA的Datasheet。
所有工具:
C开发平台:
采用UltraEdit-32,这是IDMComputerSolutions的软件,此软件用众非常广,下载也方便这里不罗嗦了。
C编译器:
采用PICC的C编译器,版本要求不高,我采用的是V9.8的,网站有类似的可以下载来用。
仿真下载器:
采用PICKIT2/3或廉价的Pick149/Pick150。
PIC16F87xA[873A、874A、876A、877A都可以]的目标板,其上带有ICSP下载接口即可。
为了加快进度,此次采用比较常见的点阵128X64LCD和字符型1602LCD。
实验从基本的I/O口逐步使用内部的各种模块直到最后完成:
电压检测显示、频率检测显示、GPS信号检测显示。
一.开发系统
可以用本网站的任意一款,也可以用如照片上的PICKit2或PICKIT3或PICK149,PICK150等等,采用ICSP5线连接下载器。
PIC单片机最小系统板PICKit2编程器PICKit3编程器
二.了解你的对象规划设计
如何看Datasheet呢?
这是初学者可能需要过得坎——但不是必须的,因为可以完全拿来主义——就我编的程序就可以拿来主义。
我有英文和中文版的,我希望起点为英文版的。
文档为PIC16F873A-EN.pdf。
这个文档适用于873、4、6、7A4种MCU。
他们在命令级上完全一样,仅IO口数量,存储器大小不一样。
文档无法上传,请自己到Microchip公司官网下载。
为了省事,我们所有的讨论都限于PIC16F873A,并且是DIP28封装的——理由很简单,皮实可靠,体积大易焊接和接入电路。
我用的是我自己老早设计的简易板子,非常小巧,除了RA口采用VDD+GND+RAx外,其余的直接IO输出,较新的设计是将除了RA口其余的都串接101~102电阻,以适应不同外设的电压——板载1117-3.3芯片。
芯片技术参数忽略不看、用得着时再翻译。
先看芯片外观图:
Page228-PinPDIP,SOIC,SSOP
有一个粗略的概念:
PIN1、PIN27、PIN28、加上GND、VDD、5线ICSP,PGM没有用,直接接到ICSP上了。
PIN2~PIN5——除了是PA0~4之外,还可以作为AN0~AN4——ADC的输入口,因此首先考虑这些口作为ADC,不作一般I/O口用。
PIN8-VSS-GND
PIN9,PIN10是外接晶振——专用吧
PIN11~PIN18是RC0~RC7I/O口,其中RC0可以作为TICKI——Time1的时钟输入——可以作为计数测频,保留不作简单IO口;
RC1和RC2有CCP1和CCP2的比较器功能,看手册,他们还有PWM输出的功能,保留不作简单IO口;
RC3~RC7有串口的功能,本实验不作SPI内容,因此可以作为普通IO口用
PIN19,PIN20=VSS,VDD
PIN21~PIN28是RB0~RB7——这8个口在不作为ICSP时,可以全部作为I/O口用,其中的RB0可以作为外部中断输入。
接着下边的技术参数可以看看,了解即可。
下边直接看4.0I/OPORTS
A口的功能前边已经说了,保留为ADC用,其他功能忽略。
FIGURE4-1:
是RA0~RA3的功能逻辑,有助于编程和接入电路。
RA4,RA5分别多了可以Clock1和Clock2输出,也不用此功能。
接着是RB0~RB7,他们都有弱上拉控制——RBPU,其中有一个括号中:
(OPTION_REG<7>)表示是OPTION的第7位设置有效,弱上拉。
按照前边的预设,RB口作为I/O口用。
接着RC口,C口的功能比较复杂,都是多功能复用口。
按照前边的描述,RC0作为脉冲输入用,RC1,RC2作为CCP或PWM口用,余下的作为I/O口用。
简概一下:
看逻辑图:
RA和RC口都有推挽输出,因此带负载能力强,大约下拉电流大于10mA[FIGURE18-18:
],上推电流也大于10mA[FIGURE18-16:
],可以看出下拉电流能力大点。
RB口下拉能力强,上推能力弱。
有了以上基本概念就可以进行下边的实验。
做实验前准备如下东西:
安装好Uedit32、PICKit2,PICCv9.8,保证下载器是Ok的;准备串接了电阻的LED,2对——我在网上介绍过的小部器件。
下次,开始第一个程序:
点亮LEDs和Uedit32的配置和PICKit2的配置。
如果可能考虑如何编写程序。
三.LED点亮
LED点亮太简单了。
不就是将接有LED的口置高或低就Ok了,却是如此。
首先需要了解C编程。
假设已有基本的C基础。
此外将目标板连接好,在RC口的RC7~RC4连接串有102电阻的LED。
如果是双向的LED效果最好。
打开Uedit32,创建第一个项目,文件夹名:
pic16f873a/picTest/,项目名:
test。
这款软件尽管以项目管理方式,但实际上他是文件操作管理软件,因此一个项目下可以放所有的相关编程,只要文件名能区别即可。
创建工程项目设置界面
在弹出的工程设置对话框中/关闭即可——除非已有程序,可以添加进工程中。
在工程界面:
文件/新建输入led.c;而后工程/添加到工程/文件,将刚创建的led.c加入项目。
与所有单片机编程一样首先需要加入头文件,在头输入:
#include
__CONFIG(0xFF32);//参阅PIC16F873Apdf文档P14214.1ConfigurationBits配置字
//接着可以采用多种方法实现,最简单方法是直接创建main函数
voidmain(void){
longi;//长整形
//defineport
TRISC=0b00001111;//H7-4=Input/L3-0=Out
PORTC=0x50;//=01010000
while
(1){
PORTC=0x55;//=01010101
for(i=50000;i>0;i--);
PORTC=0xAA;//=10101010
for(i=50000;i>0;i--);
}
}
为什么不用PORTB呢?
因为B口的有弱上拉设置,所以我们先用简单的C口操作。
在编译之前需要选择采用那种C编译软件,PICC是与Uedit32常配的编译器,单击高级/工程工具配置,按下述选输
菜单项目名称:
16f873a
命令行:
PICCC编译器绝对路径\PICC\9.80\bin\picc.exe-16F873A%F
工作目录:
项目绝对路径\picTest
工具栏位图/图标:
PICCC编译器绝对路径\PICC\9.80\resources\hitech_logo.ico
第一行是编译项目名称
第二行是编译器的文件夹路径和文件名后跟的参数
第三行是要编译的对象文件的文件夹路径
第四行是编译器的图标,可以不要
工程配置界面
之后在高级/最下一行可以看到:
16f873a的选项,单击他会对当前打开的C文件进行编译,如果没有问题会产生一个与编译文件名一样的.hex文件,将此文件烧到MCU中就可以看到结果了。
工程编译显示界面
打开PicKit2,单击DeviceFamily/Midrange/Standart;在下边的Device右边下拉找到PIC16F873A;文件/ImportHex/将工程文件夹下的编译好的.hex加到程序中,本例文件为:
led.hex;而后单击Write或Programmer/WriteDevice/数秒之后就下载完成了,可以单击VDDPICKit2下边的On选框,加载电压到目标机,
观看结果。
用PICKit2下载程序到目标板LED灯显示示意图
小小改进,上述程序没有问题,一切如愿,但不规范——C规定先定义再使用,并且最好以模块化的方式——初学者来说还是不要模块化到头即可,否则定义的都找不到岂不累死了。
上述程序功能分析可以得到一下:
有I/O口的定义,有个延时语句,有控制LED——分成ioInit、delay和main,程序改成:
#include
__CONFIG(0xFF32);//参阅PIC16F873Apdf文档P14214.1ConfigurationBits配置字
//defineport
voidioInit(void){
TRISC=0b00001111;//H7-4=Input/L3-0=Out
PORTC=0x50;//=01010000
}
//Delay
voiddelay(longts){
longi;
for(i=ts;i>0;i--);
}
//mainProcess
voidmain(void){
ioInit();
while
(1){
PORTC=0x50;//=01010000
delay(50000);
PORTC=0xA0;//=10100000
delay(50000);
}
}
上述程序从语句上看多了几行,从结构上非常清晰。
编译后的程序效率呢?
前边的:
:
040000008A11B02F82
:
100F600083018A11B32F0F3083168700503083120C
:
100F7000870050308700A301A201C330A100503088
:
100F8000A000FF30A007031CA107031CA207031C3D
:
100F9000A3072308803AFF0080307F02031DDA2F69
:
100FA00000302202031DDA2F00302102031DDA2F48
:
100FB000013020020318C12FA0308700A301A20135
:
100FC000C330A1005030A000FF30A007031CA107D0
:
100FD000031CA207031CA3072308803AFF008030EC
:
100FE0007F02031DFD2F00302202031DFD2F003064
:
100FF0002102031DFD2F01302002031CB92FE42F15
:
02400E0032FF7F
:
00000001FF
改后的:
:
040000008A11B82F7A
:
100F700083018A11C22F0F308316870050308312ED
:
100F8000870008008A11BB278A1150308700A3010F
:
100F9000A201C330A1005030A0008A11DC278A11C1
:
100FA000A0308700A301A201C330A1005030A000EF
:
100FB0008A11DC278A11C52F2308A7002208A60062
:
100FC0002108A5002008A4002708803AFF008030EF
:
100FD0007F02031DF52F00302602031DF52F003080
:
100FE0002502031DF52F01302402031C0800FF30E9
:
100FF000A407031CA507031CA607031CA707E42FCF
:
02400E0032FF7F
:
00000001FF
你会发现少了整整一行,效率反而高了。
这就是神秘的C编译器。
它是结构化的编译器。
就是说,对顺序写的程序编译效率低,对结构化的程序编译效率高,当然一般来说执行速度也会好点的。
如果你手里有PIC的反编译器,可以试试回来后的结果。
下边留个作业:
还是RC7~4亮,编程实现RC7->RC6->RC5->RC4->RC5->RC6->RC7来回循环亮。
提示多了if判断语句,采用左移和右移语句[>>1——右移1位,反之左移1位]。
可以先将led.c复制一个改名为ledshift.c,在加入到此项目中。
作业的效果:
视频无法上传,看看能否在别的地方上传。
已经上传到优酷网站:
观看视频
由于视频远小于广告的时长,可能需要看第二遍.
四.Time的运用
六的LED灯适才用循环语句完成的延时,可以理解此时MPU一直在忙着,能否让他在显示过程还干点其他的呢?
——这就用到了定时中断的概念。
873A有3个Time,最长的是Time1,Time0和Time2不加预分频器只有8位。
所以较长的定时可以用Time1,短的或长度少变的用其他两个。
其实,还是在运用,灵活选舍,不必拘泥。
先看Datasheet/Page53/5.0TIMER0MODULE/在开始的简介中可知:
8位的计数[增加]或时间,可读写,8位可编程预分频器,可以内部或外部时钟,外时钟可选择沿;WDT和Time0公用预
分频器。
看FIGURE5-1:
系统时钟/4=Time0的时钟——10MHz的晶振,Time0时基为2.5MHz。
T0SE控制RA4是否作为外部时钟输入;T0CS=0表示时钟为系统,=1表示外部时钟;PSA控制预分频器给谁用,=0时给Time0,=1时WDT;PS2:
PS0表示PS2~PS03位,控制预分频器的分频级;以上的寄存器控制字是OPTION寄存器的5~0位。
记住他以后好写OPTION寄存器。
由于Time0的性质,用作外部测频或计数不灵活,只好用于内部延时或WDT。
5.1Timer0中断。
TMR0是Time0的0xFF-->0x00时溢出计数寄存器,其溢出中断标志为T0IF[INTON的第2位]此位必须由程序清除,TMR0IE[INTON的第5位]中断允许,Time0的中断不能在Sleep[休眠]下唤醒。
注:
T0IF=TMR0IF;TMR0IE=T0IE。
5.2省了不看了。
5.3是预分频,前边已说PSA,PS2:
PS0决定给谁用和预分频数。
下边看OPTION8位寄存器:
OPTION<7>反PBPU是PORTB口的弱上拉定义位,0有效[可以利用led.c更改PORTC为PORTB等相关参数测试一下,并且可以知道,MCU默认IO口。
是不设置的——设置有效]
OPTION<5>T0CS:
TMR0时钟源选择,0内部时钟/1外部时钟
OPTION<4>T0SE:
外时钟触发沿:
0-上升沿/1-下降沿
OPTION<3>PSA:
预分频设置:
0-Time0用/1-WDT用
OPTION<2:
0>PS2:
PS0:
预分频数值,由于用于WDT和Time0相差1倍,请看手册
之后的TABLE5-1:
是Time0寄存器快速设置指南,灰色的格中的内容与Time0无关,并且说明了加电和系统复位时的状态。
下边总结一下Time0用得着的控制字设置和参数设置:
OPTION中[bit7-->bit0]的T0CS,T0SE,PSA,PS2:
PS0,为Time0用时都为0,OPTION=0b00000xxx;xxx为预分频设置值
TMR0=0xXX;XX为Time0的计数值,注意它是计满后产生中断,因此设置值的有效部分为0xFF-设置值,比如想计数5次,
设置值就是256-5=251,设置值=251=0xFA;
T0IFTime0中断标志,FF-->00时为1,需要程序清除,Time0中断判断标志
T0IE=1;Time0中断允许
GIE=1;通用中断允许
接着来设置Time0的中断函数
先看PAGE15314.11中断,由FIGURE14-10:
INTERRUPTLOGIC中断逻辑图可以看出,87xA系列的MCU中断向量为1个
——因此只能由中断向量来判断中断是哪个模块产生的,与独立中断想必各有利弊。
我们采用PICC作为C编程器,有必要看看《PICC编译器使用指南》,在此谢谢由张明峰在2004年编写的此教材。
下载信息[文件大小:
176.8KB下载次数:
250]
图片点击可在新窗口打开查看点击浏览该文件:
picc编译器使用指南-中文.pdf
直接看11.6.4中断函数的实现
可以得到中断函数的书写方法为:
voidinterrupttime(void){//中断服务程序time任意写
if(T0IE&&T0IF){//判TMR0中断只需要T0IF就可以了
T0IF=0;//清除TMR0中断标志
//在此加入TMR0中断服务
}
if(其他中断标记){//判替他中断
其他中断标记=0;//清除其他中断标记
//在此加入其他中断服务
}
......
}//中断结束并返回
上边的程序可以理解进入中断后清除标记
到此,基本上理解Time0的工作原理,Time2与之相似,Time1只是有高和低两个8位计数寄存器,其他的还是差不多的。
这样可以为Time0写两段程序:
初始化、中断
将led.c拷贝一个,改名为time_1.c,并将他添加到项目中,打开Uedit、time_1.c。
在写程序之前先弄明白一个问题:
中断处理程序越短越好——减少由于中断处理带来的延时误差,如果将LED状态也放在中断函数中,就不复合这条规定了,因此我们设定一个变量,由中断处理程序改变,主程序执行完成后恢复。
设定的变量为t0,初值为1;中断改为0。
主程序判断t0为0时执行程序,而后改回1。
有了上述的理论和各个控制字等的基础,尝试自己编个程序。
按照上次练习的循环LED,延时程序取消采用Time0中断来启动LED循环。
[需要将PreSca——PS2:
PS0设为111,否则速度太快!
],TRM0的初值为0x0C时,大约延时时间为.025S。
为了更好地理解Time的功能,可以在实验中改变:
PS2:
PS0的值,TMR0的值。
此外提出本次实验的练习:
如果采用Time0作为延时子程序,如何编程可以随时调用——就像调用delay()一样?
怎样编程才可以改变延时时间?
如何设置或编程才能得到更长的延时时间——比如1S,5S等。
循环LED和此次的程序下文给出。
今天有点时间,将循环的LED程序放上,代码如下:
#include
__CONFIG(0xFF32);//PIC16F873Apdf/P14214.1ConfigurationBits
intjs=0;
//IOInitial
voidioInit(void){
TRISC=0b00001111;//H7-4=Out/L3-0=Input
PORTC=0b10000000;//=1000000
}
//delayFunction
voiddelay(longts){
longi;
for(i=ts;i>0;i--);
}
//MainFunction
voidmain(void){
ioInit();
while
(1){
if(PORTC==0b10000000)js=0;
if(PORTC==0b00010000)js=1;
if(js==0)PORTC=PORTC>>1;//Rightshift
if(js==1)PORTC=PORTC<<1;//Leftshift
delay(5000);
}
}
为什么这么编呢?
直接判断就左移或右移不就得了——这就是每个编程人的思路不一样,因为,如果想结构化,这两个判断是不同的操作。
我的东西在网上绝对找不到第二份,完全是用手敲出来的,而程序是新创建项目新编的,有时间多敲点,没有时间会少敲点的。
在鱼和渔的问题上,我更倾向于后者,所以此连续剧许多是从文档开始到实验结束。
//Time0withInterruptfordelayer
#include
__CONFIG(0xFF32);//PIC16F873A.pdfP14214.1ConfigurationBits
intt0=1,js;//FUNFlag
//IOInitial
voidioInit(void){
TRISC=0b00001111;//H7-4=Input/L3-0=Out
PORTC=0b10000000;//=1000000
}
//Time0Initial
voidtime0Init(void){
//Time0OnePeion=2.5MHz/256/256=38.15chan=0.025s
OPTION=0b00000111;//H7=0-PBUp/6=1-PB0UpInt/5=1-Time0->RA4/4=1-HtoL/3=0-Time0/2-0=000~111=2~256PreSca
TMR0=0x0C;//SetPeion=1/250
T0IE=1;//Time0INTEnable
GIE=1;//GENINTEnable
}
//DelayFunctionNotuse
/*voiddelay(longts){
longi;
for(i=ts;i>0;i--);
}*/
//MainFunction
voidmain(void){
ioInit();
time0Init();
ei();//InterruptFunction
while
(1){
if(t0==0){
GIE=0;//GENINTDis
T0IE=0;//Time0INTDis
if(PORTC==0b10000000)js=0;
if(PORTC==0b00010000)js=1;
if(js==0)PORTC=PORTC>>1;//Rightshift
if(js==1)PORTC=PORTC<<1;//Leftshift
//delay(5000);
t0=1;
TMR0=0x0C;
T0IE=1;//Time0INTEna
GIE=1;//GENINTEna
}
}
}
//InterruptFunction
voidinterrupttime(void){
if(T0IF){
T0IF=0;//清除中断标志
t0=0;
}
}
红色的注释掉了
看了上述程序,有些人可能认为Interrupt还可以这么写吗?
——只有想不到,没有做不到。
只要你认真看了Datasheet了解了MCU之后,一切皆有可能。
再重复前边写的:
为了更好地理解Time的功能,可以在实验中改变:
PS2:
PS0的值,TMR0的值。
此外提出本次实验的练习:
如果采用Time0作为延时子程序,如何操作可以随时调用?
怎样编程才可以改变岩石时间?
如何设置或编程才能得到更长的延时时间——比如1S,5S等。
这个练习不给答按,如果有疑问的网友或初学者可以在此提问。
而六的提问此次的代码已经给了答按,运行一下
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 如何 PIC16F87xA LCD6903