--zYM0uCDKw75PZbzx Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Hi everyone, In the past, members of the list have been good at pointing out programming mistakes that I've made and I hope this time is no different. :) I'm trying to learn how to create a software UART which I want to be interrupt driven. I have a transmit routine that uses delay loops written and it works fine. My attempt at an interrupt driven transmit doesn't work and here is the symptom: no matter what is transmitted, 0xFE is received. The RS-232 parameters are 1200N1. As a guide, the interrupt driven routine is named "trasmit" and the relevant labels in the ISR are uart_3 and uart_4. The delay based routine is named "transmit2" and this routine works (Coding this was my fall back after failing to get the "transmit" routine to work.) The main loop sends a sequence of bytes starting with '!'. I would be very grateful with being helped to discover my mistake(s). The program source code is attached. BTW: I found out the hard way that setting the OSCCAL value is very important for this kind of application! Thanks! Matthew -- "With reasonable men I will reason; with humane men I will plead; but to tyrants I will give no quarter." -- William Lloyd --zYM0uCDKw75PZbzx Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="suart.asm" ; This project is an implementation of a 1200 baud software UART. ; Pin usage: ; GP2 - receive input pin ; GP5 - transmit output pin processor 12f629 include __config _MCLRE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT & _BODEN_OFF ; Constants for the GPIO pins #define LED2 0 #define RX 2 #define LED1 4 #define TX 5 ; These constants give meaning to the bits of the UART state byte uartst #define TRMT 0 ; When set a transmit is in progress. #define RECV 1 ; When set a byte has been received and can be read in the ; rx_data byte. #define RWORKING 2 ; When set the receiver is working to get a byte. #define FERR 3 ; A framing error occured (missing start or stop bit.) #define START_ERR 4 ; Specific framing error values for debugging. #define STOP_ERR 5 ; ditto. udata_shr rx_data res 1 tx_data res 1 pJ res 1 pK res 1 shadow res 1 uartst res 1 ; Holds the UART state state res 1 ; The receive and transmit FSM's use this variable. bit_count res 1 ; The number of bits that been received. tmp res 1 ; These variables are used to the uC state during interrupts. w_temp res 1 status_temp res 1 pclath_temp res 1 fsr_temp res 1 PROG code clrf STATUS clrf shadow call device_setup goto main ; The ISR begins here. (It's funny that the 12F629 linker script doesn't ; provide a seperate section for the ISR vector...) ; ; Three instruction cycles have already passed since an interrupt was flagged. movwf w_temp ; copy W to temp register swapf STATUS, W ; swap status to be saved into W movwf status_temp movf PCLATH, W movwf pclath_temp movf FSR, W movwf fsr_temp ; Saving the CPU state requires 7 instruction cycles. ; Point 1 ; ISR code goes here ... ; Begin by testing for which interrupts occurred. btfss INTCON, T0IF goto test_ioc ; 2 inst. cycles to reach here from point 1. Point 4 banksel GPIO ; Goto the appropriate state clrf PCLATH movf state, W addwf PCL, F ; 2 (I think!) goto uart_0 ; 2 Handle the start bit goto uart_1 ; Handle the eight bits goto uart_2 ; Handle the stop bit ; The next jumps are states used by the transmit routines. goto uart_3 ; This state transmits each bit. goto uart_4 ; This state asserts the stop bit. ; Test the receive pin to see that it is low (middle of the start bit). uart_0 ; From point 1 to here is 9 inst. cycles btfsc GPIO, RX goto start_bit_failure ; The start bit was high instead of low. ; The start bit was low, setup the timer. banksel TMR0 movlw .54 ; calculated value is 54.25 movwf TMR0 ; From testing the input value to here requires 5 inst. goto leave_uart_0 start_bit_failure ;bsf uartst, FERR bsf uartst, START_ERR bsf uartst, RECV ; Finished. bcf uartst, RWORKING ; Signal that the receiver is not working. bcf INTCON, T0IE ; Turn off the timer interrupt. leave_uart_0 bcf INTCON, T0IF ; Clear the timer interrupt flag incf state, F ; Next state is state 1, uart_1 movlw .7 movwf bit_count ; Receiving bit 0 first. goto leave_isr ; Receive and store each of the eight bits. uart_1 ; From point 1 to here is 9 inst. cycles. ; Sample the value on the RX pin. btfsc GPIO, RX goto receive_high_bit receive_low_bit clrc ; clear the carry bit goto receive_bit receive_high_bit setc ; set the carry bit receive_bit rrf rx_data, F ; The value of the carry bit is shifted to the MSB. ; 6 cycles to here from uart_1 tag. ; Setup the proper timer value. banksel TMR0 movlw .55 ; calculated value is 55.25 movwf TMR0 ; Modify the bit_count and and state value. bcf INTCON, T0IF ; Clear the timer interrupt flag. decfsz bit_count, F goto leave_isr ; Leave the state value unchanged. incf state, F ; The next state, uart_2, checks the stop bit. goto leave_isr ; Handle the stop bit. uart_2 btfss GPIO, RX ;bsf uartst, FERR ; Signal that the stop bit wasn't found. bsf uartst, STOP_ERR ; Reception is done, so clean up. bcf INTCON, T0IE ; Disable the timer interrupt. bcf INTCON, T0IF ; Clear the timer interrupt flag. bsf uartst, RECV ; Finished. bcf uartst, RWORKING ; Signal that the receiver is not working. goto leave_isr test_ioc ; 3 inst. cycles to reach here from point 1. Point 2 btfss INTCON, GPIF goto leave_isr ; no other interrupts to check... ; 2 inst. cycles occurred to reach here from point 2. Point 3 banksel TMR0 movlw .162 ; computed value is 161.5 movwf TMR0 ; 3 inst. cycles occurred to reach here from point 3. bcf INTCON, T0IF ; Clear the timer interrupt flag bsf INTCON, T0IE ; Enable the timer interrupt bcf INTCON, GPIE ; Disable the change on interrupt bcf INTCON, GPIF ; Clear the change on interrupt flag goto leave_isr ; This is the first state in the transmit routine. Each bit to be ; transmitted is send out here. uart_3 rrf tx_data, F ; 1 skpc ; 2 goto send_low_bit send_high_bit bsf shadow, TX ; 1 goto set_tx_bit ; 2 send_low_bit bcf shadow, TX set_tx_bit banksel GPIO ; 1 movf shadow, W ; 1 movwf GPIO ; 1 total = 9 banksel TMR0 movlw .57 ; Calculated value was 56.75 movwf TMR0 ; 3 bcf INTCON, T0IF ; Clear the interrupt flag decfsz bit_count, F incf state, F ; All the bits have been set, advance to the next state. goto leave_isr ; Assert the stop bit and clean up. ; TODO: add another state that doesn't clear TRMT until the stop bit ; period has ended. uart_4 bsf shadow, TX banksel GPIO movf shadow, W movwf GPIO bcf INTCON, T0IE bcf INTCON, T0IF bcf uartst, TRMT leave_isr movf fsr_temp, W movwf FSR movf pclath_temp, W movwf 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 main ; Main loop program code begins here. movlw '!' ; The first printable (visable) ASCII character. movwf tmp loop movf tmp, W incf tmp, F ;movlw 0xAA call transmit ; The interrupt driven routine (doesn't work). ;call transmit2 ; The delay based routine (works). movlw .150 call pdelay call flash_led1 ; An "it's working" indicator. goto loop ; Initialize the device peripherals and special function registers. ; device_setup ; Configure the interrupt settings. movlw B'01000000' ; 0 disable all interrupts at the moment ; 1 enable peripheral interrupts ; 0 disable the TMR0 interrupt ; 0 disable the GP2/INT external interrupt ; 0 disable the GPIO IOC interrupt ; 0 clear the TMR0 overflow flag ; 0 clear INTF ; 0 clear the IOC flag movwf INTCON ; Disable the voltage reference. banksel VRCON clrf VRCON ; Calibrate the internal RC oscillator. call 0x03ff movwf OSCCAL ; The only inputs are GP3/MCLR and GP2, the receive pin. ; banksel TRISIO ; In same bank as VRCON movlw B'00001100' movwf TRISIO bsf IOC, RX ; Enable interrupt on change for the receive pin. ; Turn the comparator off. banksel CMCON movlw B'00000111' movwf CMCON bsf shadow, TX ; Assert the stop condition. movf shadow, W movwf GPIO ; Setup the timer and other features that use OPTION_REG and INTCON. clrwdt ; suggested by the datasheet to prevent reset banksel OPTION_REG movlw B'11010001' ; 1 pull-ups are disabled. ; 1 interrupt on rising edge of GP2/INT ; 0 TMR0 will use the internal clock ; 1 TMR0 edge select, not used ; 0 the pre-scaler is assigned to TMR0 ; 001 use the 1:4 pre-scaler rate movwf OPTION_REG banksel TMR0 clrf TMR0 clrf uartst ; Clear all of the state bits. bsf INTCON, GIE ; Enable global interrupts return ; Take the value in W and transmit that value. While working, the TRMT bit ; in the uartst byte is set; this bit will be cleared when tranmission is ; finished. The data to be transmitted is stored in the tx_data byte and ; this byte is modified during transmission. ; transmit movwf tx_data movlw .3 ; The first state for the transmit FSM movwf state ; Start by sending the start bit. bcf shadow, TX movf shadow, W banksel GPIO movwf GPIO ; Now configure the timer to interrupt when the start bit should end. banksel TMR0 movlw .57 movwf TMR0 ; 3 cycles + 2 where TMR0 doesn't increment. bsf uartst, TRMT movlw .8 movwf bit_count bcf INTCON, T0IF ; Clear the flag and enable the TMR0 interrupt. bsf INTCON, T0IE transmit_wait btfsc uartst, TRMT goto transmit_wait return ; This routine configures the IOC setting for the receive pin and then waits ; until a complete byte has been received. That byte is stored in rx_data. If ; an error occured, such as a missing start bit, then the appropriate bit in ; the uartst byte is set. This routine does not return until a byte has been ; received or an error has occured. ; ; If a framing error occured then the FERR bit in the uartst byte is set. ; receive clrf uartst bsf uartst, RWORKING ; Signal that the receiver is working clrf state ; The receive state machine begins at state 0. bcf INTCON, GPIF ; Clear the change on interrupt flag, just in case bsf INTCON, GPIE ; Enable the change on interrupt receive_wait btfss uartst, RECV goto receive_wait return ; A delay base transmit routine that works! (As opposed to the interrupt driven ; transmit routine...) ; transmit2 movwf tx_data movlw .8 movwf bit_count bcf shadow, TX banksel GPIO movf shadow, W movwf GPIO ; start bit movlw .164 ; 1200 baud call psldelay tx_loop rrf tx_data, F ; 1 skpc ; 2 goto tsend_low_bit tsend_high_bit bsf shadow, TX ; 1 goto tset_tx_bit ; 2 tsend_low_bit bcf shadow, TX tset_tx_bit banksel GPIO ; 1 movf shadow, W ; 1 movwf GPIO ; 1 total = 9 movlw .163 ; 1200 baud call psldelay decfsz bit_count, F ; 1 goto tx_loop ; 2 bsf shadow, TX banksel GPIO movf shadow, W movwf GPIO ; stop bit movlw .165 ; 1200 baud call psldelay return ; A single loop delay. Calling the routine and its return is accounted for. ; delay*Hz/4 = 2+1+1+2+5*p = 6+5*p psldelay movwf pJ ; 1 sloop nop ; 1 nop ; 1 decfsz pJ, F ; 1 (2) goto sloop ; 2 return ; 2 ; A parameterized delay ; ; To determine the necessary start value (the value in W) to load use this ; formula: delay*Hz/4 = 2+1+(1+1+2+1+1+2)*p^2+(2+2+1+2)*p+1 ; = 4+7*p+8*p^2 p is the parameter in W and delay is the ; required delay length in seconds, and Hz is the clock frequency. The formula ; considers both the time to call the routine and its return. ; ; RAM variables used: pK, pJ. W is not changed. ; pdelay movwf pJ ; 1 pjloop movwf pK ; 1 pkloop decfsz pK, f ; 1 (2) goto nothing ; 2 goto jbottom ; 2 nothing clrwdt ; 1 nop ; 1 goto pkloop ; 2 jbottom decfsz pJ, f ; 1 (2) goto pjloop ; 2 return ; 2 flash_led1 bsf shadow, LED1 banksel GPIO movf shadow, W movwf GPIO movlw .255 call pdelay bcf shadow, LED1 movf shadow, W movwf GPIO return flash_led2 bsf shadow, LED2 banksel GPIO movf shadow, W movwf GPIO movlw .255 call pdelay bcf shadow, LED2 movf shadow, W movwf GPIO return end --zYM0uCDKw75PZbzx Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline -- http://www.piclist.com PIC/SX FAQ & list archive View/change your membership options at http://mailman.mit.edu/mailman/listinfo/piclist --zYM0uCDKw75PZbzx--