Lab Report4
Lab Report4
Abstract
This lab report will document the development of embedded device communication between PICs with a list
of devices using UART, SPI, etc. Experiments took place mostly during 2021-23.
This tiny camera is interesting for the study of bi-directional UART communication. It is capable of finding the
baud-rate through receiving a synchronizing command from the host. It uses a handshaking protocol with acknowl-
edge messages. Note that we use the 3.3.V supply circuit shown in Fig. 1.
1
Since the camera module draws 60mA current, and has operation ranges from 3.0-3.6V, this circuitry should be
sufficient for test purpose. The camera will be addressed via UART from the PC. Level conversions are made with
1:3 resistor net.
Yellow: TX (3.3V)
Green: RX (3.3.V)
Black: GND
A Command Set
Camera can support 11 commands for interfacing to host as following.
Color Setting
2-bit Gray-Scale 01h
4-bit Gray-Scale 02h
8-bit Gray-Scale 03h
2-bit Color 05h
16-bit Color 06h
JPEG 07h
Preview Resolution
80*60 01h
160*120 03h
JPEG Resolution
80*64 01h
160*128 03h
320*240 05h
640*480 07h
2
2. Get Picture (AA04h)
Host issues this command to get a picture from camera.
Get Setting
Snapshot 01h
Preview Picture 02h
JPEG Preview Picture 03h
3. Snapshot (AA05h)
Camera keeps a single frame of JPEG still picture data in the buffer after receiving this command.
Snapshot Setting
Compressed Picture 00h
Uncompressed Picture 01h
Skip Frame: the number of dropped frames can be defined before compression occurs. 0 keeps the current frame, 1
captures the next frame etc.
Note: As the transmission of uncompressed image is not the package mode, it is not necessary to set the package
size for the uncompressed image.
Baud Rate 1st Divider 2nd Divider Baud Rate 1st Divider 2nd Divider
7200bps FFh 01h 28800bps 3Fh 01h
9600bps BFh 01h 38400bps 2Fh 01h
14400bps 7Fh 01h 57600bps 1Fh 01h
19200bps 5Fh 01h 115200bps 0Fh 01h
6. Reset (AA08h)
Reset Setting 00h resets the whole system. C328-7640 will reboot and reset all registers and state machines. 01h
resets state machines only.
3
8. Data (AA0Ah)
The type and size of image data prepared for transmitting out to host would be advised to host when camera issuing
this command.
Data Type
Snapshot Picture 01h
Preview Picture 02h
JPEG Picture 05h
Data Length: these 3 bytes represent the length of Snapshot Picture, Preview Picture and JPEG Preview Picture.
9. SYNC (AA0Dh)
Either the host or the camera can issue this command to make connection. An ACK command must be sent out
after receiving this command.
B LabVIEW program
Here the LabVIEW code for getting snapshots from the camera:
4
Figure 2: Main program front panel (test3.vi).
5
Figure 4: UART initialization.
6
Figure 6: TRUE case in the former diagram.
Figure 7: TX command.
7
Figure 8: RX command.
8
Figure 10: C328-7640.
II LCD Display
We follow the advice by Dogan Ibrahim,1 and use the MPLAB XLCD library, by including xlcd.h in the C-programs.
This library has all the functions to use displays with incorporated HD44780 LCD controllers.
We currently use a device by Displaytech from the 161A series.2 The particularity of our part is that, although
showing a single 16 character line, this line is internally split into two 8-bit lines. So, the device must be configured
as multiline 5x7 dots. We will drive it in 4 bit mode, as recommended by Ibrahim.
# include < p18F452 .h >
# include < xlcd .h >
# include < delays .h >
1 D. Ibrahim, SD Card Projects Using the PIC Microcontroller, Elsevier, (2010), pp. 225-239
2 https://cdn.displaytech-us.com/sites/default/files/display-data-sheet/161A%20series-v21.pdf
9
Nop (); Nop (); Nop (); Nop ();
Nop (); Nop ();
return ;
}
/ * = = = == = = = = = = = = = = = = Main program = = = = = = = = = = = = = = = = * /
while (1)
{
PORTCbits . RC2 = 1; // Make RC2 one
Delay1KTCYx (1000); // delay a bit
If numbers are being used, we can apply the MPLAB standard library number to string function (itoa).
# include < p18F452 .h >
# include < xlcd .h >
# include < delays .h >
# include < stdlib .h >
10
Inspired by book : Dogan Ibrahim
4 - bit configuration of the display */
/ * = = = == = = = = = = = = = = = = Main program = = = = = = = = = = = = = = = = * /
11
Figure 13: Schematics of the 3641AS display.
Although the PIC16F876 has enough IO pins to allow the control of the display, we will stick to the method of
using additional shift-register 74HC595, which can save IO pins. It can replace 8 pins for digital signal output by
serially writing an 8-bit binary number using 3 pins only.
Q0-Q7: 8-bit parallel data output pins, able to control 8 LEDs or 8 pins of 7-segment display directly.
Q7’: Series output pin, connected to DS of another 74HC595 to connect multiple 74HC595s in series
MR: Reset pin, active at low level;
SHcp: Time sequence input of shift register. On the rising edge, the data in shift register moves
successively one bit, i.e. data in Q1 moves to Q2, and so forth. While on the falling edge, the data
in shift register remain unchanged.
STcp: Time sequence input of storage register. On the rising edge, data in the shift register moves
into memory register.
CE: Output enable pin, active at low level.
DS: Serial data input pin
VCC: Positive supply voltage.
GND: Ground.
12
Figure 14: Pinout of the 74hc395 shift register.
Further functionality:
When MR (pin10) is high level and OE (pin13) is low level, data is input in the rising edge of SHcp
and goes to the memory register through the rising edge of SHcp.
If the two clocks are connected together, the shift register is always one pulse earlier than the memory
register.
There is a serial shift input pin (Ds), a serial output pin (Q) and an asynchronous reset button (low
level) in the memory register.
The memory register outputs a Bus with a parallel 8-bit and in three states.
When OE is enabled (low level), the data in memory register is output to the bus (Q0 to Q7).
A schematics
The PIC16F876 is clocked at 20MHz. For further use, RA0-RA5 are configured as analog channels. (Note that
AN4 is internally wired to RA5! This is a particularity of the 16F87x series.) Therefore, RA5 must be manually
reconfigured as input and RA4 as output. PICLab doesn’t do this on its own. RB0-RB3 are used as digit selection.
RC0 is the data-line (SDI), RC1 (RCLK), RC2 (SRCLK) of the serial communication to the 74hc595.
13
B PIC16F876-main loop method
This method is the easiest to implement, because the 74hc595 shifting is controlled in the main loop. Note that the
display needs to be cleared, before setting a new digit. If this is not preformed, the LEDs get intermediate states
producing imprecise appearance. Single digits are extracted from the number to show and subsequently converted
to the right 7-segment LED pattern displayed in a look-up table for figures 0-9, and letters A-F.
The program uses a ms-timer (in fact 0.9984ms) and configures a ms-interrupt. Counters incremented at each
interrupt event or after 1000 such events. The sec counter is shown on the display. Note that leading zeroes are not
shown.
14
Figure 18: Decimal digit subroutine.
Although the method is easy to implement, it must be considered as very inefficient, because most of the time the
main loop is kept in wait functions. These are simple loops, where the processor is occupied with unproductive
activity. Also the digit switching requires much computing time, because digit extraction must is done at the main
loop rate.
15
Figure 20: Interrupt state machine.
16
Level converters: https://www.digikey.be/htmldatasheets/production/1828278/0/0/1/bob-12009.html
A Hardware
Configuring a micro-controller as an I2C-master is a relatively easy and well documented task. Unfortunately, this is
not the case for the I2C-slave mode. For PICs, we were astonished to find few information only, and above all very
incomplete and buggy. The only valuable document was the original Microchip Application Note 00734, and –not
easy to find on the Internet, because of broken links– the related Assembly code.
Note that some authors complain that their code doesn’t always work as expected. Some do completely misunder-
stand the role of the Buffer Full status bit BF of the SSPSTAT register, which is Read-only. And through they
recommend clearing it by software. In fact, this bit is set and cleared by the PIC hardware only. Some authors
confuse methods of handling the I2C bus by mixing up polling with interrupt methods. They introduce unbreakable
while-loops within interrupt service routines that poll the BF bit. This is a very bad idea, because the system can
hang up, if the SSPBUF register isn’t correctly written or read. The program could hang up.
So, we decided to write our own well working code, which is presented here.
PIC16F876 uses RC3 and RC4 for the I2C module. ESP8266 uses GPO4 and GPIO5. Because the PIC is powered
at 5V and the ESP8266 at 3V3, we must use level converters between the two devices. We also add 1.5kΩ pull-up
resistors to the I2C bus (on the 3V3 side). We don’t add any resistor on the 5V side, because only one slave is being
used.
B PIC program
Functionality description:
Correctly initialize the I2C module in slave mode (7-bit address mode)
Define a special byte-array (=buffer) that is filled for test reasons with ASCII values 65..68 (letters A, B, C,
D).
Define the interrupt service routine that can handle read, write modes and also distinguish data from slave
address. (Note the I2C master sends the device address first, and then only one or more data bytes.)
This is test version for further use that should read data from the PIC’s buffer. The ISR is configured to
respond on both read and write commands. However, in this actual version the variable first is never cleared,
which means that the PIC is never allowed to write into the buffer from the ISR. The ESP8266 can only read
the buffer. Therefore the ESP8266 must first send the buffer location (index) that it wants to read and then
17
read the related data. Don’t confuse the slave address with the buffer location. (If writing to the buffer is
desired, first must be cleared after the reception of the data location.)
Important note: Because the 0-bit of the address must be cleared at the Slave side -otherwise the module wouldn’t
recognize it as an address–, the address used by the ESP8266 must be left-shifted one bit on the PIC side, i.e. PIC
address 0x48=dec72 corresponds to 0x24=dec36.
Figure 22: A well working test program. (READ-ONLY configuration: Variable first is never cleared!)
18
Figure 23: I2C slave configuration.
Figure 24: We use a buffer for the data that is going to travel on the I2C bus.
19
Figure 25: A bunch of subroutines. Note that the subroutines must be define in PICLab within the interrupt service
routine.
;========================================================================================
; Assembler code for te st _I 2 C_ sl av e 4 . vi created with PICLab
;========================================================================================
__CONFIG ( _BODEN_OFF & _CP_OFF & _PWRTE_ON & _WDT_OFF & _LVP_OFF & _HS_OSC )
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Variable definitions * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
20
I 2 C _ B U F F E R _ C U R R E N T _ A D D R E S S _ B U S EQU 0 X43
I 2 C _ B U F F E R _ C U R R E N T _ A D D R E S S _ B U S _ H EQU 0 X44
I2C_CURRENT EQU 0 X45
SLAVE_ADDRESS EQU 0 X46
I 2 C _ B U F F E R _ S T A R T _ A D D R E S S EQU 0 X47
I 2 C _ B U F F E R _ S T A R T _ A D D R E S S _ H EQU 0 X48
LOCATION EQU 0 X49
I 2 C _ B U F F E R _ E N D _ A D D R E S S EQU 0 X4A
I 2 C _ B U F F E R _ E N D _ A D D R E S S _ H EQU 0 X4B
FIRST EQU 0 X4C
I 2 C _ B U F F E R _ C U R R E N T _ A D D R E S S _ I N EQU 0 X4D
I 2 C _ B U F F E R _ C U R R E N T _ A D D R E S S _ I N _ H EQU 0 X4E
I 2 C _ B U F F E R _ M A X _ B Y T E S EQU 0 X4F
DUMMY EQU 0 X50
ISR_TMPX8 EQU 0 X51
BANK0 MACRO
BCF STATUS , RP0
BCF STATUS , RP1
ENDM
BANK1 MACRO
BSF STATUS , RP0
BCF STATUS , RP1
ENDM
BANK2 MACRO
BCF STATUS , RP0
BSF STATUS , RP1
ENDM
BANK3 MACRO
BSF STATUS , RP0
BSF STATUS , RP1
ENDM
GOTO START
ORG 0 X4
GOTO LABEL_ISR
SUMV16
MOVF TEMPY16 , W
ADDWF TEMPX16
BTFSC STATUS , C
INCF TEMPX16_H
MOVF TEMPY16_H , W
ADDWF TEMPX16_H
RETURN
ISR_DIVV8
MOVF ISR_TEMPY8 , F
BTFSC STATUS , Z ; SKIP IF NON - ZERO
RETURN
CLRF ISR_RESULT8
MOVLW 1
MOVWF ISR_IDX16
ISR_SHIFT_IT8
BCF STATUS , C
RLF ISR_IDX16 , F
BCF STATUS , C
RLF ISR_TEMPY8 , F
BTFSS ISR_TEMPY8 ,7
GOTO ISR_SHIFT_IT8
ISR_DIVU8LOOP
MOVF ISR_TEMPY8 , W
SUBWF ISR_TEMPX8
BTFSC STATUS , C
GOTO ISR_COUNT8
ADDWF ISR_TEMPX8
GOTO ISR_FINAL8
ISR_COUNT8
MOVF ISR_IDX16 , W
ADDWF ISR_RESULT8
ISR_FINAL8
BCF STATUS , C
RRF ISR_TEMPY8 , F
BCF STATUS , C
RRF ISR_IDX16 , F
BTFSS STATUS , C
GOTO ISR_DIVU8LOOP
RETURN
ISR_SUMV16
MOVF ISR_TEMPY16 , W
ADDWF ISR_TEMPX16
BTFSC STATUS , C
21
INCF ISR_TEMPX16_H
MOVF ISR_TEMPY16_H , W
ADDWF ISR_TEMPX16_H
RETURN
ISR_GRU16U16
MOVF ISR_TEMPX16 , W
SUBWF ISR_TEMPY16
MOVF ISR_TEMPX16_H , W
BTFSS STATUS , C
INCFSZ ISR_TEMPX16_H , W
SUBWF ISR_TEMPY16_H
BTFSS STATUS , C
RETLW 0
RETLW 1
START
; INITIALIZE PORT A
BANK0
CLRF PORTA
BANK1
MOVLW 0 X0
MOVWF TRISA
; INITIALIZE PORT B
BANK0
CLRF PORTB
BANK1
MOVWF TRISB
; INITIALIZE PORT C
BANK0
CLRF PORTC
BANK1
MOVWF TRISC
; OPERATION
; SET VARIABLE U16 WITH VARIABLE U16
MOVF I2C_BUFFER_END_ADDRESS , W
MOVWF TEMPX16
MOVF I2C_BUFFER_END_ADDRESS_H , W
MOVWF TEMPX16_H
; SET VARIABLE U16 WITH CONSTANT
MOVLW 0 X3
MOVWF TEMPY16
MOVLW 0 X0
MOVWF TEMPY16_H
CALL SUMV16
; SET VARIABLE U16 WITH VARIABLE U16
BANK0
MOVF TEMPX16 , W
MOVWF I 2 C _ B U F F E R _ E N D _ A D D R E S S
22
MOVF TEMPX16_H , W
MOVWF I 2 C _ B U F F E R _ E N D _ A D D R E S S _ H
INCF I2C_BUFFER _C UR R EN T_ A DD RE SS _ BU S , F
BTFSC STATUS , Z
INCF I2C_BUFFER _ C U R R E N T _ A D D R E S S _ B US _ H , F
INCF I2C_BUFFER _C UR R EN T_ A DD RE SS _ BU S , F
BTFSC STATUS , Z
INCF I2C_BUFFER _ C U R R E N T _ A D D R E S S _ B US _ H , F
INCF I2C_BUFFER _C UR R EN T_ A DD RE SS _ BU S , F
BTFSC STATUS , Z
INCF I2C_BUFFER _ C U R R E N T _ A D D R E S S _ B US _ H , F
INCF I2C_BUFFER _C UR R EN T_ A DD RE SS _ BU S , F
BTFSC STATUS , Z
INCF I2C_BUFFER _ C U R R E N T _ A D D R E S S _ B US _ H , F
; CONFIGURE AD - CONVERTER
; CONFIGURE PORT INPUTS
BANK1
MOVF TRISA , W
IORLW 0 X1F
MOVWF TRISA
MOVLW 0 X82
MOVWF ADCON1
MOVLW 0 X81
BANK0
MOVWF ADCON0
23
MOVLW 0 XF
BANK0
MOVWF PORTB
; WAIT
MOVLW 0 XAA
MOVWF TEMPYY
LABEL_1003
MOVLW 0 X1D
MOVWF IDX16
LABEL_1004
MOVLW 0 XA7
MOVWF IDX16_H
LABEL_1005
DECFSZ IDX16_H , F
GOTO LABEL_1005
DECFSZ IDX16 , F
GOTO LABEL_1004
DECFSZ TEMPYY , F
GOTO LABEL_1003
NOP
NOP
NOP
NOP
NOP
NOP
NOP
; CONFIGURE UART
BANK1
BSF TRISC ,7
BSF TRISC ,6
MOVLW 0 X20
MOVWF SPBRG
BCF TXSTA , BRGH
BCF TXSTA , SYNC
BANK0
BSF RCSTA , SPEN
BCF RCSTA , RX9
BSF RCSTA , CREN
BANK1
BCF TXSTA , TX9
BSF TXSTA , TXEN
LABEL_0
; WAIT
BANK0
MOVLW 0 XE9
24
MOVWF TEMPYY
LABEL_1021
MOVLW 0 X73
MOVWF IDX16
LABEL_1022
MOVLW 0 XB9
MOVWF IDX16_H
LABEL_1023
DECFSZ IDX16_H , F
GOTO LABEL_1023
DECFSZ IDX16 , F
GOTO LABEL_1022
DECFSZ TEMPYY , F
GOTO LABEL_1021
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
GOTO LABEL_0
LABEL_1024
GOTO LABEL_1024
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * SUBROUTINES * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
L A BEL_AQUISITION
; WAIT
BANK0
MOVLW 0 X12
MOVWF TEMPYY
LABEL_1002
DECFSZ TEMPYY , F
GOTO LABEL_1002
NOP
RETURN
L A B E L_ IS R _O V ER FL O W
BANK0
BCF SSPCON ,6
GOTO LABEL_1007
LABEL_1006
LABEL_1007
; END OF IF - STRUCTURE
RETURN
LABEL_ISR_WRITE_COLLISION
BANK0
BCF SSPCON ,7
25
MOVF SSPBUF , W
MOVWF DUMMY
GOTO LABEL_1009
LABEL_1008
LABEL_1009
; END OF IF - STRUCTURE
RETURN
LABEL_ISR_SET_LOCATION_POINTER
; OPERATION
; SET VARIABLE U16 WITH VARIABLE U16
MOVF I2C_BUFF ER _C U RR EN T_ A DD RE SS _ BU S , W
MOVWF ISR_TEMPX16
MOVF I2C_BUFFER _ C U R R E N T _ A D D R E S S _ B US _ H , W
MOVWF ISR_TEMPX16_H
; SET VARIABLE U16 WITH VARIABLE U8
MOVF LOCATION , W
MOVWF ISR_TEMPY16
CLRF ISR_TEMPY16_H
CALL ISR_SUMV16
; SET VARIABLE U16 WITH VARIABLE U16
BANK0
MOVF ISR_TEMPX16 , W
MOVWF I 2 C _ B U F F E R _ C U R R E N T _ A D D R E S S _ B U S
MOVF ISR_TEMPX16_H , W
MOVWF I 2 C _ B U F F E R _ C U R R E N T _ A D D R E S S _ B U S _ H
RETURN
LABEL_ISR_INCREMENT_POINTER
BANK0
INCF I2C_BUFFER _C UR R EN T_ A DD RE SS _ BU S , F
BTFSC STATUS , Z
INCF I2C_BUFFER _ C U R R E N T _ A D D R E S S _ B US _ H , F
; BEGIN OF IF - STRUCTURE
; COMPARE - OPERATION
; SET VARIABLE U16 WITH VARIABLE U16
BANK0
MOVF I2C_BUFF ER _C U RR EN T_ A DD RE SS _ BU S , W
MOVWF ISR_TEMPX16
MOVF I2C_BUFFER _ C U R R E N T _ A D D R E S S _ B US _ H , W
MOVWF ISR_TEMPX16_H
; SET VARIABLE U16 WITH VARIABLE U16
MOVF I2C_BUFFER_END_ADDRESS , W
MOVWF ISR_TEMPY16
MOVF I2C_BUFFER_END_ADDRESS_H , W
MOVWF ISR_TEMPY16_H
CALL ISR_GRU16U16
BANK0
MOVWF ISR_RESULT8
BTFSC ISR_RESULT8 ,0
GOTO LABEL_1010
26
; SET VARIABLE U16 WITH VARIABLE U16
BANK0
MOVF I2C_BUFFER_START_ADDRESS , W
MOVWF I 2 C _ B U F F E R _ C U R R E N T _ A D D R E S S _ B U S
MOVF I2C_BUFFER_START_ADDRESS_H , W
MOVWF I 2 C _ B U F F E R _ C U R R E N T _ A D D R E S S _ B U S _ H
GOTO LABEL_1011
LABEL_1010
LABEL_1011
; END OF IF - STRUCTURE
RETURN
LABEL_ISR
MOVWF 0 X7F
SWAPF STATUS , W
BANK0
MOVWF ISR_STATUS
MOVF 0 X7F , W
MOVWF ISR_W
MOVF TEMPPORT8 , W
MOVWF ISR_TEMPPORT8
MOVF FSR , W
MOVWF ISR_FSR
BANK1
BTFSS PIE1 , SSPIE
GOTO LABEL_1025
BANK0
BTFSS PIR1 , SSPIF
GOTO LABEL_1025
CALL LABEL_EVENT0
BANK0
BCF PIR1 , SSPIF
LABEL_1025
BANK0
MOVF ISR_FSR , W
MOVWF FSR
MOVF ISR_TEMPPORT8 , W
MOVWF TEMPPORT8
MOVF ISR_W , W
MOVWF 0 X7F
SWAPF ISR_STATUS , W
MOVWF STATUS
SWAPF 0 X7F , F
SWAPF 0 X7F , W
RETFIE
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * EVENT - ROUTINES * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
LABEL_EVENT0
BANK0
BCF SSPCON ,4
GOTO LABEL_1014
LABEL_1013
LABEL_1014
; END OF IF - STRUCTURE
27
MOVWF FSR
MOVF INDF , W
MOVWF SSPBUF
CALL L A B E L _ I S R _ I N C R E M E N T _ P O I N T E R
BANK0
BSF FIRST ,0
GOTO LABEL_1020
LABEL_1012
CALL L A B E L _ I S R _ S E T _ L O C A T I O N _ P O I N T E R
BANK0
BCF FIRST ,0
GOTO LABEL_1017
LABEL_1016
CALL L A B E L _ I S R _ I N C R E M E N T _ P O I N T E R
LABEL_1017
; END OF IF - STRUCTURE
GOTO LABEL_1018
LABEL_1015
BSF FIRST ,0
LABEL_1018
; END OF IF - STRUCTURE
; SEND MAIL
BANK0
LABEL_1019
BTFSS PIR1 , TXIF
GOTO LABEL_1019
; SET SFR WITH VARIABLE U8
BANK0
MOVF I2C_CURRENT , W
MOVWF TXREG
LABEL_1020
; END OF IF - STRUCTURE
CALL L A B E L _ I S R _ W R I T E _ C O L L I S I O N
28
CALL L AB E L_ IS R _ O V E R F L O W
BANK0
BSF SSPCON ,4
RETURN
END
Figure 26: A very simple Micopython code to read from the I2C slave as desired.
29