Malay
颜色选择
阳光橙
深夜黑
天空蓝
葡萄紫
薄荷绿
深海蓝
首页
论坛
佳礼报道
新闻
搜索
27374
查看
101
回复

[教学]入门microchip c18教程(PIC18F4620)

[复制链接]

楼主: fritlizt       显示全部楼层   阅读模式

楼主
 楼主| 发表于 29-10-2009 12:24 PM | 显示全部楼层

简易I/O控制

看之前的教学, 觉得好像一次过讲得太深了。
现在从新整理从简单的开始讲。
开始简单I/O控制。shcmatics用第二十八楼的schematics.:)

要控制pic io,有两个东西要控制。第一个tris ,第二个latch/port.
tris就是控制data direction的register.在pic里面如果你要bit 0是output,就需要把相关的tris bit0 set去0。相反的,如果是要当作input,就要set 去1.

例:
RB0    Input
RB1    Input
RB2    Output
RB3    Input
RB4    Output
RB5    Output
RB6    Output
RB7    Input

先不要理你的output/input是0还是1,如果你要用以上的io direction configuration.
你的trisb应该是 10001011 = 0x8b;
TRISB = 0x8b.

好, 接下来是控制io另一个东西,就是latch和port.
简单来讲要write to output , 你用latch,
要read input, 用port.

看看example, 这个example,我把portb全都set 去output "10101010"
然后program进入死循环。就这样。


  1. #include <p18cxxx.h>

  2. #pragma config OSC = HSPLL            //hspll osc
  3. #pragma config BOREN = OFF            //no brownout reset
  4. #pragma config WDT = OFF            //no watchdog
  5. #pragma config MCLRE = OFF            //no mclr

  6. void main (void)
  7. {
  8. LATB = 0xAA;
  9.         TRISB = 0x00;
  10.     while (1);
  11. }
复制代码


很明显, code 非常的简单。
一开始就是include file,这个是必要的。
p18cxxx.h是pic 18 的header. compile time的时候, 会根据你ide device setting来决定要用什么pic的header file.

接下来是fuse bit setting.
用hspll, 没有brown out reset, 没有watchdog timer,没有master clr.

然后LATB = 0xAA, (10101010)
然后再set data direction.
run 你的program,就会看到portb 变成10101010
回复

使用道具 举报

楼主
 楼主| 发表于 29-10-2009 12:27 PM | 显示全部楼层
原帖由 cfy0104 于 26-10-2009 01:53 PM 发表
我是打算用C LANGUAGE来写我的FINAL YEAR PROJECT...
这边可以帮到小弟吗??????


可不可以, 要看你自己本身肯不肯学
有问题, 可以上来发问。 这里会尽量帮你的。
当然, 自己要做足功课才来发问。
回复

使用道具 举报

发表于 29-10-2009 06:55 PM | 显示全部楼层
不是先TRIS 然后才 LATB 的吗?
我认为当mcu在初始时,portb是set as input 的,所以portb data register是受外部因素影响,尤其是当port pin 处于floating 的时候,通常会有‘1’ 的voltage level 出现(个人经验)。这时候,无论你放什么latb value 都好,在接下来的“TRISB"完成后,最后的Port B data 不就变回“FFh" 了吗?

[ 本帖最后由 电子达人 于 29-10-2009 07:07 PM 编辑 ]
回复

使用道具 举报

发表于 30-10-2009 11:39 AM | 显示全部楼层
原帖由 电子达人 于 29-10-2009 06:55 PM 发表
不是先TRIS 然后才 LATB 的吗?
我认为当mcu在初始时,portb是set as input 的,所以portb data register是受外部因素影响


这个是 PORTB。LATB 是不会被 pin 的 state 影响的。

当 MCU 初始化时,LAT 的 value 是 unchange 的。如果 LAT 的 value 是 1,当你 set TRIS 成为 output 时,output pin 也会马上变成 high。所以我们多数都会先 set LAT 才 set TRIS。

评分

参与人数 1积分 +10 收起 理由
fritlizt + 10

查看全部评分

回复

使用道具 举报

发表于 30-10-2009 06:21 PM | 显示全部楼层
哦!我明白了,PIC 的PORT read data latch ,write data latch 不是同一个的data latch 是吗?有错请纠正!

[ 本帖最后由 电子达人 于 30-10-2009 06:34 PM 编辑 ]
回复

使用道具 举报

楼主
 楼主| 发表于 30-10-2009 09:30 PM | 显示全部楼层
原帖由 电子达人 于 30-10-2009 06:21 PM 发表
哦!我明白了,PIC 的PORT read data latch ,write data latch 不是同一个的data latch 是吗?有错请纠正!


PORT read data latch这句很深啊,不明白。哈哈。
关于port, latch的问题让我解释一下。

通常write output,我们write to latch, read input,我们read from port.
你要read latch也是可以的, 不过只是会return你output latch 的value, port value是不会map在latch register的。
这两者之间不是同一个register.

latch register主要hold这你的output status.当你的tris 从input转去output的时候, 那它的output state是什么呢?就是latch register value咯。 这个是latch register的功能。

port register,就是你pic physical port 咯。。。没什么可以解释。

write to port不是很suggested主要的问题出现在bit operation
bit operation在pic16, 18是read-modify-write operation.
你用bsf, bcf会发生什么事情呢?
比如说bsf portb.2, pic就会把你的整个portb read进来,然后modify bit2,然后 overwrite整个port.

如果是capacitive load, voltage从0->5v要时间。假设你的portb2和portb3本来是"0"

bsf portb2
bsf portb3

以上的可能就会出现问题。 从program来看portb2,和3都会是1不过到最后你会发觉到port3会是1而已。
bsf portb2的时候portb2.开始从0->5v,你的instruction 执行到bsf portb3的时候, portb2还没到1的level.
不过又要read modify write. 这时候read到的portb2是0.然后modify portb3去1。然后overwrite portb....
就这样。。。。bla bla....

又错情纠正。。。
回复

使用道具 举报

楼主
 楼主| 发表于 30-10-2009 09:31 PM | 显示全部楼层
原帖由 电子达人 于 29-10-2009 06:55 PM 发表
不是先TRIS 然后才 LATB 的吗?
我认为当mcu在初始时,portb是set as input 的,所以portb data register是受外部因素影响,尤其是当port pin 处于floating 的时候,通常会有‘1’ 的voltage level 出现(个人经验) ...


这个问题waiweng大大在64楼帮我回答了。
program是死的, 人是活的。看你的application,可能你需要set tris, 才set port.也可能你的application两者怎样set都没关系。
回复

使用道具 举报

发表于 30-10-2009 10:03 PM | 显示全部楼层
多谢你的解释,但是我所说的不是这个。。。
我所说的latch 是 hardware 的latch, 是Gated D-latch circuit.
(我是用ASM 语言的,还没学好C)
所以呢,我的意思是,在同一个port 里,有两个不同的latch ,output data latch 和input data latch ,是吗?

[ 本帖最后由 电子达人 于 30-10-2009 10:05 PM 编辑 ]
回复

使用道具 举报

发表于 31-10-2009 01:53 AM | 显示全部楼层

回复 68# 电子达人 的帖子

你可以看datasheet, io port 那里的block diagram, 看看他的logic gate 是如何接的,答案就在里面。 你也可以看看special function register 那里, 有没有分input latch , output latch?
根据18f4550 datasheet, in out是共享一个latch.
write LAT, PORT, 都是写入latch register.
latch register 只有在 write instruction (to LAT or PORT) 才会更改
read LAT, 是给latch register 的output (不管你的IO port 是什么)
read PORT, 是给 IO port 的output. (不管你的latch register 是什么)
回复

使用道具 举报

楼主
 楼主| 发表于 28-12-2009 10:46 PM | 显示全部楼层

Analog to Digital Converter

本帖最后由 fritlizt 于 5-3-2010 04:27 PM 编辑

接下来就是ADC, analog to digital converter.
一般PIC都有build in 10bits ADC.
这里就教如何使用adc.来测量voltage.用途很广,单看单晶片分区就动了。

关于什么是adc,这里有的参考。
http://en.wikipedia.org/wiki/Analog-to-digital_converter#Resolution

有一个很common的mistake, 比如说8bits adc, convert 0-5v.一个step是多少呢?
一个step是 5/(2^8-1), = 5/255 = 0.019607843v.

比如说8bits adc, aref high = 5v, vref low = gnd. 那么2v的时候,expected result是
2/5*255 = 102, 而不是2/5*256 = 102.4.当然结果不会有floating point.这里只是讲解用的例子而已。

接下来看看pic18f4620的adc.

pic18f4620有内建13个10bits adc.
了解adc的操作以后,在c18写adc code不会很难。

这个example我不用build in peripheral library,有bug,我就自己写了。

基本上18f4620的adc只有三个register 来控制。
ADCON0->负责enable adc, start conversion还有channel selection.
ADCON1->负责reference voltage setting, analog port config.
ADCON2->result left/right justified, adc acq time, adc conversion clock.

一开始就要configure adc module.让adc根据你的setting来走。 不适当的configuration,可能会导致准确性降低, 或者adc不能运行。

一些简单的setting.
1.enable adc 要用的话不能不enable, analog/digital设定。 由于pic analog引脚和digital i/o是share的, 所以你要设定哪一个引脚用来当analog 用途。 这里我直接set all analog,简单。
2.设定operating range.也就是vref+, vref-.我用gnd = vref-, vcc = vref+.也就是0-5v = 0-1023
3.设定right justified/left justified.由于结果只是10bits,不过pic用了两个8 bits register也就是16bits来储存结果,所以你可以选择left/right justified.一般要拿10bits结果, 设定right justified即可。如果只要8bits结果,设定left justified,拿high byte就可以了。
4.设定A/D acq time.简单来讲,如果你有两个analog input, ch0, ch1.ch0 = 1v, ch1 = 5v.当你从channel 0还去channel 1的时候,你要等input channel voltage从1v升到5v所需要的时间。如果时间太短,input来不及升到5v就开始转换, 结果可能会不准确。这个并没有一个很standard的计算方法。根据系统要求会有不一样的设定。 example calculation可以在pic18f4620 datasheet, 19.1找到。我的example,我假设Tacq = 12Tad = 12x0.8us = 9.6us
5.设定A/D conversion clock.这个我懒惰解释。 不过有limitation.
For correct A/D conversions, the A/D conversion clock
(TAD) must be as short as possible, but greater than the
minimum TAD (see parameter 130 for more
information).
我的case, Tosc = 40Mhz, Tad min = 0.7us. 在要大过0.7us限制下我又要找到最快的Tad,只有Fosc/32 = 0.8us.
这个也是很重要的,不正确的设定也可能导致结果不准确。

设定coding

  1. ADCON0 = 0x01;                    //enable adc
  2. ADCON1 = 0x00;                    //vref+ = Vcc, vref- = vss, all analog
  3. ADCON2 = 0xAA;                    //right justified, Tacq = 12Tad, Fosc/32
复制代码


设定完毕, 接下来就是start adc conversion & read result.这个部分不难, 直接用c18的peripheral library.
其实设定也是可以用peripheral library来做,不过c18的adc setting peripheral library有bug.所以就自己来,也不过那三个register而已。
SelChanConvADC(ADC_CH0);    //设定channel,然后开始A/D转换
while(BusyADC());           // 等待A/D转换完毕
result = ReadADC();         // 然后, 读取结果

完整的example program:
  1. #include <p18cxxx.h>
  2. #include <adc.h>

  3. #pragma config OSC = HSPLL                        //hspll osc
  4. #pragma config BOREN = OFF                        //no brownout reset
  5. #pragma config WDT = OFF                        //no watchdog
  6. #pragma config MCLRE = OFF                        //no mclr

  7. void main (void)
  8. {
  9.         unsigned int result;
  10.         ADCON0 = 0x01;                //enable adc
  11.         ADCON1 = 0x00;                //vref+ = Vcc, vref- = vss, all analog
  12.         ADCON2 = 0xAA;                //right justified, 12Tad, Fosc/32
  13.         while (1){
  14.                 SelChanConvADC(ADC_CH0);   
  15.                 while(BusyADC());            
  16.                 result = ReadADC();      
  17.         }
  18. }
复制代码
回复

使用道具 举报

发表于 9-1-2010 01:11 AM | 显示全部楼层
之前看到教程用到 LAT 时还真的不明白是什么东西,现在知道了。。。谢谢 fritlizt 版主。

请问我要如何用 proteus 来 simulate 我在 mplab 写的 code 呢?因为我想看看 output 是怎样跑,谢谢。
回复

使用道具 举报

楼主
 楼主| 发表于 16-1-2010 12:08 AM | 显示全部楼层
回复 71# funky


   

请参考。【讨论】Proteus VSM。有相关的可以在那个贴发问。
http://cforum5.cari.com.my/viewthread.php?tid=1032953&extra=page%3D1&page=3
回复

使用道具 举报

发表于 17-1-2010 10:56 PM | 显示全部楼层
回复 72# fritlizt


   謝謝 fritlizt 大大的回復。

請問一下,如果一個人用 8051 然後是 assembly code 寫的,然後一個是用 PIC16 或 18 然後是 C 寫的,請問兩個 chips 之間的 communication 會很難做嗎?

就是説 51 會接收 IR sensor 的 information 然後傳給 PIC16 或 18, 之後 PIC16 或 18 會 用 IF THEN function 去判斷要進行什麽 condition, 之後再將它傳給 power window motor 去進行一定角度的轉動。

謝謝
回复

使用道具 举报

楼主
 楼主| 发表于 17-1-2010 11:23 PM | 显示全部楼层
回复 73# funky

假设是用hardware uart做简单的communication.不难。
回复

使用道具 举报

发表于 18-1-2010 12:29 AM | 显示全部楼层
本帖最后由 funky 于 18-1-2010 12:32 AM 编辑

回复 74# fritlizt


   意思是说两个 coding language 不同也可以咯?是不是理论上 51 打开 TX 将 digitalized 的 information 传给 PIC 的 RX 然后在 PIC 内进行运算,之后再用 PIC 的 TX 传给 51 的 RX?我刚刚看了 18F4620 的 datasheet EUSART 的部分,看不明白 -_-''' 请问要怎么看,很多东西我真的不明白。(其实我也刚刚学 PIC 罢了,很多东西的确不是看很明白,哈哈)
回复

使用道具 举报

发表于 24-1-2010 12:53 AM | 显示全部楼层
想知道21楼的simulator 是什么?
回复

使用道具 举报

楼主
 楼主| 发表于 5-3-2010 03:50 PM | 显示全部楼层
回复 75# funky


   

coding不是问题。最主要是两边(mcu)都能通过一样的protocol来接受/发送讯息。
uart<->uart, spi<-> spi. 你不能spi<->uart.coding你可以,assembly, c, basic etc.
回复

使用道具 举报

楼主
 楼主| 发表于 5-3-2010 04:31 PM | 显示全部楼层

[教学]入门microchip c18教程(PIC18F4620) - Timer

终于update了那两个月的ad converter。目录请看第一楼
接下来要讲的是Timer.....

to be continue.....
回复

使用道具 举报

楼主
 楼主| 发表于 19-6-2010 12:36 AM | 显示全部楼层
本帖最后由 fritlizt 于 19-6-2010 12:37 AM 编辑

回复 78# fritlizt


   

n 久没有update这个贴了。。。。。
好了。 今天讲讲timer.
timer 用途很多。都不懂要怎么开始讲。对我来说,timer,是mcu里面其中一个非常重要的hardware.有些hardware可以可有可无。timer是最基本,一定要有。呵呵。

既然是入门教学。 我就说说基本的timer operation.
一般timer operation可以当作timer/counter, 也就是算时间,当作counter,算external input.
其实都是一样,都是counter,只是clock source不一样,一个用instruction cycle来做input,一个是external input,可以是sensor还是其他,所以叫做counter.用counter来做timer也是没问题。

pic18f4620有好几个timer,每个timer都有不一样的feature,未必每个timer都有counter.timer2没有。
还有其他xxyy等的feature.这个请自己读datasheet.
我用timer1来做example.可以是timer也可以是counter.比较general purpose.
timer1基本上可以拿来做:
• Timer
• Synchronous Counter
• Asynchronous Counter

1. TIMER
先说概念,timer可以拿来做什么?讲到底,就是拿来算时间。从算时间你可以做很多其他的东西。 比如说:
时间到, send msg,
时间到, 读感应器。
时间到,响alarm.
等等。

怎样算时间, 怎样知道时间到等。
由于是算instruction clock, instruction clock快慢会影响你的timer计算的速度。
以下的算法都假设Fcy = 40Mhz.也就是crystal = 10Mhz, PLL = 4.
那么instruction clock = Fcy/4 = 10Mhz.

接下来clock指的是instruction clock. 如果是Fcy/crystal会另加注明。
这个部分很重要,要用timer前, 必须了解timer的基本操作。
timer1 会随着instruction clock 增加。default每一个instruction clock,timer 1 register会增加1。
timer1 还有一个feature叫做prescaler. 简单来说就是 每X个instruction clock, timer register会增加1。x可以是1,2,4,8.
timer 1 register可以加到多少? 可以是8 bits (255 max),也可以是16bits (65536 max).
加到最多过后, timer 1 register会reset回去0,然后set overflow flag.代表timer 1 register已overflow了。已经到max了。
(注意:timer算到65535的时候还没有overflow,加多一的时候,才会overflow, 然后reset 去0.)
所以timer1最多可以算到 65536 * 8 / 10Mhz = 0.0524288秒。

接下来进入programming.
先给个case.
我想每 10milisecond 读取 PORTB,然后paste在PORTC.
这个很简单, 就是算 10milisecond, 时间到, 读B 写C.
这种可以有两种写法。
第一:利用timer overflow. 算timer 1 register 到overflow需要多少时间,然后设定timer 1 register,等overflow.
第二:设定timer 1 register 去 0,然后算10milisecond的时候, timer 1 register应该是什么value. 通过compare,就知道时间到了没有。

算法:
方法一:你要算10milisec,要等到10milisec后overflow.你现在的timer应该什么value?很typical的从屁股算起的数学题。
那么在10Mhz下, 10milisec需要多少个clock....
total clock = 10mhz * 10milisec = 100000.
假设你的prescaler = 1, 你需要100,000个count来到达overflow.很明显,无论是8bits 还是16bits你最多只能65536,你不可能算到100,000个count,到65535过后就reset 0了。
所以需要prescaler = 2,那么你需要 50,000个count就可以了。16bits timer1 register可以算到50,000个count.
你需要50,000个count来达到overflow,那么你应该从什么value算起。很简单65536 - 50000 = 15536.从15536算,当overflow的时候就是10milisecond的时候。

方法二
这个不难。只需要50000个count,那么你需要把timer 1 value set 去0,然后看timer 1 register 什么时候超过50000的时候就是时间到了。非常直接。

先看看第一种方法:
和其他peripheral一样,使用前需要initialize.
function prototype如下
  1. void OpenTimer1( unsigned char config  );
复制代码

config 可以是

Enable Timer1 Interrupt:

  

    TIMER_INT_ON                  Interrupt  enabled

  

   TIMER_INT_OFF                  Interrupt disabled

Timer Width:

  

      T1_8BIT_RW                       8-bit  mode

  

      T1_16BIT_RW                     16-bit  mode

  

Clock Source:

  

      T1_SOURCE_EXT                 External  clock source (I/O pin)

  

      T1_SOURCE_INT                 Internal  clock source (Tosc)

  

Prescaler:

  

      T1_PS_1_1                        1:1  prescale

  

      T1_PS_1_2                        1:2  prescale

  

      T1_PS_1_4                        1:4  prescale

  

      T1_PS_1_8                        1:8  prescale

  
  

Oscillator Use:

  

      T1_OSC1EN_ON                  Enable  Timer1 oscillator

  

      T1_OSC1EN_OFF                Disable  Timer1 oscillator

  

Synchronize Clock Input:

  

      T1_SYNC_EXT_ON              Sync  external clock input

  

      T1_SYNC_EXT_OFF            Don’t  sync external clock input


  1. #include <p18cxxx.h>
  2. #include <timers.h>

  3. #pragma config OSC = HSPLL          //hspll osc
  4. #pragma config BOREN = OFF          //no brownout reset
  5. #pragma config WDT = OFF            //no watchdog
  6. #pragma config MCLRE = OFF          //no mclr

  7. void waitTimeout (void);

  8. void main (void)
  9. {
  10.         TRISB = 0xFF;                //portb as input
  11.         TRISC = 0x00;                //portc as output
  12.         TRISD = 0x00;
  13.         OpenTimer1(T1_16BIT_RW & T1_SOURCE_INT & T1_PS_1_2);
  14.         CloseTimer1();

  15.         while (1){
  16.                 LATDbits.LATD0 =~ LATDbits.LATD0;
  17.                 waitTimeout ();
  18.                 LATC = LATB;
  19.         }
  20.         while(1);
  21. }

  22. void waitTimeout (void)
  23. {
  24.         WriteTimer1(15536);
  25.         T1CONbits.TMR1ON = 1;            //start timer
  26.         while (!PIR1bits.TMR1IF);        //wait for timer overflow
  27.         T1CONbits.TMR1ON = 0;            //stop timer
  28.         PIR1bits.TMR1IF = 0;            //clear overflow flag
  29. }
复制代码


要用internal function, 需要include <timers.h>

OpenTimer1(T1_16BIT_RW & T1_SOURCE_INT & T1_PS_1_2);
用16bits mode, 用internal clock source.如果是external clock source就代表counter了。然后prescaler 2.

CloseTimer1();
不需要的时候off timer.

接下来就到wait timeout了。由于 internal functions没有check overflow flag的features.就要 DIY了。
一开始用 write timer1把 15536写进 timer1 register.
然后让 timer跑。
当timer overflow的时候 , PIR1bits.TMR1IF会变成 1.所以如果是0 就原地循环。等到1为止就停止timer.然后clear PIR1bits.TMR1IF. 这个 bit是 hardware set, software clear。overflow会自动变一 ,不过不会自动变0。自己来。

timeout过后 ,就 read B write C. latd 是我拿来debug用 。


第二个方法 :
wait timeout unction有变动。
直接 set去 0,等 50000个 count.
  1. #include <p18cxxx.h>
  2. #include <timers.h>

  3. #pragma config OSC = HSPLL          //hspll osc
  4. #pragma config BOREN = OFF          //no brownout reset
  5. #pragma config WDT = OFF            //no watchdog
  6. #pragma config MCLRE = OFF          //no mclr

  7. void waitTimeout (void);

  8. void main (void)
  9. {
  10.         TRISB = 0xFF;                //portb as input
  11.         TRISC = 0x00;                //portc as output
  12.         TRISD = 0x00;
  13.         OpenTimer1(T1_16BIT_RW & T1_SOURCE_INT & T1_PS_1_2);
  14.         CloseTimer1();

  15.         while (1){
  16.                 LATDbits.LATD0 =~ LATDbits.LATD0;
  17.                 waitTimeout ();
  18.                 LATC = LATB;
  19.         }
  20.         while(1);
  21. }

  22. void waitTimeout (void)
  23. {
  24.         WriteTimer1(0);
  25.         T1CONbits.TMR1ON = 1;            //start timer
  26.         while (ReadTimer1() < 50000);        //wait for timeout
  27.         T1CONbits.TMR1ON = 0;            //stop timer
  28. }
复制代码


两个方法都行得通。
个人喜欢第一个方法。overflow flag可以拿来做 interrupt,比较 flexible.

to be continue .....->counter
回复

使用道具 举报

发表于 8-9-2010 12:24 PM | 显示全部楼层
超级高手 !!
留个脚印。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册 | 登入

本版积分规则

 WeChat
 CARI App
Get it FREE Google play
 Instagram
cari_malaysia
FOLLOW
- 版权所有 © 1996-2020 Cari Internet Sdn Bhd 佳礼网络有限公司 (483575-W) -
Private Cloud provided by IPSERVERONE
0.180411s Gzip On
快速回复 返回顶部 返回列表