RS-232 SPI CONTROLLER – part 3.1
by languer , 11th February 2012 at 06:21 AM (1855 Views)
I’ve decided to add a timeout to both the bidirectional routine and the unidirectional routine; and then wrap this up. This project originated from a need to control two SPI devices from ADI. The single unidirectional controller presented before was based on that.
The bidirectional controller was based on a need to control a proprietary SPI interface; this sort of died away, so no good reason to keep pushing it. The interface work for this is done and tested; but I have no real use at the moment. The unidirectional controller on the other hand, I have plenty of use for.
Let’s do a brief flow diagram a few of the things we did previously using Oshonsoft Basic just for the sake of sanity.
> Open UART for 19,200bps – there is no particular reason for this speed; but you certainly wouldn’t want it to make it slower (faster is better ).
> Initialize signals to inputs and outputs – set outputs to desired states.
> Set global flag to indicate when a complete packet has been received (flag_rxcomplete = _false).
> Go into loop waiting to execute main code once a complete packet has been received.
> Inside UART interrupt routine
>> monitor for start of packet (STX),
>> indicate when it is received (flag_rxinprogress),
>> store data as received until packet length is reached (If packet_len = data_buffer_cnt),
>> if all data is received properly indicate the complete packet was received (flag_rxcomplete = _true),
>> if there was a hiccup on the received data, restart the process (flag_rxcomplete = _false).
> Once a complete packet is received,
>> set SPI mode (spi_mode = data_buffer(0)),
>> initialize SPI port (Call spi_init(spi_mode)),
>> send data (Call spi_send(spi_mode, data)),
>> for the bidirectional case
>>> receive data (data = spi_receive(spi_mode)),
>>> send data through UART (Hserout data).
>> restart the process (flag_rxcomplete = _false).
To add a second port (i.e. capable of controlling two independent – but mutually exclusive SPI ports); simply add one more control byte to the serial packet. The serial packet would then look similar to: STX, PACKET_LEN, SPI_PORT, SPI_MODE, DATA (or STX, PACKET_LEN, SPI_MODE, SPI_TX_PACKET_LEN, SPI_RX_PACKET_LEN, SPI_DATA).
Code :'Author: languer (©2012) 'Pin Allocation: 'PIN# Main_Fn Secondary_Fn 'RA0 -> not used 'RA1 -> not used 'RA2 -> not used 'RA3 -> not used 'RA4 -> not used 'RA5 -> not used 'RA6 -> OSC 'RA7 -> OSC 'RB0 -> CTS# (RS232) 'RB1 -> TX_RS232 (PC_RX) 'RB2 -> not used 'RB3 -> not used 'RB4 -> RX_RS232 (PC_TX) 'RB5 -> SPI_CS 'RB6 -> SPI_SCK PGC (Programming clock) 'RB7 -> SPI_SDIO PGD (Programming data) 'Usage Information: 'RS232 Baud Rate: 19200bps 'RS232 Handshake: Uses CTS to indicate to PC that MCU is ready to receive next command '0.1 second timeout on UART communications (packet must not take longer than 0.1sec to arrive) 'Version Info: 'v1 '->data format: STX,PACKET_LEN,SPI_MODE,DATA 'where STX is start of character, 'PACKET_LEN is number of data bytes to expect (maximum packet length: 32_bytes) 'SPI_MODE is the SPI mode (1 or 2) '->SPI modes: 1 or 2 'SPI Mode 1 for data on rising edge of clock, 'SPI Mode 2 for data on falling edge of clock 'CS is active low 'General Configuration 'for external 20MHz Define CONFIG1L = 0x00 Define CONFIG1H = 0x02 Define CONFIG2L = 0x0e Define CONFIG2H = 0x00 Define CONFIG3L = 0x00 Define CONFIG3H = 0x00 Define CONFIG4L = 0x80 Define CONFIG4H = 0x00 Define CONFIG5L = 0x03 Define CONFIG5H = 0xc0 Define CONFIG6L = 0x03 Define CONFIG6H = 0xe0 Define CONFIG7L = 0x03 Define CONFIG7H = 0x40 'Oscillator/Clock Configuration 'Define CLOCK_FREQUENCY = 40 Define CLOCK_FREQUENCY = 20 'HW UART Setup Hseropen 19200 'SPI Definitions Symbol spi_cs = RB5 Symbol spi_sck = RB6 Symbol spi_sdio = RB7 'Variable Declarations Const trisa1 = %11111111 Const trisb1 = %00010100 Symbol pc_tx = RB4 'rs-232 input Symbol pc_rx = RB1 'rs-232 output Symbol pc_cts_n = RB0 'rs232 cts# handshake signal 'rs223 interface constants Const stx = 0x81 'start-of-packet indicator Dim _true As Bit Dim _false As Bit _true = True _false = False Dim flag_rxinprogress As Bit Dim flag_rxcomplete As Bit Dim rxdata As Byte Dim rxdata_len As Byte Dim rxbuffer(32) As Byte Dim rxbuffer_cnt As Byte 'Main Program main: Dim data As Byte Dim cnt As Byte Dim spi_mode As Byte WaitMs 2500 Call init() PIR1.RCIF = _false INTCON.PEIE = _true 'enable peripheral interrupts PIE1.RCIE = _true 'enable RX UART interrupt Enable High 'enable general interrupt While _true 'uart packet received succesfully If flag_rxcomplete = _true Then High pc_cts_n 'mcu busy indication spi_mode = rxbuffer(0) Call spi_init(spi_mode) Low spi_cs For cnt = 1 To rxbuffer_cnt data = rxbuffer(cnt) Call spi_send(spi_mode, data) Next cnt High spi_cs Low spi_sdio Low spi_sck flag_rxcomplete = _false Low pc_cts_n 'mcu idle indication Else 'timeout on uart packet If PIR1.TMR1IF = _true Then Call disable_tmr1() flag_rxinprogress = _false flag_rxcomplete = _false Endif Endif Wend End Proc init() Dim cnt As Byte AllDigital TRISA = trisa1 TRISB = trisb1 flag_rxinprogress = _false flag_rxcomplete = _false rxbuffer_cnt = 0 rxdata_len = 0 High spi_cs Low spi_sdio Low spi_sck Low pc_cts_n 'mcu idle indication 'init tmr1 for 0.1sec timeout T1CON.T1CKPS1 = 1 T1CON.T1CKPS0 = 1 TMR1H = 0x0b TMR1L = 0xd6 T1CON.TMR1ON = 0 PIR1.TMR1IF = 0 End Proc Proc enable_tmr1() 'set for 0.1sec TMR1H = 0x0b TMR1L = 0xd6 T1CON.TMR1ON = 0 PIR1.TMR1IF = 0 End Proc Proc disable_tmr1() 'set for 0.1sec T1CON.TMR1ON = 0 TMR1H = 0x0b TMR1L = 0xd6 PIR1.TMR1IF = 0 End Proc Proc spi_init(mode As Byte) Select Case mode Case 1 'data on rising edge of clock High spi_cs Low spi_sck Low spi_sdio WaitUs 10 Case 2 'data on falling edge of clock High spi_cs High spi_sck Low spi_sdio WaitUs 10 Case Else 'do nothing EndSelect End Proc Proc spi_send(mode As Byte, data As Byte) Dim cnt As Byte Select Case mode Case 1 'data on rising edge of clock For cnt = 0 To 7 Low spi_sck spi_sdio = data.7 data = ShiftLeft(data, 1) High spi_sck ASM: nop ASM: nop ASM: nop ASM: nop ASM: nop ASM: nop Next cnt Low spi_sck Low spi_sdio Case 2 'data on falling edge of clock For cnt = 0 To 7 High spi_sck spi_sdio = data.7 data = ShiftLeft(data, 1) Low spi_sck ASM: nop ASM: nop ASM: nop ASM: nop ASM: nop ASM: nop Next cnt High spi_sck Low spi_sdio Case Else 'do nothing EndSelect End Proc On High Interrupt 'Save System If PIR1.RCIF = _true Then Hserin rxdata If flag_rxinprogress = _false Then If rxdata = stx Then High pc_cts_n 'mcu busy indication flag_rxinprogress = _true flag_rxcomplete = _false rxbuffer_cnt = 0 rxdata_len = 0 Call enable_tmr1() Else 'do nothing Endif Else If rxdata_len = 0 Then rxdata_len = rxdata Else rxbuffer(rxbuffer_cnt) = rxdata rxbuffer_cnt = rxbuffer_cnt + 1 If rxdata_len = rxbuffer_cnt Then rxbuffer_cnt = rxbuffer_cnt - 1 flag_rxinprogress = _false flag_rxcomplete = _true Endif Endif Endif Endif PIR1.RCIF = _false Resume
How to arrive at the proper TMR1 values:
UART Timeout Calculation.png