Most PIC microcontrollers come with a certain amount of on-chip memory. This memory is enough for most applications created for these tiny processors, but there are times when more memory is needed to get the job done. The LAB-X1 has five empty 8-pin sockets. Three of these (the three on the left) are designed to allow us to experiment with three types of single-wire memory ICs. The ICs don’t need just one wire for full control, but the data does go back and forth on one wire.
Note Each memory socket accepts only one type of memory device, and only one of the ICs is allowed to be in place at any one time because the lines are shared between the sockets, so having more than one device plugged in can create conflicts.
Depending on the type of memory you want to experiment with, one of the three schematics in Figure 7.1 is applicable.
The interfaces that have been developed for the three types of one-wire memory give you the choices you need for flexibility in board design and layout, but it also means that a single interface and protocol won’t work for everything. The interfaces vary in speed, number of signal lines, and in other important details.
Since the memories are all one-wire serial devices, their memory content can vary from 128 bytes to 4 kilobytes or more and still maintain the 8-pin interface.
The salient characteristics of the three types of memory are as follows:
I2C SEEPROM I2C SEEPROMs are serial, electrically erasable and programmable, read-only memories. They are best suited for applications needing a modest amount of inexpensive nonvolatile memory where a lot of I/O lines are not available for memory transfers. Requires four lines for control.
Figure 7.1 One-wire memory sockets. (The three types of memory you can experiment with on the LAB-X1 and their wiring layouts; only one IC may be in place at any one time.)
Microwire Microwire is a National Semiconductor standard and is specially suited to use with their microcontrollers. Though often called a three-wire interface, it is actually a five-wire interface with four signal lines and a ground.
SPI SPI (serial peripheral interface) originated at Motorola. It is much like Microwire, though the signal names, polarities, and other details vary. Like Microwire, SPI is often referred to as a three-wire interface, though a read/write interface actually requires two data lines, a clock, a chip select, and a common ground, making five wires.
Other manufacturers provide products to meet the standards that have been established.
I2C is the best EEPROM type to use if you have just two signal lines to spare, or if you have a cabled interface (I2C also has the strongest drivers).
If you want a clock rate faster than 400 kHz, use Microwire or SPI.
For more on using serial EEPROMs, refer to the manufacturers’ pages on the Web, especially the following sites:
National Semiconductor
www.national.com/design/
(Contains many application notes on Microwire)
Motorola Semiconductor
www.mcu.motsps.com/mc.html
(Onsite microcontroller references contain SPI documentation.)
Jan Axelson’s article in Circuit Cellar is a good source of detailed information on these devices. The article can be found at www.lvr.com/files/seeprom.pdf.
The PICBASIC PRO Compiler provides the instructions necessary to access these serial memories.
In all the programs I have listed in this chapter, I have used the programs and documentation verbatim from the microEngineering Labs Web site. I did this for two reasons. One, to not reinvent the wheel since the work has already been done by microEngineering Labs, and two, to expose the readers to how the programs are structured by a programmer other than myself. Each programmer has his or her own style of doing things and it is well worth being exposed to the work of other programmers.
Socket U3 accommodates I2C memory only. Figure 7.2 and Program 7.1 illustrate the use of this memory type.
Figure 7.2 I2C SEEPROM. (Wiring and circuitry requirements.)
Program 7.1 Program to read from and write to I2C SEEPROMs
;This microEngineering Labs program can be found on their
;Web site. Use the latest version.
CLEAR ; clear memory
SO CON 0 ; define serial output pin
N2400 CON 4 ; set serial mode
; define variables
DPIN VAR PORTA.0 ; I2C data pin
CPIN VAR PORTA.1 ; I2C clock pin
B0 VAR BYTE ; variable declaration
B1 VAR BYTE ; variable declaration
B2 VAR BYTE ; variable declaration
; write to the memory
FOR B0 = 0 TO 15 ; loop 16 times
I2CWRITE DPIN, CPIN, $A0, B0, [B0] ; write each location's
; address to itself
PAUSE 10 ; delay 10 ms after each
; write is needed
NEXT B0 ;
;
LOOP: ;
FOR B0 = 0 TO 15 STEP 2 ; loop 8 times
I2CREAD DPIN, CPIN, $A0, B0, [B1, B2] ; read 2 locations in
; a row
SEROUT SO, N2400, [#B1,” ”,#B2,” “] ; print 2 locations to
; CRT
NEXT B0 ;
;
SEROUT SO, N2400, [13,10] ; print a line feed
GOTO LOOP ;
END ;
Figure 7.3 SPI SEEPROM. (Wiring and circuitry requirements.)
Socket U4 is wired to use SPI memory only. Figure 7.3 and Program 7.2 illustrate the use of this memory type.
Program 7.2 Program to read from and write to SPI SEEPROMs
; This microEngineering Labs program can be found on their
; Web site. Use the latest version.
; PICBASIC PRO program to read and write to SPI SEEPROMs
; Write to the first 16 locations of an external serial EEPROM
; Read first 16 locations back and send to LCD repeatedly
; Note: For SEEPROMs with word-sized addresses
DEFINE LOADER_USED 1 ; allows use of the boot
; loader.
; this will not affect
; normal program operation.
DEFINE LCD_DREG PORTD ; define LCD registers and
; bits
DEFINE LCD_DBIT 4 ;
DEFINE LCD_RSREG PORTE ;
DEFINE LCD_RSBIT 0 ;
DEFINE LCD_EREG PORTE ;
DEFINE LCD_EBIT 1 ;
INCLUDE “MODEDEFS.BAS” ;
CS VAR PORTA.5 ; chip select pin
SCK VAR PORTC.3 ; clock pin
SI VAR PORTC.4 ; data in pin
SO VAR PORTC.5 ; data out pin
ADDR VAR WORD ; address
B0 VAR BYTE ; data
TRISA.5 = 0 ; set CS to output
ADCON1 =%00000111 ; set all of PORTA and PORTE
; to digital
LOW PORTE.2 ; LCD R/W line low (W)
PAUSE 100 ; wait for LCD to start up
FOR ADDR = 0 TO 15 ; loop 16 times
B0 = ADDR + 100 ; B0 is data for SEEPROM
GOSUB EEWRITE ; write to SEEPROM
PAUSE 10 ; delay 10 ms after each
; write
NEXT ADDR ;
LOOP: ;
FOR ADDR = 0 TO 15 ; loop 16 times
GOSUB EEREAD ; read from SEEPROM
LCDOUT $FE, 1, #ADDR,”: ”,#B0 ; display
PAUSE 1000 ;
NEXT ADDR ;
GOTO LOOP ;
; Subroutine to read data from addr in serial EEPROM
EEREAD: CS = 0 ; enable serial EEPROM
SHIFTOUT SI, SCK, MSBFIRST, [$03, ADDR.BYTE1, ADDR.BYTE0]
; Send read cmd and addr
SHIFTIN SO, SCK, MSBPRE, [B0] ; Read data
CS = 1 ; disable
RETURN ;
; Subr to write data at addr
; in serial EEPROM
EEWRITE: ;
CS = 0 ; enable serial EEPROM
SHIFTOUT SI, SCK, MSBFIRST, [$06] ; send write enable command
CS = 1 ; disable to execute command
CS = 0 ; enable
SHIFTOUT SI, SCK, MSBFIRST, [$02, ADDR.BYTE1, ADDR.BYTE0, B0]
; Sends address and data
CS = 1 ; disable
RETURN ;
END ; end program
Socket U5 is wired to use Microwire memory. Figure 7.4 and Program 7.3 illustrate the use of this memory type.
Figure 7.4 Microwire SEEPROM. (Wiring and circuitry requirements.)
This microEngineering Labs program (Program 7.3)—to read and write Microwire SEEPROM devices—can be found on their Web site. Use the latest version.
Program 7.3 Program to read from and write to Microwire SEEPROMs
; PICBASIC PRO program to read and write to Microwire
; SEEPROM 93LC56A
; Write to the first 16 locations of an external serial EEPROM
; Read first 16 locations back and send to LCD repeatedly
; Note: For SEEPROMs with byte-sized address
;
DEFINE LCD_DREG PORTD ; define LCD registers and bits
DEFINE LCD_DBIT 4 ;
DEFINE LCD_RSREG PORTE ;
DEFINE LCD_RSBIT 0 ;
DEFINE LCD_EREG PORTE ;
DEFINE LCD_EBIT 1 ;
INCLUDE “MODEDEFS.BAS” ;
CS VAR PORTA.5 ; chip select pin
CLK VAR PORTC.3 ; clock pin
DI VAR PORTC.4 ; data in pin
DO VAR PORTC.5 ; data out pin
ADDR VAR BYTE ; address
B0 VAR BYTE ; data
LOW CS ; chip select inactive
ADCON1 = 7 ; set PORTA and PORTE to digital
LOW PORTE.2 ; LCD R/W line low (W)
PAUSE 100 ; wait for LCD to start up
GOSUB EEWRITEEN ; enable SEEPROM writes
FOR ADDR = 0 TO 15 ; loop 16 times
B0 = ADDR + 100 ; B0 is data for SEEPROM
GOSUB EEWRITE ; write to SEEPROM
PAUSE 10 ; Delay 10ms after each write
NEXT ADDR ;
LOOP: ;
FOR ADDR = 0 TO 15 ; loop 16 times
GOSUB EEREAD ; read from SEEPROM
LCDOUT $FE, 1, #ADDR,”: ”,#B0 ; display
PAUSE 1000 ;
NEXT ADDR ;
GOTO LOOP ;
; Subroutine to read data from addr in serial EEPROM
EEREAD: ;
CS = 1 ; enable serial EEPROM
SHIFTOUT DI, CLK, MSBFIRST, [%1100\4, ADDR] ; send read cmd
; and address
SHIFTIN DO, CLK, MSBPOST, [B0]; read data
CS = 0 ; disable
RETURN ;
; subroutine to write data at
; addr in serial EEPROM
EEWRITE: CS = 1 ; Enable serial EEPROM
SHIFTOUT DI, CLK, MSBFIRST, [%1010\4, ADDR, B0]
; sends write command, address
; and data
CS = 0 ; disable
RETURN ;
; Subroutine to enable writes to serial EEPROM
EEWRITEEN: ; subroutine
CS = 1 ; enable serial EEPROM
SHIFTOUT DI, CLK, MSBFIRST, [%10011\5, 0\7]
; send write enable cmd and
; dummy clocks
CS = 0 ; disable
RETURN ;
END ; end program
Four options are available for using socket U6. This socket is designed to let us experiment with three real-time clocks and with a 12-bit analog-to-digital converter. It connects to the microcontroller as shown:
As shown in Figure 7.5, this is a four-wire interface between the MCU and the IC. The wiring for this chip is similar to the wiring for the Microwire SEEPROMS and the Microwire memory ICs. Essentially, this looks like a memory chip to the processor. When we write to this memory, we are writing to the clock, and when we read from this chip, we are reading an ever changing memory content that gives us information that we can interpret as “time.” So the program to read and write to this clock looks like a program that interacts with the Microwire family of SEEPROMS. (See Program 7.3.)
Figure 7.5 Clock implemented using IC NJU6355. (To the MCU, this clock IC looks like a set of memory locations.)
The same is true for the other chips. See Figures 7.6 and 7.7.
The NJU6355, the DS1202, and the DS1302 real-time clocks are the three integrated circuits for use in socket U6.
Note 1 Jumper J5 which is used for soldering in the crystal for the clock ICs is also the connection that the analog signal for the 12-bit A-to-D converter goes into. So, if you solder in a crystal, you will have to remove the crystal and make arrangements to read in the analog signal when you want to experiment with the LTC1298 12-bit A-to-D converter. The A-to-D converter uses the same socket (U6) as is used by the three clock chips.
Figure 7.6 Clock implemented using IC DS1202. (To the MCU, this clock IC looks like a set of memory locations.)
Note 2 There are a total of six empty sockets—U3, U4, U5, U6, U7, and U10—on the LAB-X1 board as received. Though more than one socket can be occupied by an IC at any one time, it is best if only one IC is experimented with at any one time. This will ensure that there are no conflicts between the various devices. If the extreme right RS485 socket U10 is to be used, the RS 232 IC in the socket just to the left of it, in U9, must be removed. One of these two communication chips can remain in place at all times in that the communications circuitry does not conflict with the memory locations.
THE CLOCK ICS (IN SOCKET U6)
The two 8-pin Dallas Semiconductor clock ICs are interchangeable, and each of them goes into the existing empty socket U6. The DS1302 is the successor to the DS1202.
The NJU6355 also goes into socket U6, but it is not pin-for-pin compatible with the Dallas Semiconductor chips. Fortunately, it too needs to have its crystal between pins 2 and 3, and its other lines can share the connections to the PIC 16F877A.
Figure 7.7 Clock implemented using IC DS1302. (To the MCU, this clock IC Looks like a set of memory locations.)
Before you can use the NJU6355, the DS1202, or the DS1302, you have to install a crystal between pins 2 and 3 of the chip socket. This has to be a 32.768 kHz crystal and it is to be installed at jumper J5 next to the real-time clock IC socket. If you do not have a crystal in place, the program will show the date and other items on the LCD, but the clock will not move forward.
If you want to have battery backup for the clock, you need to install a battery at jumper J4 (at the edge of the board next to U10). The pins for this jumper are already on the board when you receive it. The IC will accept from 2.0 to 5.5 volts, so three AA cells in series can provide an inexpensive backup power source. (The power drawn by this IC is 300 nano-amps at 2.0 volts. Two AA cells may not provide enough voltage because of the voltage drop across the in-line diode, so I have recommended three cells.)
The DS1302 (in Socket U6) The DS1302 is the successor to the DS1202. The DS1302 IC is very similar except for backup power capability and seven additional bytes of scratch pad memory. See the datasheet for more specific details.
The emphasis in the program we will develop is to see how we get the data to and from the real-time clock. Setting the clock is going to be done in the program startup routine, and the time cannot be modified once the program is running. If you want that, you can add that to the program you write.
The DS1302 has 31 RAM registers. When you want to send or receive data to the IC, the data can be transferred to and from the clock/RAM one byte at a time, or in a burst of up to 31 bytes.
For our purposes, in making instruments and controllers, 8-, 10-, and 12-bit A-to-D converters are used as interfaces between sensors and microprocessors. Sensors usually provide a change in resistance, inductance, or capacitance as some other factor is manipulated. These changes are usually very small and need to be amplified and digitized so they can be manipulated in a digital environment. The interface that converts these small analog signals to useful digital information is the A-to-D converter. Getting comfortable with A-to-D converters is an important part of making instruments and controllers.
MicroEngineering Labs provide a program on their Web site that shows how to read the 12-bit LTC1298. It’s shown in Program 7.4.
Program 7.4 Program to read from 12-bit LTC1298 A-to-D chip by microEngineering Labs
; PICBASIC PRO program to read LTC1298 ADC by microEngineering
; Labs.
; Define LOADER_USED to allow use of the boot loader.
; This will not affect normal program operation.
DEFINE LOADER_USED 1 ;
DEFINE LCD_DREG PORTD ; define LCD pins
DEFINE LCD_DBIT 4 ;
DEFINE LCD_RSREG PORTE ;
DEFINE LCD_RSBIT 0 ;
DEFINE LCD_EREG PORTE ;
DEFINE LCD_EBIT 1 ;
INCLUDE “MODEDEFS.BAS” ;
; alias pins
CS VAR PORTC.5 ; chip select
CK VAR PORTC.3 ; clock
DI VAR PORTA.2 ; data in
DO VAR PORTC.1 ; data out
; allocate variables
ADDR VAR BYTE ; channel address / mode
RESULT VAR WORD ;
X VAR WORD ;
Y VAR WORD ;
Z VAR WORD ;
HIGH CS ; chip select inactive
ADCON1 = 7 ; set PORTA, PORTE to digital
LOW PORTE.2 ; LCD R/W line low (W)
PAUSE 100 ; wait for LCD to start
GOTO MAINLOOP ; skip subroutines
; subroutine to read a/d converter
GETAD: ;
CS = 0 ; chip select active
; send address / mode –
; start bit, 3 bit addr, null bit]
SHIFTOUT DI, CK, MSBFIRST, [1\1, ADDR\3, 0\1]
SHIFTIN DO, CK, MSBPRE, [RESULT\12]; get 12-bit result
CS = 1 ; chip select inactive
RETURN ;
; subroutine to get x value (channel 0)
GETX: ;
ADDR = %00000101 ; single ended, channel 0, MSBF high
GOSUB GETAD ;
X = RESULT ;
RETURN ;
; subroutine to get y value (channel 1)
GETY: ;
ADDR = %00000111 ; single ended, channel 1, MSBF high
GOSUB GETAD ;
Y = RESULT ;
RETURN ;
; subroutine to get z value
; (differential)
GETZ: ;
ADDR = %00000001 ; diff (ch0 = +, ch1 = -), MSBF high
GOSUB GETAD ;
Z = RESULT ;
RETURN ;
;
MAINLOOP: ;
GOSUB GETX ; get x value
GOSUB GETY ; get y value
GOSUB GETZ ; get z value
LCDOUT $FE, 1, “X=”, #X, ; send values to LCD
“Y=”, #Y, “Z=”, #Z
PAUSE 100 ; do it about 10 times a second
GOTO MAINLOOP ; do it forever
END ; end program
Program 7.4 reads three values from the A-to-D converter and displays them as X, Y, and Z values on the LCD. The 1298 is a two-channel device, and the two signals are read from pins 2 and 3 on the device. The third value being displayed on the LCD is the differential between the two values, meaning that the device is now being used for looking at the two inputs, not as individual inputs but as one signal across the two lines (as compared to two signals between each of the pins and ground).
The two channels are connected to the two pins at J5. These are the two pins that the crystal for the clocks goes across and, as mentioned before,because of this there is a hardware conflict between using the clock chips and the A-to-D converter.
The LTC 1298 can provide a maximum of 11.1 thousand samples per second. The device accepts an analog reference voltage between –0.3 and Vcc +0.3 volts, so the signals to be read must be conditioned to reflect these requirements.
Sockets U7 and U8 are designed for temperature-sensing experiments. (U8 is a three-hole group for soldering in a three-wire temperature-sensing device and is located next to U7.) How this is done is demonstrated in Programs 7.5 and 7.6.
The DS1820 temperature reading device goes in socket U7.
The DS1620 temperature sensor has to be soldered into socket U8.
Program 7.5 Using the DS1820 (Program to read temperature by microEngineering Labs)
; This microEngineering Labs program can be found on their
; Web site. Use the latest version.
; PICBASIC PRO program to read DS1820 1-wire temperature sensor
; and display the temperature on the LCD
DEFINE LCD_DREG PORTD ; define lcd pins
DEFINE LCD_DBIT 4 ;
DEFINE LCD_RSREG PORTE ;
DEFINE LCD_RSBIT 0 ;
DEFINE LCD_EREG PORTE ;
DEFINE LCD_EBIT 1 ;
; allocate variables
COMMAND VAR BYTE ; storage for command
I VAR BYTE ; storage for loop counter
TEMP VAR WORD ; storage for temperature
DQ VAR PORTC.0 ; alias DS1820 data pin
DQ_DIR VAR TRISC.0 ; alias DS1820 data direction pin
;
;
ADCON1 =%00000111 ; set PORTA and PORTE to digital
LOW PORTE.2 ; lcd r/w line low (w)
PAUSE 100 ; wait for lcd to start
LCDOUT $FE, 1, “TEMP IN DEGREES C” ; display sign-on message
;
; mainloop to read the temperature and display on lcd
MAINLOOP: ;
GOSUB INIT1820 ; init the DS1802
COMMAND = %11001100 ; issue skip rom command
GOSUB WRITE1820 ;
COMMAND = %01000100 ; start temperature conversion
GOSUB WRITE1820 ;
PAUSE 2000 ; wait 2 seconds for conversion to
; complete
GOSUB INIT1820 ; do another init
COMMAND = %11001100 ; issue skip rom command
GOSUB WRITE1820 ;
COMMAND = %10111110 ; read the temperature
GOSUB WRITE1820 ;
GOSUB READ1820 ;
; display the decimal temperature
LCDOUT $FE, 1, DEC (TEMP >> 1), “.”, DEC (TEMP.0 * 5),_
“ DEGREES C”
GOTO MAINLOOP ; do it forever
; initialize DS1802 and check for
; presence
INIT1820: ;
LOW DQ ; Set the data pin low to init
PAUSEUS 500 ; wait > 480us
DQ_DIR = 1 ; release data pin (set to input
; for high)
PAUSEUS 100 ; wait > 60us
IF DQ = 1 THEN ;
LCDOUT $FE, 1, “DS1820 NOT PRESENT” ;
PAUSE 500 ;
GOTO MAINLOOP ; try again
ENDIF ;
PAUSEUS 400 ; Wait for end of presence pulse
RETURN ;
; write “command” byte to the ds1820
WRITE1820: ;
FOR I = 1 TO 8 ; 8 bits to a byte
IF COMMAND.0 = 0 THEN ;
GOSUB WRITE0 ; write a 0 bit
ELSE ;
GOSUB WRITE1 ; write a 1 bit
ENDIF ;
COMMAND = COMMAND >> 1 ; shift to next bit
NEXT I ;
RETURN ;
; write a 0 bit to the DS1802
WRITE0: ;
LOW DQ ;
PAUSEUS 60 ; low for > 60us for 0
DQ_DIR = 1 ; release data pin (set to input
; for high)
RETURN ; write a 1 bit to the DS1820
WRITE1: ;
LOW DQ ; low for < 15us for 1
@NOP ; delay 1us at 4mhz
DQ_DIR = 1 ; release data pin (set to input
; for high)
PAUSEUS 60 ; use up rest of time slot
RETURN ; read temperature from the DS1820
READ1820: ;
FOR I = 1 TO 16 ; 16 bits to a word
TEMP = TEMP >> 1 ; shift down bits
GOSUB READBIT ; get the bit to the top of temp
NEXT I ;
RETURN ; read a bit from the DS1820
READBIT: ;
TEMP.15 = 1 ; preset read bit to 1
LOW DQ ; start the time slot
@NOP ; delay 1us at 4mhz
DQ_DIR = 1 ; release data pin (set to input
; for high)
IF DQ = 0 THEN ;
TEMP.15 = 0 ; set bit to 0
ENDIF ;
PAUSEUS 60 ; wait out rest of time slot
RETURN ;
END ; end
Program 7.6 Using the DS1620 (microEngineering Labs program to read temperature)
; This microEngineering Labs program, too, can be found on
; their Web site. Use the latest version.
; PICBASIC PRO program to read DS1620 three-wire temperature
; sensor
; and display temperature on the LCD
INCLUDE “MODEDEFS.BAS” ;
DEFINE LCD_DREG PORTD ; define lcd pins
DEFINE LCD_DBIT 4 ;
DEFINE LCD_RSREG PORTE;
DEFINE LCD_RSBIT 0 ;
DEFINE LCD_EREG PORTE ;
DEFINE LCD_EBIT 1 ;
; alias pins
RST VAR PORTC.0 ; reset pin
DQ VAR PORTC.1 ; data pin
CLK VAR PORTC.3 ; clock pin
; allocate variables
TEMP VAR WORD ; storage for temperature
LOW RST ; reset the device
ADCON1 =%00000111 ; set PORTA and PORTE to digital
LOW PORTE.2 ; lcd r/w line low (w)
PAUSE 100 ; wait for lcd to start
LCDOUT $FE, 1, “TEMP IN DEGREES C” ; display sign-on message
; loop to read the temp and display
; on lcd
MAINLOOP: ;
RST = 1 ; enable device
SHIFTOUT DQ, CLK, LSBFIRST, [$EE] ; start conversion
RST = 0 ;
PAUSE 1000 ; wait 1 second for conversion to
; complete
RST = 1 ;
SHIFTOUT DQ, CLK, LSBFIRST, [$AA] ; send read command
SHIFTIN DQ, CLK, LSBPRER, [TEMP\9] ; read 9 bit temperature
RST = 0 ;
; display the decimal temperature
LCDOUT $FE, 1, DEC (TEMP >> 1), “.”, DEC (TEMP.0 * 5),_
“DEGREES C”
GOTO MAINLOOP ; do it forever
END ; end program