- Blog entry posted in 'Electronics and Other Ramblings...', February 11, 2012.
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:
70249Comments