(no commit message)
[amrad.git] / projects / lf / PIC8__95__4B.ASM
1 ;Greetings and I hope you find this code segment for serial coms using the\r
2 ;the 16F628 or 16F627 PIC Microprocessors.\r
3 ;\r
4 ;I hacked this code for the 627 and 628 from the fine work done by\r
5 ;Friar  Tom McGahee's work in his program PICUART.ASM.  The\r
6 ;Friar solved the Microchip Serial UART Coms problems for the earlier\r
7 ;chips.  I took the code and made it work for the 16F628 and\r
8 ;16F627, it was not easy but it is done.....and it works reliably.  DON'T\r
9 ;CHANGE A SINGLE LINE OF IT, COMPILE and load and it\r
10 ;will work.  If you think you see an error, and there are some glaring look\r
11 ;like errors in the code, DON'T CHANGE IT NOW,\r
12 ;GET THIS CODE WORKING, then you can happily put in many hours making it\r
13 ;simple and concise and you will learn some\r
14 ;rather nasty lessons about Microchip and Microchip documentation.  It is\r
15 ;difficult to say anything nice about Microchip, INTEL back\r
16 ;mid 1970's had a rather messy silicon for the first INTEL 8251's  At least\r
17 ;the Intel documentation was correct, just the silicon\r
18 ;was broken and INTEL fixed the silicon, unlike Microchip who as far as I\r
19 ;can find has never done anything about the Serial Coms\r
20 ;documentation........\r
21 ;\r
22 ;Once you have this code working you can go through and have a good day\r
23 ;cleaning it up and then trying to make it work again!  When I :say do a\r
24 ;Read when any reasonable person would do a Write, go back and make it a\r
25 ;Read.....\r
26 ;\r
27 ;Have fun, good fun with this code segment, use it in good health and for\r
28 ;as much profit as you can make from it.\r
29 ;Larry Kayser, VA3LK / WA3ZIA, va3lk@rac.ca\r
30 ;\r
31 ;As of 20030101 this URL worked for Friar McGahee's picuart.zip,\r
32 ;http://www.pic101.com/mcgahee/\r
33 ;\r
34 ;******************************************************************************\r
35 ;kayser@sympatico.ca\r
36         LIST    p=16F628        ;PICF628 is the target processor\r
37 ;\r
38         #include        "P16F628.INC"   ;Include header file\r
39 ;\r
40         ErrorLevel      0       ;0 for Messages, Warnings, Errors\r
41         ErrorLevel      -302    ;1 for Warnings, Errors\r
42                                 ;2 for Errors\r
43                                 ;-302 to suppress Page Message\r
44                                 ;\r
45 ;enable _hs_osc, _pwrte_on, when programming the device\r
46 ;******************************************************************************\r
47                 cblock  h'20'   ;bank 0  h'20' to h'7f'. 96 locations\r
48                 savew1  ;SAVEW1 *MUST* be at location h'20'!\r
49                 savestatus\r
50                 savepclath      \r
51                 savefsr\r
52                 rx_data         ;the received byte from the serial UART\r
53                 tx_data         ;byte to be transmitted via UART\r
54                 endc\r
55 ;******************************************************************************\r
56                 cblock  h'A0' ;bank 1\r
57                 savew2          ;SAVEW2 *MUST* be at location h'A0'.\r
58                 endc\r
59 ;\r
60         org     h'0000'         ;set code origin to beginning of rom start\r
61         goto    initialize      ;we must get past interrupt vector at 0004\r
62 ;\r
63 ;INTERRUPT ROUTINE\r
64 \r
65         org     h'0004'         ;interrupt vector location\r
66 inthandler\r
67         movwf   savew1          ;save w register! (at h'20', h'A0', etc.)\r
68         movf    status,w                ;w now has copy of status\r
69         clrf    status          ;ensure we are in bank 0 now!\r
70         movwf   savestatus              ;save status\r
71         movf    pclath,w                ;save pclath\r
72         movwf   savepclath      \r
73         clrf    pclath          ;explicitly select Page 0\r
74 ;\r
75         movf    fsr,w\r
76         movwf   savefsr         ;save fsr (just in case)\r
77 ;\r
78 vector_to_interrupt\r
79 ;\r
80         btfsc   intcon,t0if             ;test to see which interrupt \r
81         goto    service_t0if    ;needs servicing...\r
82 ;\r
83         btfsc   intcon,intf             ;there can be many different sources \r
84         goto    service_intf    ;of interrupt...\r
85                                         ;add as many checks here as you \r
86                                         ;have possible interrupt sources.... \r
87 ;\r
88 service_t0if\r
89         nop                             ;!!! or do something useful here...\r
90 ;\r
91 t0if_done\r
92                 bcf     intcon,t0if             ;clear interrupt flag that caused interrupt.\r
93                 goto    intclean                ;restore and return from interrupt!\r
94 ;\r
95 service_intf    \r
96                 nop                     ;!!! or do something useful here...\r
97 ;\r
98 intf_done       bcf     intcon,intf     ;clear flag that caused interrupt.\r
99                 goto    intclean        ;restore and return from interrupt!\r
100 ;\r
101 intclean\r
102                 movf    savefsr,w\r
103                 movwf   fsr             ;restore fsr\r
104 ;       \r
105                 movf    savepclath,w\r
106                 movwf   pclath          ;restore pclath. (Page=original)\r
107 ;\r
108                 movf    savestatus,w\r
109                 movwf   status          ;restore status! (bank=original)\r
110 ;\r
111                 swapf   savew1,f        ;restore w from *original* bank! \r
112                 swapf   savew1,w        ;swapf does not affect any flags!\r
113 ;\r
114                 retfie                  ;return from interrupt!\r
115                                         ;gie is auto-re-enabled.\r
116 ;\r
117 initialize                              ;initialize ports and registers\r
118 \r
119         bcf     status,rp0              ;first do page 0 stuff. yep, page 0\r
120 \r
121 gie01   bcf     intcon,gie              ;turn gie off \r
122         btfsc   intcon,gie              ;MicroChip recommends this check!\r
123         goto    gie01                   ;without this check\r
124                                         ;you are not sure gie is cleared!\r
125         clrf    pir1            ;clear peripheral flags\r
126 ;\r
127         clrf    porta           ;clear all i/o registers...\r
128         clrf    portb\r
129 ;\r
130         bsf     status,rp0      ;allow access to page 1 stuff!\r
131 ;\r
132         clrf    pie1            ;disable peripheral interrupts\r
133 ;                               \r
134         movlw   b'00000110'\r
135         movwf   trisb           ;0=output  1=input\r
136                                 ; BUT for UART use RB1 and RB2 MUST be programmed as an INPUT! \r
137\r
138         bsf     option_reg, not_rbpu    ;!rbpu! rb_pullup 0=enabled \r
139                                 ; 1=disabled. enabling is based on individual \r
140                                 ;port-latch values. currently pullups are \r
141                                 ;disabled.\r
142         bsf     option_reg, intedg      ;intedg 0=inc on falling 1=inc on \r
143                                 ; rising edge. <<note: intedg and t0se use \r
144                                 ; opposite definitions!>>\r
145                                 ;currently set for rising edge detection.\r
146         bcf     option_reg, t0cs        ;t0cs timer0clocksource 0=internal clkout\r
147                                 ;1=ra4/int. currently set for internal clkout\r
148 \r
149         bcf     option_reg, t0se        ;t0se timer0signaledge 0=inc on rising 1=inc \r
150                                 ; on falling edge.\r
151                                 ; <<note: intedg and t0se use opposite \r
152                                 ; definition!>>\r
153         bcf     option_reg, psa ;psa prescaler assignment 0=tmr0 1=wdt\r
154                                 ;ps2-ps0 determine prescaler rate, which is\r
155                                 ;dependent also on whether tmr0 or wdt is \r
156                                 ;selected:\r
157                         ;wdt from 0-7 is div by 1 2 4 8 16 32 64 128\r
158                         ;tmr0 from 0-7 is div by 2 4 8 16 32 64 128 256\r
159                         ;if wdt is assigned prescaler, then tmr0 is div by 1\r
160                         ; here we will set prescaler to divide by 16 for tmr0\r
161                         ; !!! set this any way you want. \r
162                         ; This is just an example that works.\r
163         bcf     option_reg,ps2  ;ps2 \r
164         bsf     option_reg,ps1  ;ps1\r
165         bsf     option_reg,ps0  ;ps0\r
166 \r
167 ;intcon register: bit assignments\r
168 ;\r
169 ;enables... 1=enable 0=disable\r
170 ;<7>=gie=global_int_enable\r
171 ;<6>=peie=peripheral_int_enable\r
172 ;<5>=t0ie=t0_int_enable (enables <2> t0if)\r
173 ;<4>=inte=int_enable (rb0/int) (enables <1> intf)\r
174 ;<3>=rbie=rb_int_enable (enables <0> rbif)\r
175 ;\r
176 ;intcon flags. software reset. 0=reset 1=flagged\r
177 ;<2>=t0if=t0_int_flag\r
178 ;<1>=intf=int_flag (rb0/int)\r
179 ;<0>=rbif=rb_int_flag (rb7-rb4)\r
180 \r
181         clrf    intcon          ;in this example we have no interrupts used.\r
182 ;\r
183 ;pie1 peripheral interrupt enable 1 register:\r
184 ; bit assignments. 1=enable  0=disable\r
185 ;\r
186 ;<7>=pspie=parallel_slave_port_int_enable\r
187 ;<6>=adie=a/d_int_enable\r
188 ;<5>=rcie=receiver_int_enable for uart (may use later)\r
189 ;<4>=txie=transmit_int_enable for uart (may use later)\r
190 ;<3>=sspie=sync_serial_int_enable\r
191 ;<2>=ccp1ie=ccp1_int_enable\r
192 ;<1>=tmr2ie=timer2_int_enable\r
193 ;<0>=tmr1ie=timer1_int_enable\r
194 ;\r
195         clrf    pie1            ;no interrupts used in this example\r
196 ;\r
197 ;uart specific initialization\r
198                                 ;txsta=Transmit STAtus and control register.\r
199                                 ;take nothing for granted.\r
200         bcf     txsta,csrc      ; <7> (0) don't care in asynch mode\r
201         bcf     txsta,tx9       ; <6>  0  select 8 bit mode\r
202         bsf     txsta,txen      ; <5>  1  enable transmit function \r
203                                 ;      *MUST* be 1 for transmit to work!!!\r
204         bcf     txsta,sync      ; <4>  0 asynchronous mode. \r
205                                 ;      *MUST* be 0 !!!\r
206                                 ;      If NOT 0 the async mode is NOT selected!\r
207                                 ; <3>  (0) not implemented\r
208         bsf     txsta,brgh      ; <2>  0 disable high baud rate generator !!!\r
209                                 ; lsk for 16F628\r
210                                 ; 1    (0) trmt is read only.\r
211         bcf     txsta,tx9d      ; <0>  (0)  tx9d data cleared to 0.\r
212 ;\r
213 ;   For brgh=0       baudrate=Fosc/(64(spbrg+1))\r
214 ;   So when brgh=0   spbrg_value = (xtal_freq/(baudrate*d'64'))-1\r
215 \r
216 ;   For brgh=1       baudrate=Fosc/(16(spbrg+1)) \r
217 ;   So when brgh=1   spbrg_value = (xtal_freq/(baudrate*d'16'))-1\r
218 ;\r
219 xtal_freq       =       d'4000000'      ;crystal frequency in Hertz.\r
220 baudrate        =       d'19200'        ;desired baudrate.\r
221 ;                               ;now calculate spbrg_value...\r
222 ;spbrg_value    =       (xtal_freq/(baudrate*d'64'))-1\r
223 spbrg_value             =       (xtal_freq/(baudrate*d'16'))-1\r
224 \r
225 ;\r
226         movlw   spbrg_value     ;set baud rate generator value\r
227         movwf   spbrg\r
228 ;\r
229         bcf     status,rp0      ;allow access to page 0 stuff again. (normal)\r
230\r
231 ;more uart specific initialization\r
232 ;\r
233                                 ;rcsta=ReCeive STAtus and control register\r
234 ;\r
235         bsf     rcsta,spen      ; 7 spen 1=rx/tx set for serial uart mode\r
236                                 ;   !!! very important to set spen=1\r
237         bcf     rcsta,rx9       ; 6 rc8/9 0=8 bit mode\r
238         bcf     rcsta,sren      ; 5 sren 0=don't care in uart mode\r
239         bsf     rcsta,cren      ; 4 cren 1=enable constant reception\r
240                                 ;!!! (and low clears errors)\r
241                                 ; 3 not used / 0 / don't care\r
242         bcf     rcsta,ferr      ; 2 ferr input framing error bit. 1=error\r
243                                 ; 1 oerr input overrun error bit. 1=error\r
244                                 ;!!! (reset oerr by neg pulse clearing cren)\r
245                                 ;you can't clear this bit by using bcf.\r
246                                 ;It is only cleared when you pulse cren low. \r
247         bcf     rcsta,rx9d      ; 0 rx9d input (9th data bit). ignore.\r
248 ;\r
249 ;If you are using a MAX232 that uses\r
250 ;charge pumping, put a delay routine\r
251 ;right HERE, a few seconds\r
252 ;\r
253 ;we need to initialize some things, so do it here.\r
254 ;\r
255         movf    rcreg,w         ;clear uart receiver\r
256         movf    rcreg,w         ; including fifo\r
257         movf    rcreg,w         ; which is three deep.\r
258 ;\r
259         movlw   0               ;any character will do.\r
260         movwf   txreg           ;send out dummy character\r
261                                 ; to get transmit flag valid!\r
262 ;\r
263 main\r
264         bsf     intcon,gie      ;enable interrupts if you are using any!\r
265 ;                               \r
266 loop\r
267         call    ser_in          ;get UART input into W and rx_data\r
268         call    transmitw       ;send W to the UART transmitter\r
269         goto    loop            ;blithely echo characters forever...\r
270 ;*****************************************************************\r
271 ;SER_IN\r
272 ser_in\r
273         btfsc   rcsta,oerr\r
274         goto    overerror       ;if overflow error...\r
275         btfsc   rcsta,ferr\r
276         goto    frameerror      ;if framing error...\r
277 uart_ready\r
278         btfss   pir1,rcif\r
279         goto    ser_in          ;if not ready, wait...\r
280 ;                       \r
281 uart_gotit\r
282         bcf     intcon,gie      ;turn gie off.\r
283         btfsc   intcon,gie      \r
284         goto    uart_gotit\r
285 ;\r
286         movf    rcreg,w         ;recover uart data\r
287         bsf     intcon,gie      ;re-enable interrupts!!\r
288         movwf   rx_data         ;save for later\r
289         return\r
290 ;\r
291 overerror                       \r
292         bcf     intcon,gie      ;turn gie off. \r
293         btfsc   intcon,gie      ;\r
294         goto    overerror       ;\r
295 ;\r
296         bcf     rcsta,cren      ;pulse cren off...\r
297         movf    rcreg,w         ;flush fifo\r
298         movf    rcreg,w         ; all three elements.\r
299         movf    rcreg,w\r
300         bsf     rcsta,cren      ;turn cren back on.\r
301                                 ;this pulsing of cren\r
302                                 ;will clear the oerr flag.\r
303         bsf     intcon,gie      ;enable interrupts.\r
304         goto    ser_in          ;try again...\r
305 ;\r
306 frameerror                      \r
307         bcf     intcon,gie      ;turn gie off.\r
308         btfsc   intcon,gie      ;\r
309         goto    frameerror      \r
310 ;\r
311         movf    rcreg,w         ;reading rcreg clears ferr flag.\r
312         bsf     intcon,gie      ;enable interrupts.\r
313         goto    ser_in          ;try again...\r
314 ;\r
315 ;TRANSMIT subroutine:\r
316 transmit\r
317         movf    tx_data,w       ;copy tx_data to w.\r
318 transmitw\r
319         btfss   pir1,txif\r
320         goto    transmitw       ;wait for transmitter interrupt flag\r
321 gietx   bcf     intcon,gie      ;disable interrupts\r
322         btfsc   intcon,gie      ;making SURE they are disabled!\r
323         goto    gietx\r
324         movwf   txreg           ;load data to be sent...\r
325         bsf     intcon,gie      ;re-enable interrupts\r
326         return\r
327 ;\r
328 TransWt                 ; use to ensure that TX buffer is empty\r
329         bsf     STATUS,RP0\r
330 TxWt    btfss TXSTA,TRMT        ; transmission is complete if hi\r
331         goto    TxWt\r
332         clrf    STATUS  ; RAM Page 0\r
333         return\r
334 ;\r
335 ;----------------------------------------------------------------------\r
336         end\r
337 \r