- Blog entry posted in 'Electronics and Other Ramblings...', January 15, 2012.
This project initiated by the need to control some SPI devices through a PC. A few home-made programs to control some nifty frequency synthesizes were being developed and required an interface to provide communications between the PC and the devices themselves. To accomplish this, two options were looked at: (1) using the parallel port on the PC to directly control the SPI devices, (2) using the serial port on the PC to indirectly control the SPI devices. Option 1 involves bit-banging the parallel port lines to create the SPI interface. Option 2 involves using the serial port to communicate with an intermediate device which translates the serial port communication (RS232) to SPI and vice-versa. Option 1 has been used many times over with great success, but it places strict requirements on the PC-software to toggle the lines appropriately and is quite “boring”. Option 2 potentially makes the PC software less complex and it can use a PIC to act as the interface. Which, of course, made option 2 the more “logical” choice ;).
The development is not complete yet, so this will be called part 1. This first part introduces the PIC hardware and connections which will be the framework for the project. The following components will be used:
• PIC18F1320
• FTDI - DS_TTL-232R-5V (RS232 –to- TTL cable)
The schematic is quite simple and shown below. The PIC is able to interface through RS232 with the PC and control an SPI device based on the commands received from the PC. The RS232 control adds the CTS# line to provide handshaking between the PC and the PIC. This is not strictly necessary for applications only controlling the SPI device, but for applications which require to communicate to and from the SPI device, this provides a much easier interface.
60179
This initial part of only implements the ability to control the SPI device (for simple control of SPI devices not requiring information to be sent back to the host). Code is presented below. As an example of possible uses, a slightly more elaborate version of this was used to control a DDS and PLL combination from one PC application (one PIC and two SPI devices).
Code
'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
'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 10MHz w PLL (40MHz)
Define CONFIG1L = 0x00
Define CONFIG1H = 0x06
Define CONFIG2L = 0x0a
Define CONFIG2H = 0x00
Define CONFIG3L = 0x00
Define CONFIG3H = 0x80
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
'Debug Option
Dim debug As Bit
debug = True
debug = False
'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
If debug = _false Then
WaitMs 2500
Endif
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
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
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
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
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