Search This Blog

Sunday, August 1, 2010

Bootloader - Send and Receive Protocol

    Worked on the bootloader a bit this weekend. This was my first time using the built in AUSART modules of the 16f88 rather than bit-banging routines. Got the pic to transmit pretty quickly at 8MHz internal clock with data rate of 19,200. Then I wrote a simple python program to simulate the protocol I wanted for the bootloader. The program fires up and starts sending the start bit, 0xC1, over and over. After each send, it listens for 20ms for a response of 'K'. It times out after one minute. On the pic side, the program fires up the USART in ansynch mode and listens for 1 second for the start code. If it hears it, it validates that it is the right code. It sends 'K' back, and it then transmits its message ( 'Hello, world!', of course ).
    The programs looked great, but they were not working. My most bone-headed move of late, I forgot to hook up the receiving wire! Incidentally, I use some transistors, resistors, and a diode for my converter. I tried the Max232 chip for this, but I got way too much noise on the circuit. I really need to figure that out sometime....
Anyway, the program works great. Below is the code for posterity. I hope it comes through clean this time. As mentioned in previous posts, the Python code uses the uspp code for serial transmission. Great bit of work. I am indebted to the developer for saving me the trouble of learning raw serial transmission in Linux. And it will work with Windoze.
    Edit:  Looking over past posts, I realized I changed direction again. I am writing my bootloader from whole cloth, stealing from the tinyBld code. I am not going to bit bang. I'll use the built in modules. Robot code will shift the motors to RB0 and 1 and RB7 and 6, leaving RB5(TX) and 2(RX) free for ICSP bootloading.
Pic Code:

;******************************************************************************
;
; Filename: periph.asm

; Date: 20010.07.31

; File Version: 1.0.0

;

; Author: Tom Hunt
;

;******************************************************************************

; NOTES: Echo some ascii over serial to PC usart.
; Use the built-in usart module.
;
; Pic 16f88 running at 8MHz
; data rate: 19200 with spbrg high
;
;******************************************************************************
; CHANGE LOG:
; Backed by a python program (talk.py), the python program sends the start
; char every 1ms and then listens for ~20ms. The pic fires up and listens
; for the start char. When it hears it and validates that it is the
; start char, it sends back a 'K'. The python prog hears the 'K' and
; goes into eternal listen mode. The pic sends the message, 'Hello, world!'
; The python prog prints this to the screen.
;
;******************************************************************************



list R=DEC, p=16f88 ; list directive to define processor

#include <p16f88.inc> ; processor specific variable definitions



;Program Configuration Register 1
__CONFIG _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_ON & _PWRTE_ON & _WDT_OFF & _INTRC_IO

;Program Configuration Register 2
__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF


errorlevel 1, -302 ; suppress Register in operand not in bank 0 -- Get rid of this during development


;******************************************************************************
; VARIABLE DEFINITIONS
;******************************************************************************

#define STARTSIGNAL 0xC1

CBLOCK 0x20
count
buffer:20
i
tIndex
ENDC

CBLOCK 0x70 ; interupt context-saving vars here see datasheet chp 15
w_temp
status_temp
pclath_temp
ENDC

;******************************************************************************
; MACRO DEFINITIONS *
;******************************************************************************

bank0 MACRO
bcf STATUS, RP1
bcf STATUS, RP0
ENDM

bank1 MACRO
bcf STATUS, RP1
bsf STATUS, RP0
ENDM


;******************************************************************************
; RESET VECTOR *
;******************************************************************************

ORG 0x000 ; processor reset vector
nop
nop
nop

goto main ; go to beginning of program



;******************************************************************************
; INTERRUPT VECTOR
;******************************************************************************

ORG 0x004 ; interrupt vector location
MOVWF w_temp ;Copy W to TEMP register
SWAPF STATUS, W ;Swap status to be saved into W
CLRF STATUS ;bank 0, regardless of current bank, Clears IRP,RP1,RP0
MOVWF status_temp ;Save status to bank zero STATUS_TEMP register
MOVF PCLATH, W ;Only required if using page 1
MOVWF pclath_temp ;Save PCLATH into W
CLRF PCLATH ;Page zero, regardless of current page
;
;(ISR) ;(Insert user code here)
;
MOVF pclath_temp, W ;Restore PCLATH
MOVWF PCLATH ;Move W into PCLATH
SWAPF status_temp, W ;Swap STATUS_TEMP register into W
;(sets bank to original state)
MOVWF STATUS ;Move W into STATUS register
SWAPF w_temp, F ;Swap W_TEMP
SWAPF w_temp, W ;Swap W_TEMP into W


retfie ; return from interrupt


;******************************************************************************
; INITIALIZATION CODE
;******************************************************************************
; no interrupts, all digital i/o, 8MHz internal clock
; usart configured for 19,200 data rate
; brgh set to high speed
; spbrg = clock / ( dataRate * 16 * ( 4^0 ) ) - 1
; spbrg = 8e6 / ( 19200 * 16 * 1 ) - 1 = 25.042
init
bank0
clrf INTCON
clrf ADCON0 ; all digital
bank1
movlw 0x00
movwf ANSEL ; go all digital
movlw b'01110010' ; bits for OSCCON internal clock at 8MHz 0111 0000
movwf OSCCON^0x080
movlw 0x19 ; decimal 25
movwf SPBRG^0x080
; txsta gets b'00100100' enables asynch transmit and fast(brgh)
movlw 0x00 ^ ( ( 1 << TXEN ) + ( 1 << BRGH ) )
movwf TXSTA ^ 0x080
; tmr0 set up
movlw 0x00 ^ ( ( 1 << PS2 ) + ( 1 << PS1 ) + ( 1 << PS0 ) ) ; b'00000111' tmr0 prescaler 1:128 ( and the 2x factor )=256
movwf OPTION_REG ^ 0x080
bank0
; rcsta gets b'10010000' enables serial port and continuous receive
movlw 0x00 ^ ( ( 1 << SPEN ) + ( 1 << CREN ) )
movwf RCSTA
return

;******************************************************************************
; RS232 CODE
;******************************************************************************
; receive tries to receive a byte for one second from RCREG
receive
movlw 0x28 ; count = 40
movwf count
timeIt
; tmr0 will give ~25ms @ 8MHz -> 256*195= 49920 instrs
movlw 0x03D ; 256-195=61
movwf TMR0
testRcReg
btfsc PIR1,RCIF ; if set, new byte in RCREG
goto gotAByte
movf TMR0,W
btfss STATUS,Z ; wait for timeout
goto testRcReg
decrementCount
decfsz count,F
goto timeIt
; if we get here, we've timed out after 1 second
goto errorAndExit
gotAByte
movf RCREG,W
return

sendByte
movwf TXREG
bank1
btfss TXSTA,TRMT ; when TRMT is set, transmission is completed
goto $ - 1
bank0
return

;******************************************************************************
; MAINLINE CODE
;******************************************************************************

main
call init
bank1
movlw 0x00 ^ ( 1 << 2 )
movwf TRISB ^0x080 ; enable all PORTB for output except PORTB,2 ( RX )
movlw b'11111011' ; set PORTA to Input except for A2
movwf TRISA ^ 0x080
bank0
waitForSignal
movlw 0x00 ; init W reg
call receive
sublw STARTSIGNAL
btfss STATUS,Z
goto errorAndExit
movlw 'K'
call sendByte
sendMsg
movlw 0
movwf i
sayHi
call table
iorlw 0
btfsc STATUS, Z ; if end of table, end
goto done
call sendByte
incf i, F
movf i, W ; increment index
goto sayHi

errorAndExit ; placeholder
bank0
bcf RCSTA,SPEN ; desactivate UART

clrf PCLATH
done
goto $

;******************************************************************************
; TABLE DATA
;******************************************************************************

table
movwf tIndex ; save table index
movlw HIGH tableEntries ; get the current high page
movwf PCLATH ; put this in PCLATH to move us to the right page
movf tIndex, W ; move the index asked for into w
addlw LOW tableEntries ; compute the offset for the block
btfsc STATUS, C ;
incf PCLATH, F ; if in next page, increment PCLATH
movwf PCL ; put the correct addr in PCL
tableEntries
dt "Hello, world!\0"

END

Python Code:

#!/usr/bin/python

from uspp import *
from time import sleep as sleep

startByte = 0xC1
ok = 'K'

def init( tty ):
tty = SerialPort("/dev/ttyS0", 0, 19200 )
tty.flush() # discard unread bytes
return tty

def sendStart( tty ):
print 'Sending start byte ', 0xC1
gotStart = False
ch = ''
count=800
while count > 0:
tty.write( chr( startByte ) )
tries = 20
while tries > 0:
ch = tty.read()
if ok == ch:
gotStart = True
return True
sleep(.001)
tries = tries - 1
print count, ' ch=\'', ch, '\''
count = count - 1
print ''
if True == gotStart:
print 'Got ok from pic'
else:
print 'Never got ok back from pic'
return gotStart



def listen( tty ):
# ignore timeouts and read
print 'Endless listen:'
ch = ''
try:
while True:
ch = tty.read()
if ch != '':
print ch
except SerialPortException:
pass

def main():
print 'Starting...'
tty = None
tty = init( tty )
ready = sendStart( tty )
if True == ready:
listen( tty )
else:
print 'Never got ready. Aborting...'
tty.__del__()

main()