Programming PIC MCUs in BASIC

Chapter 9: Communications

  * Introduction
* 9.1 USART and Software UART
* 9.2 SPI and Software SPI
* 9.3 I2C and Software I2C
* 9.4 Manchester Code
* 9.5 RS485
* 9.6 OneWire
* 9.7 CAN & CANSPI

Introduction

When you start writing real-life programs for PIC, you may soon find yourself having a “lack of space” feeling, that you need just a few more pins and a couple of dozens of bytes to do the job. You might want to solve this problem by transferring to bigger PIC, and the problem will be solved… for a little while. Or, you just happened to have found a beautiful, brand-new humidity sensor that does “all the job by itself”, leaving you just to connect it to PIC and pick up the data… That is, if you know how to do that. If you’ve come up with these or similar problems, it really is time for you to teach your PIC and yourself some communication lessons.

There are many ways for two machines to communicate these days, and PICs are generally well equipped for the task. Depending on the job to be done, data exchange – what communication basically is – can be done in a fairly simple manner, such as the SPI communication, but can extend to an entire network of various devices – MCUs, PCs, cameras, “intelligent” sensors, etc. With increased demands, rules of device behaviour must encompass a wider set of possible scenarios and therefore protocols dramatically grow in complexity (CAN, for instance).

Consider your needs carefully before jumping to CAN-driven solutions. There are different communication methods, offering lot of possibilities at varying levels of complexity. The rule of “sacred simplicity” remains here as well: do not use more complex communication tools than you really need to!

In this chapter, you’ll get yourself acquainted with various means of communication that are being used by the PIC MCUs, and the ways to access and extend them from the BASIC programming language. You may have already noticed that some of the communication devices also have their software counterparts, meaning you can have the same communication functionality that is achieved through a set of software routines that can be used through the BASIC programming language. You should be using software communication when you have utilized all the real (hardware) communication resouces but still need an extra communication line.

9.1 USART and Software UART

9.1.1 USART

USART stands for the Universal Synchronous/Asynchronous Receiver/Transmitter. It may sound mysterious, but actually this is the most frequent communication device used today throughout the computer world: microcontrollers, (some) cell phones, barcode readers, PCs…

First, let’s see what all those words stand for:

* Universal means that it can be used with a wide scope of devices
* Synchronous/Asynchronous shows whether or not the devices that communicate with each other require an external synchronization line (the clock). This device, present in most PICs, can do it both way. The Asynchronous mode (without the common clock) is easier to implement, although it is generally slower than the synchronous. It is also the older way – older versions of PIC did not have the possibility of working in synchronous mode, therefore the devices they had were more appropriately named as UART (without S)
* Receiver/Transmitter means that this device can receive and transmit (send) data simultaneously. It is also called the two-way or duplex communication.

The USART itself can be set to communicate in many ways. The most frequent one is, of course, the one that helps your PIC talk to the PC. This old standard, known as the RS232, is understood by the 99.9% of PCs, although is lately being superseded by the USB standard. You can see from the image below how to connect your PIC to PC. You have to add an extra IC in between (MAX232) that simply adjusts the USART voltage levels to those required by the RS232 standard.
Programming PIC MCUs in BASIC
As you can see, you need two wires for this communication: one to receive data (named Rx), and the other one to send (transmit) it (Tx). Here follows a simple BASIC program that gets (receives) data from the PC and sends it back to it:

program RS232com
dim  received_byte as byte
main:
  USART_Init(2400)                  ' Initialize USART module
  while true
    if USART_Data_Ready = 1 then    ' If data is received
       received_byte = USART_Read   '     read received data,
       USART_Write(Received_byte)   '     send it back via USART
    end if
  wend
end.

When you compile this program and write it into the PIC, you can open some communication terminal program on your PC, such as Windows Terminal, set up the connection parameters, open connection, and send some characters to PIC. If everything is OK, it will send you that same data back.

9.1.2 Software UART

If you have used up hardware USART and need another communication channel (for example if you wish to communicate with two PCs) or you do not have an USART device at all (on smaller PICs), you can use the software UART. As its name implies, it uses a set of software routines to simulate the real, hardware UART device. Working with software UART is almost the same as it is with USART, the only difference being the initialization. You can see it from the following example:

program soft_uart_test
dim received_byte as byte
dim rec_ok as byte
main:
  Soft_UART_Init(PORTB, 1, 2, 2400)           ' initialize software UART
                                               ' you have to tell PIC which pins
                                               '    to use as Rx and Tx
  while true
    do
      received_byte = Soft_UART_Read(Rec_ok)  ' read received data
    loop until rec_ok
       Soft_UART_write(received_byte)         ' send data via UART
  wend
end.

9.2 SPI and Software SPI

SPI or Serial Peripheral Interface is probably the simplest communication style between your PIC and the outside world. It is, however, limited to communicating between the chips, meaning that cables have to be as short as possible. The basic idea is to allow for two chips to exchange information in a master-slave manner, the master initializing communication, selecting the slave to communicate with, and providing a clock for synchronization. In most cases your PIC will be the master and will ask other, less “intelligent” chips for some data, issue commands to them, etc.

The given example uses BASIC’s SPI routines to access the max7219 chip, which is used to drive up to eight 7-segment displays. All this is achieved by using a single pin from PIC (RC1) for communication. For chip select and clock transfer purposes, pins SDO, SDI and SCK on both PICs have to be inter-connected as well.

program SPI
include "m7219.pbas"
dim i as byte
main:
     SPI_Init               ' Standard configuration
     TRISC = TRISC and $FD
     max7219_init           ' Initialize max7219
     PORTC.1 = 0            ' Select max7219
     SPI_Write(1)           ' Send address (1) to max7219
     SPI_Write(7)           ' Send data (7) to max7219
     PORTC.1 = 0            ' Deselect max7219s
end.

And this is how the m7219.bas module looks like:

module m7219
sub procedure max7219_init
  PORTC = PORTC and $FD   ' SELECT MAX
  SPI_Write($09)          ' BCD mode for digit decoding
  SPI_Write($FF)
  PORTC = PORTC or 2      ' DESELECT MAX
  PORTC = PORTC and $FD   ' SELECT MAX
  SPI_Write($0A)
  SPI_Write($0F)          ' Segment luminosity intensity
  PORTC = PORTC or 2      ' DESELECT MAX
  PORTC = PORTC and $FD   ' SELECT MAX
  SPI_Write($0B)
  SPI_Write($07)          ' Display refresh
  PORTC = PORTC or 2      ' DESELECT MAX
  PORTC = PORTC and $FD   ' SELECT MAX
  SPI_Write($0C)
  SPI_Write($01)          ' Turn on the display
  PORTC = PORTC or 2      ' DESELECT MAX
  PORTC = PORTC and $FD   ' SELECT MAX
  SPI_Write($00)
  SPI_Write($FF)          ' No test
  PORTC = PORTC or 2      ' DESELECT MAX
end sub
end.

Using software SPI is similar to any software communication – the software contained in BASIC routines simulates the real device. Having that in mind, you must be careful with initialization routines. Here’s an example that uses software SPI to “talk to” another chip (the LTC1290 multiple channel 12-bit A/D converter), this time over the RD1 pin.

'********************************************************************* **********
' microcontroller : P18F452
'
' Project: LTC1290
'
' This code demonstrates using software routines for SPI communication.
' Also, this example demonstrates working with ADC LTC1290.
' CS pin LTC1290 should be connnected to RD1
' and SDO, SDI, SCKL pins should be appropriately connected.
' Result of AD conversion is printed on LCD display.
' Tested on 16F877A and 18F452
'********************************************************************* **********
program ltc1290
dim low_res as byte
dim high_res as byte
dim t as char[17]
' Formats and prints result on LCD
sub procedure Display_ADval
dim tmp as word
dim value as longint
  tmp = word((high_res << 4)) + word(low_res  4)
  value = (5000*tmp)  12
  tmp = word(value)
  t[1]= 48 + word(tmp div 1000)
  t[3]= 48 + word((tmp div 100) mod 10)
  t[4]= 48 + word((tmp div 10) mod 10)
  t[5]= 48 + word(tmp mod 10)
  t[2]= 46
  t[0]= 5                          'length of the string is in the zero element
  LCD_out(2, 1, t)
end sub

main:

    PIE1   = 0
    INTCON = 0                     ' disable interrupts
    TRISB  = 0                     ' designate portb as output
    LCD_Init(PORTB)                ' initialize LCD on PORTB
    LCD_Cmd(LCD_CURSOR_OFF)        ' LCD cursor off
    low_res = 110
    high_res = 1
    Display_ADval
    Soft_SPI_Config(PORTD,7,6,5)
    SetBit(PORTD,1)
    ClearBit(TRISD,1)                 ' pin RD1 is output
    t = "mikroElektronika"
    LCD_Out(1, 1, t)                  ' print "mikroElektronika" on LCD
    while true
        ClearBit(PORTD,1)             ' select LTC1290
        high_res = Soft_SPI_Read(255) ' get upper byte of AD conversion
        low_res = Soft_SPI_Read(0)    ' get 4 least significant bits of AD conversion
        SetBit(PORTD,1)               ' deselect LTC1290
        Display_ADval                 ' format and print value on LCD
        Delay_ms(1)                   ' wait 1 ms before next conversion
    wend
end.

9.3 I2C and Software I2C

9.3.1 I2C

I2C or I²C stands for Inter-Integrated Circuit and is pronounced I-squared-C. This is another way chips (i.e. PICs) can communicate between themselves. It is simple, cheap, and somewhat slow (from the chip’s point of view), and has been widely used for more than twenty years now. Similar to USART and SPI, the data is transferred in serial manner, i.e. bit-by-bit, through a single wire. Unlike the SPI, this standard uses only two bi-directional wires in total, one for data and one for the clock. It also allows for over 1000 chips to be inter-connected over only two wires, and it is designed to allow the chips to be safely added to or removed from the bus without turning off the power. This and the fact that it has been invented and supported by Philips, can explain such a popularity of this standard.

From BASIC user’s point of view, using I2C looks pretty much like using the SPI. You have to:

* Initiate communication between ICs;
* Read or write some data (in byte-sized chunks);
* Terminate communication

We will demonstrate this by connecting a 24c02 EEPROM to our PIC. Here is how to connect them:
Programming PIC MCUs in BASIC
And here’s the program:

‘ Example of communication with 24c02 EEPROM

program i2c_test
dim  EE_adr as byte
dim  EE_data as byte
dim  jj as word
main:
    I2C_init(100000)    ' initialize full master mode
    TRISD = 0           ' PORTD is output
    PORTD = $FF         ' initialize PORTD
    I2C_Start           ' issue I2C start signal
    I2C_Wr($A2)         ' send byte via I2C(command to 24cO2)
    EE_adr  = 2
    I2C_Wr(EE_adr)      ' send byte(address for EEPROM)
    EE_data = $AA
    I2C_Wr(EE_data)     ' send data(data that will be written)
    I2C_Stop            ' issue I2C stop signal
    for jj = 0 to 65500 ' pause while EEPROM writes data
        nop
    next jj
    I2C_Start           ' issue I2C start signal
    I2C_Wr($A2)         ' send byte via I2C
    EE_adr = 2
    I2C_Wr(EE_adr)      ' send byte (address for EEPROM)
    I2C_Repeated_Start  ' issue I2C signal repeated start
    I2C_Wr($A3)         ' send byte (request data from EEPROM)
    EE_data = I2C_Rd(1) ' read data
    I2C_Stop            ' issue I2C stop signal
    PORTD = EE_data     ' show data on PORTD
noend:                  ' endless loop
    goto noend
end.

You can find more details on I2C routines’ syntax in Chapter 5: Built-in and Library Routines. You shouldn’t bother yourself too much about the meaning of the I2C_Start and I2C_Repeated_Start routines – that’s the way your PIC performs the initialization of its I2C device.

9.3.2 Software I2C

Working with software I2C is very similar to working with the “real one”. You can see it for yourself from the following example, which performs exactly the same thing as the previous one.

'********************************************************************* *********
' microcontroller P16F877A
'
' Project: soft_i2c_test
' This project is designed to work with PIC 16F877A
' with minor adjustments, it should work with any other PIC MCU
' that has MSSP module.
'
' This code demonstrates comunication with 24c02 EEPROM
' connected to appropriate pins on PORTD.
' After the byte is read, it is displayed on PORTC.
'********************************************************************* *********
program soft_i2c_test
dim  Addr as byte
dim  EE_ByteOut as byte
dim  EE_ByteIn as byte
main:
    TRISD = $FF
    TRISC = 0
    PORTC = $FF
    Addr =   2
    EE_ByteOut = $9F
    Soft_I2C_Config(PORTD,4,3)    ' initialize I2C, 100 kHz clk, full master mode
    Soft_I2C_Start                ' issue I2C start signal
    Soft_I2C_Write($A2)           ' send byte via I2C  (command to 24cO2)
    Soft_I2C_Write(2)             ' send byte (address of EEPROM location)
    Soft_I2C_Write(EE_ByteOut)    ' send data (data to be written)
    Soft_I2C_Stop                 ' issue I2C stop sinal
    PORTC = $B0
    Delay_ms(2000)
    Soft_I2C_Start                ' issue I2C start signal
    Soft_I2C_Write($A2)           ' send byte via I2C  (command to 24cO2 read cycle)
    Soft_I2C_Write(2)             ' send byte (address of EEPROM location)
    Soft_I2C_Start                ' issue I2C signal repeated start
    Soft_I2C_Write($A3)           ' send byte (request data from EEPROM)
    EE_ByteIn = Soft_I2C_Read(0)  ' Read the data
    Soft_I2C_Stop
    PORTC  = EE_ByteIn            ' display data on PORTC
end.

9.4 Manchester Code

9.4.1 A Bit of Theory

Manchester encoding is especially suitable for allowing your PIC to communicate with others over RF (Radio Frequency, wireless) communications and can be performed using the hardware or software UART. It is simple to encode /decode and has good error detection abilities, hence negating the need for the “check-summed” data. Manchester encoding is practical and fairly easy to implement. It is being used everywhere in the RF world, its most notable “customer” being the 802.3 Ethernet (wireless Ethernet) standard. This is a synchronous communication standard, meaning that keeping the clock rate stable and maintaining exact baudrate is essential when applying this type of communication.

If you’re a bit more interested in how the Manchester encoding works and why it’s being used, read-on through this chapter. If you’re anxious to put the things to work, skip this and go straight to the examples.

In usual, wire-type communications world, encoding a stream of data is pretty straightforward. For example, in RS232, you have (usually) one start bit, 8 data bits, (optional) parity bit(s) and one stop bit. Data bits are sent as “high” or “low” signal levels.
Programming PIC MCUs in BASIC
If you would try to send this data straightforward through the RF channel, you would encounter a number of problems. The biggest one is the existance of the DC component in the signal, meaning that if you integrate the voltage over time for, say, those 8 bits sent in previous example, you would get a non-zero (positive or negative) value. This means that if you want to send this data through the radio, you need a lot of power, even for short distances. Furthermore, when this kind of data reaches the receiver, it cannot, due to the principles on which it is been built, interpret it properly. Again, it’s that DC component in the signal that represents the “obstruction”.

Manchester encoding solves this problem by treating the data (i.e. 1’s and 0’s) not as signal (voltage, current) levels, but rather as a transition pattern between the levels. This means that “1” is coded as transition from high to low signal level, while the “0” is coded in the opposite way – as transition from low to high signal level. So, the data from the previous example, in Manchester encoding, looks like this (showing only the “data” portion of message):
Programming PIC MCUs in BASIC
You can see that the voltage summary now remains zero no matter how much and what kind of data we are sending.

If you wish to find out more on Manchester encoding, there’s plenty of material about it on the Internet

9.4.2 Manchester Code Examples

in BASIC, Manchester encoding functions are implemented on top of the software UART library. Using them is similar to software UART, exept the necessity to re-synchronize the receiver side if there are too many errors.

The first example is code for the transmit side of the link:

'********************************************************************* **********
' microcontroller P18F452
'
' Project RTX
' This project is designed to work with PIC 18F452
' with minor adjustments, it should work with any other PIC MCU
'
' This code demonstrates how to send data using Manchester encoding
'********************************************************************* **********
program RF_TX
dim i as byte
dim s1 as string[20]
main:
  PORTB  = 0                      ' Initialize port
  TRISB  = %00001110
  ClearBit(INTCON, GIE)           ' Disable interrupts
  Man_Send_Init(PORTB)            ' Initialize manchester sender
  while TRUE
      Man_Send($0B)               ' Send start marker
      Delay_ms(100)               ' Wait for a while
      s1 = "mikroElektronika"
      for i = 1 to StrLen(s1)
         Man_Send(s1[i])          ' Send char
         Delay_ms(90)
      next i
      Man_Send($0E)               ' Send end marker
      Delay_ms(1000)
  wend
end.

As you can see, the transmit side is pretty straightforward. The receive side needs to have a little extra coding to provide the re-synchronization during data receive, if necessary. In BASIC, Manchester receive routines (Man_Receive_Config, Man_Receive_Init, Man_Receive) are blocking calls. This means that PIC will wait until the task is performed (e.g. byte is received, synchronization achieved, etc.). Routines for receiving are limited to a baud rate scope from 340 ~ 560 bps.

'********************************************************************* **********
' microcontroller: P16F877A
'
' Project: RRX
' This project is designed to work with PIC 16F877A
' with minor adjustments, it should work with any other PIC MCU
'
' This code shows how to use manchester library for receiving data.
' The example works in conjuction with transmitter which sends
'   the word "mikroElektronika" using Manchester encoding.
'********************************************************************* **********
program RRX
dim ErrorFlag as byte
dim ErrorCount as byte
dim IdleCount as byte
dim temp as byte
dim LetterCount as byte
main:
  errorCount = 0
  trisc      = 0                          ' ErrorFlag indicator
  portc      = 0
  Man_Receive_Config(PORTD,6)             ' Initialize receiver and try to synchronize it
  LCD_Init(PORTB)                         ' Initialize LCD on PORTB
  while true
    do                                     ' Loop endlessly
      IdleCount = 0                       ' Reset idle counter
      temp = Man_Receive(ErrorFlag)       ' Attempt byte receive
      if ErrorFlag then
        inc(errorCount)
      else
        portc = 0                         ' Clear error indicator
      end if
      if errorCount >  20 then            ' If there are too many errors
                                           '   try to syncronize the receiver again
          errorCount = 0
          portc = $AA                     ' Indicate error state
          Man_Receive_Init(PORTD)         ' Synchronize receiver
        end if
      inc(IdleCount)
      if IdleCount > 18 then              ' If nothing is received after some time
          IdleCount = 0                   '   try to synchronize again
          Man_Receive_Init(PORTD)         ' Synchronize receiver
      end if
    loop until temp = $0B                 ' End of message marker
    ' Get the message
    if errorFlag = false then             ' If no errorFlag then write the message
        LCD_Cmd(LCD_CLEAR)
        LetterCount = 0
        while LetterCount < 17            ' The message is 16 chars in size.
          inc(LetterCount)
          temp = Man_Receive(ErrorFlag)
          if errorFlag = false then
            LCD_Chr_CP(temp)
          else
            inc(errorCount)
          end if
        wend
       temp = Man_Receive(ErrorFlag)
       if temp <> $0E then
         inc(errorCount)
       end if
    end if
  wend
end.

9.5 RS485

RS232 is a well documented, simple, reliable, good-old standard that is still used a lot and will be so in the near future. You should certainly use it wherever and whenever possible. But what happens when it cannot do the job? This is primarily addressed to the cable length, i.e. distances it cannot cover. The answer is (or should be) RS485. Together with the RS422, it is one of the most widely used communication standards today, especially in the industrial production facilities and remote stations, although in the past decade it has been constantly losing the battle against the newer and more advanced standard – the Ethernet. So, what are the basic differences between RS232 and RS485 and when should you use the latter?

* The way the signals are represented: At RS232, signals are represented by voltage levels with respect to ground. There is a wire for each signal, together with the ground signal (reference for voltage levels), so if you want to use the communcation without the hardware handshake, you’ll need 3 wires for this: Rx (receive), Tx (transmit) and GND (ground) as common reference point. This also means that the difference in GND voltage levels between the devices in communication must be very small (if any). On the other hand, at RS485, signals are represented by voltage difference, which allows for much longer cable distances (with much more electrical noise) to be covered.
* Number of endpoints in communication: RS232 is a single-point protocol, meaning you can connect only one peripheral to your PC or PIC through one RS232 link. RS485 is multipoint protocol, which allows multiple devices to be connected to a single signal cable.
* Type of communication: RS232 is a full-duplex communication, meaning that both sides can send and receive data simultaneously. RS485 is a half-duplex communication – only one device can send messages at a time, while others on the net are listening.

You should use RS485:

* When you need your device to communicate to more than one of its “colleagues”, i.e. when you need to have a network of devices. Up to 32 devices (in standard configuration) can be connected;
* When you need to cover larger distances than those you normally can do with RS232. For RS 485 the cable can be up to 1200 meters long, compared to max. 30 – 60 meters for RS232;

To implement all this, RS485 uses the Master/Slave architecture. This means that one device (the master) controls the line, allowing other devices (slaves) to send messages only when spoken to. Master and slave devices interchange packets of information, each of these packets containing synchronization bytes, CRC byte, address byte, and the data. In Master/Slave architecture, slave can never initiate communication. Each slave has its unique address and receives only the packets containing that particular address. It is programmer’s responsibility to ensure that only one device transmits data via 485 bus at a time.

BASIC provides a set of library routines to allow you comfortable working with RS485 system. RS485 routines require USART module on PORTC. Pins of USART need to be attached to RS485 interface transceiver, such as LTC485 or similar. Pins of transceiver (Receiver Output Enable and Driver Outputs Enable) should be connected to PORTC, pin 2 (see the figure at end of the chapter). Note that address 50 is a common address for all Slave devices: packets containing address 50 will be received by all Slaves. The only exceptions are slaves with addresses 50 and 169, which require their particular address to be specified in the packet.

The following example demonstrates use of Slave nod in RS485 Master/Slave architecture.

program pr485
dim dat as byte[8]     ' Buffer for receiving/sending messages
dim i as byte
dim j as byte
sub procedure interrupt
 if TestBit(RCSTA, OERR) = 1 then
   PORTD = $81
 end if
 RS485Slave_Read(dat)               ' Every byte is received by
end sub                             '     RS485Slave_Read(dat);
                                    ' Upon receiving a message w/o errors
main:                               '     data[4] is set to 255
  TRISB = 0
  TRISD = 0
  USART_Init(9600)                  ' Initialize usart module
  RS485Slave_Init(160)              ' Initialize MCU as Slave with address 160
  SetBit(PIE1, RCIE)                ' Enable interrupt
  SetBit(INTCON, PEIE)              '     on byte received
  ClearBit(PIE2, TXIE)              '     via USART (RS485)
  SetBit(INTCON, GIE)
  PORTB = 0
  PORTD = 0                         ' Ensure that message received flag is 0
  dat[4] = 0                        ' Ensure that error flag is 0
  dat[5] = 0
  while true
    if dat[5] then
     PORTD = $AA                    ' If there is error, set PORTD to $aa
    end if
    if dat[4] then                  ' If message received:
    dat[4] = 0                      '     Clear message received flag
      j = dat[3]                    '     Number of data bytes received
      for i = 1 to j
        PORTB = dat[i - 1]          '     Output received data bytes
      next i
      dat[0] = dat[0] + 1           '     Increment received dat[0]
      RS485Slave_Write(dat,1)       '     Send it back to Master
    end if
  wend
end.

Programming PIC MCUs in BASIC
Example of interfacing PC to PIC MCU via RS485 bus

9.6 OneWire

This is another Master/Slave protocol, and all the cabling you need is a single wire. Because of the hardware configuration it uses (single pullup and open collector drivers), it allows for the slaves even to get their power supply from that line. Some basic characteristics of this protocol are:

* single master system,
* low cost,
* low transfer rates (up to 16 kbps),
* fairly long distances (up to 300 meters),
* small data transfer packages.

Each 1-Wire device also has a unique 64-bit registration number (8-bit device type, 48-bit serial number and 8-bit CRC), so multiple slaves can co-exist on the same bus.

In low-level part of this protocol, each bit is transferred by the master by pulling the line low. Whether the bit is zero or one depends on how long the line is kept low, longer time for the “0” and shorter for the “1”. When the master is reading a bit from a slave it pulls the line low for a minimum amount of time. The slave pulls the line low as well and after the master releases the line the slave keeps it low for the time required for the bit type (same as for the master).

The higher level protocol, also known as 1-wire Bus System, allows the master to enumerate devices on the bus. The master sends a command for all slaves to respond with their registration number. As each bit is being read, the master sends the value of the bit it is interested in. The slaves that match continue while the slaves that don’t match stop outputting. By the time the entire configuration code is read, one unique code has been read identifying one device. The command is repeated until no new devices respond and all devices on the bus have been identified.

This code demonstrates use of low-level 1-wire library procedures and functions in BASIC. The example reads the temperature using DS1820 connected to PORTA, pin 5. Be sure to set the Fosc (oscillator frequency) appropriately in your project.

program onewire_test

dim i    as byte
dim j1   as byte
dim j2   as byte
dim por1 as byte
dim por2 as byte
dim text as char[20]

main:
text   = “Temperature:”
PORTB  = 0                       ‘ initialize PORTB to 0
PORTA  = 255                     ‘ initialize PORTA to 255
TRISB  = 0                       ‘ PORTB is output
TRISA  = 255                     ‘ PORTA is input
LCD_Init(PORTB)
LCD_Cmd(LCD_CURSOR_OFF)
LCD_Out(1, 1, text)
do
OW_Reset(PORTA, 5)           ‘ 1-wire reset signal
OW_Write(PORTA, 5, $CC)      ‘ issue command to DS1820
OW_Write(PORTA, 5, $44)      ‘ issue command to DS1820
Delay_ms(120)
i = OW_Reset(PORTA, 5)
OW_Write(PORTA, 5, $CC)      ‘ issue command to DS1820
OW_Write(PORTA, 5, $BE)      ‘ issue command to DS1820
Delay_ms(1000)
j1 = OW_Read(PORTA, 5)       ‘ get result
j2 = OW_Read(PORTA, 5)       ‘ get result
j1 = j1 >> 1                 ‘ assuming the temp. >= 0C
ByteToStr(j1, text)          ‘ convert j1 to text
LCD_Out(2, 8, text)          ‘ print text
LCD_Chr(2, 10, 223)          ‘ degree character (°)
LCD_Chr(2, 11, “C”)
Delay_ms(500)
loop until false                 ‘ endless loop
end.
Programming PIC MCUs in BASIC
Example of DS1820 on PORTA, pin 5 communicating via 1-Wire

9.7 CAN & CANSPI
9.7.1 CAN

CAN stands for the “Controller Area Network” and, as its name implies, it addresses the need to connect the controllers (or MCUs) into network in a single area. This “area” can vary from couple of square meters and up to 70,000 square meters and more.

CAN is a serial bus standard developed to replace the RS485, and, although quite different from the RS485, from user’s point of view it can be considered as the “new and improved” RS485. It is designed to be fast, highly noise-resistant and practically error-proof which makes it perhaps the most complete, elaborate, and sophisticated communication standard that is applied on PIC MCUs. All this doesn’t come cheaply, meaning it doesn’t pay off using it for distances less than 30-40 meters unless you need some (very) high data throughput. Remember, each of the communication standards, protocols and devices described in this chapter has its own scope of applications!

CAN was originally created by the BOSCH company in the early 80’s for automotive electronics, to allow for various car/truck/bus electronic systems (such as ABS, Electronic Power Control, AquaStop, GPS/navigation, data display system, etc) to exchange data over (single) serial bus, i.e. to form a network of devices within the vehicle. It soon proved its value, expanding itself into other areas, such as HVAC, elevators and Building Automation systems. Its price and transfer rate characteristics put the CAN in between the RS485 (older, slower, and by far cheaper) and the Ehternet (newer, faster, and more complex/expensive), and that’s also where its scope of applications stands.

CAN is a very robust protocol that has error detection and signalling, self–checking and fault confinement. Faulty CAN data and remote frames are re-transmitted automatically, just like at Ethernet.

Data transfer rates vary from up to 1 Mbit/s at network lengths below 40 m to 250 kbit/s at 250 m cables, and can go even lower at greater network distances, downto 200 kbit/s, which is the minimum bitrate defined by the standard. Cables used are shielded twisted pairs, and maximum cable length is 1000 m.

CAN supports two message formats:

* Standard format, with 11 identifier bits, and
* Extended format, with 29 identifier bits

The protocol itself is standardized in ISO 11898-1 (2003). Every CAN solution must implement the standard format and may accept the extended format.

Here, all devices are connected to a single shared bus and they are all allowed to start a transmission (whenever they want to), unlike at RS485. Therefore, if two or more devices start transmitting simultaneously, there must be some arbitration scheme involved to decide which one will be granted permission to continue transmitting. This mechanism is called the Carrier Sense Multiple Access / Collision Avoidance (CSMA/CA) scheme.

The example that follows is a simple demonstration of working with the CAN system through BASIC programming language. The CAN device is first initialized, then the data is intermittently read, incremented and written back. The PIC that is used for this example must have the CAN module built-in, e.g. you could use the P18F448 or any other PIC MCU from P18Fxx8 family. Furthermore, you need to have a chip that performs signal conditionning for CAN bus (or CAN transceiver) to boost up voltage levels and make level transition cleaner and less noisy, and for that purpose we used the MCP2551 IC.

program can_test

dim aa  as byte
dim aa1 as byte
dim lenn as byte
dim aa2 as byte
dim data as byte[8]
dim id as longint
dim zr as byte
dim cont as byte
dim oldstate as byte

sub function TestTaster as byte
result = true
if Button(PORTB, 0, 1, 0) then
oldstate = 255
end if
if oldstate and Button(PORTB, 0, 1, 1) then
result = false
oldstate = 0
end if
end sub

main:
TRISB.0 = 1                            ‘ Pin RB0 is input
PORTC = 0
TRISC = 0
PORTD = 0
TRISD = 0
aa    = 0
aa1   = 0
aa2   = 0

aa1 =  CAN_TX_PRIORITY_0 and           ‘ Form value to be used
CAN_TX_XTD_FRAME and           ‘   with CANSendMessage
CAN_TX_NO_RTR_FRAME

aa =   CAN_CONFIG_SAMPLE_THRICE and    ‘ Form value to be used
CAN_CONFIG_PHSEG2_PRG_ON and   ‘   with CANInitialize
CAN_CONFIG_STD_MSG and
CAN_CONFIG_DBL_BUFFER_ON and
CAN_CONFIG_VALID_XTD_MSG and
CAN_CONFIG_LINE_FILTER_OFF

cont = true                   ‘ Upon signal change on RB0 pin
while cont                    ‘     from logical 0 to 1
‘     proceed with program
cont = TestTaster           ‘     execution
wend

data[0] = 0
CANInitialize( 1,1,3,3,1,aa)                        ‘ Initialize CAN
CANSetOperationMode(CAN_MODE_CONFIG,TRUE)           ‘ Set CONFIGURATION mode
ID = -1
CANSetMask(CAN_MASK_B1,ID,CAN_CONFIG_XTD_MSG)       ‘ Set all mask1 bits to ones
CANSetMask(CAN_MASK_B2,ID,CAN_CONFIG_XTD_MSG)       ‘ Set all mask2 bits to ones
CANSetFilter(CAN_FILTER_B1_F1,3,CAN_CONFIG_XTD_MSG) ‘ Set id of filter B1_F1 to 3
CANSetOperationMode(CAN_MODE_NORMAL,TRUE)           ‘ Set NORMAL mode
PORTD = $FF
id = 12111
CANWrite(id, data, 1, aa1)            ‘ Send message via CAN
while true
oldstate = 0
zr = CANRead(id, Data, lenn, aa2)
if (id = 3) and zr then
PORTD = $AA
PORTC = data[0]                   ‘ Output data at PORTC
data[0] = data[0]+1
id = 12111
CANWrite(id, data, 1, aa1)        ‘ Send incremented data back
if lenn = 2 then                  ‘ If message contains two data as bytes
PORTD = data[1]                 ‘      output second as byte at PORTD
end if
end if
wend
end.
Programming PIC MCUs in BASIC
Example of interfacing CAN transceiver with MCU and bus

9.7.2 CANSPI

Working with CAN is nice, but what about those PICs that do not have the CAN module? The BASIC solution for them is the CANSPI, actual CAN-over-SPI module library. Each routine of CAN library has its CANSPI counterpart with identical syntax. This library provides us with a fictive external CAN module that can be communicated with through the SPI.

In example that follows we used the MCP2510 CAN controller. For more information consult the previous entry and example. Please note that the effective communication speed now depends on the SPI, and is certainly slower than the “real” CAN.

program CANSPI
dim  aa as byte
dim  aa1 as byte
dim  lenn as byte
dim  aa2 as byte
dim  data as byte[8]
dim  id as longint
dim  zr as byte

main:
TRISB = 0
SPI_init        ‘ Must be performed before any other activity
TRISC.2 = 0     ‘ This pin is connected to Reset pin of MCP2510
PORTC.2 = 0     ‘ Keep MCP2510 in reset state
PORTC.0 = 1     ‘ Make sure that MCP2510 is not selected
TRISC.0 = 0     ‘ Make RC0 output
PORTD  = 0
TRISD  = 0      ‘ PORTD is output
aa = 0
aa1 = 0
aa2 = 0
aa = CAN_CONFIG_SAMPLE_THRICE and
CAN_CONFIG_PHSEG2_PRG_ON and
CAN_CONFIG_STD_MSG and
CAN_CONFIG_DBL_BUFFER_ON and
CAN_CONFIG_VALID_XTD_MSG   ‘ Prepare flags for CANSPIinitialize procedure
PORTC.2 = 1                      ‘ Activate MCP2510 chip
aa1 = CAN_TX_PRIORITY_BITS and
CAN_TX_FRAME_BIT and
CAN_TX_RTR_BIT             ‘ Prepare flags for CANSPIwrite function
CANSPIInitialize( 1,2,3,3,1,aa)   ‘ Initialize MCP2510

CANSPISetOperationMode(CAN_MODE_CONFIG,true)      ‘ Set configuration mode
ID = -1
CANSPISetMask(CAN_MASK_B1,id,CAN_CONFIG_XTD_MSG)
‘ bring all mask1 bits to ones

CANSPISetMask(CAN_MASK_B2,0,CAN_CONFIG_XTD_MSG)
‘ bring all mask2 bits to ones

CANSPISetFilter(CAN_FILTER_B1_F1,12111,CAN_CONFIG_XTD_MSG)
‘ set filter_b1_f1 id to 12111

CANSPISetOperationMode(CAN_MODE_NORMAL,true)
‘ get back to Normal mode

while true
zr = CANSPIRead(id, Data, lenn, aa2)
if (id = 12111) and zr then
PORTD = $AA
PORTB = data[0]
data[0] = data[0] + 1
id = 3
Delay_ms(10)
CANSPIWrite(id, data, 1, aa1)
if lenn = 2 then
PORTD = data[1]
end if
end if
wend
end.

Programming PIC MCUs in BASIC

Tags: , , , , , , , , , , , , , ,

Related posts

2 Responses

  1. Christian Says:

    On your site familiar in the ICQ link Kinula. It turned out that nothing like it. Tepr all the time to read will

  2. COMUNICACION EN MICROCONTROLADORES « Dinotronics Says:

    [...] http://www.thishelps.net/2008/03/programming-pic-mcus-in-basic-8.html  [...]

Leave a Comment

Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.