0% found this document useful (0 votes)
2 views45 pages

Report

This document outlines a micro-controller course exercise involving programming a PIC16F877 microcontroller. It includes code for UART communication, timer interrupts, and PID control for motor speed regulation. The exercise also details variable declarations, interrupt functions, and the main loop for processing input and controlling output based on received data.

Uploaded by

Khoa Nguyễn
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views45 pages

Report

This document outlines a micro-controller course exercise involving programming a PIC16F877 microcontroller. It includes code for UART communication, timer interrupts, and PID control for motor speed regulation. The exercise also details variable declarations, interrupt functions, and the main loop for processing input and controlling output based on received data.

Uploaded by

Khoa Nguyễn
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 45

HO CHI MINH NATIONAL UNIVERSITY

HO CHI MINH UNIVERSITY OF TECHNOLOGY

🙞···☼···🙜

MICRO-CONTROLLER COURSE
CLASS EXERCISE 3

Lecturer: Assoc. Prof: Lê Đức Hạnh

STUDENT NAME STUDENT ID


Nguyễn Ngọc Khoa 2053139
Phạm Anh Hoàng 2152082
Vũ Minh Dũng 2152489
Lê Thành 2052706
Ngô Duy Hưng 2053072

HO CHI MINH CITY, 2024


EXERCISE 1
1. WIRING

2. PROGRAM

#include <16F877A.h>
#device ADC=16
#FUSES NOWDT // No Watch Dog Timer
#FUSES NOBROWNOUT // No brownout reset
#FUSES NOLVP // No low voltage prgming, B3(PIC16) or B5(PIC18) used
for I/O
#use delay(crystal=20000000)
#include <stdlib.h>

2
char x[10],y[10],pwmValue[10],speedValue[10],inputString[30];
int characterCounter=0,posD=0,posE=0;
int16 tmr1ovrf=0,tmr0ovrf=0;
#byte TRISC=0x87
#byte PORTC=0x07
#bit PINC0=PORTC.0
#bit PINC1=PORTC.1
#byte TXREG=0x19
#byte TXSTA=0x98
#bit TRMT=TXSTA.1
#bit BRGH=TXSTA.2
#bit SYNC=TXSTA.4
#bit TXEN=TXSTA.5
#byte RCSTA=0x18
#bit CREN=RCSTA.4
#bit SPEN=RCSTA.7
#byte SPBRG=0x99
#byte RCREG=0x1A
#byte PIE1=0x8C
#bit RCIE=PIE1.5
#byte INTCON=0x0B
#bit PEIE=INTCON.6
#bit GIE=INTCON.7
#byte T1CON=0x10
#bit TMR1ON=T1CON.0
#byte TMR1L=0x0E
#byte TMR1H=0x0F
#byte T2CON=0x12
#byte TMR2=0x11

3
#byte PR2=0x92
#byte PIR1=0x0C
#byte CCPR1L=0x15
#byte CCPR1H=0x16
#byte CCP1CON=0x17
#byte TMRO=0x01
#byte OPREG=0x81

#int_rda
void interruptUART()
{
switch(RCREG)
{
case 'd': posD=characterCounter;
case 'e': posE=characterCounter;
default:
if((RCREG>=46)&&(RCREG<=101))
{
inputString[characterCounter]=RCREG;
characterCounter+=1;
}
}
if(posE!=0)
{
for(int i=1;i<6;i++)
{
x[i-1]=inputString[i];
}
for(int j=1;j<6;j++)

4
{
y[j-1]=inputString[j+6];
}
for(int k=13;k<posD;k++)
{
pwmValue[k-13]=inputString[k];
}
for(int l=posD+1;l<posE;l++)
{
speedValue[l-1-posD]=inputString[l];
}
}
}

#int_timer1
void interruptTimer1()
{
tmr1ovrf+=1;
TMR1L=0;
TMR1H=0;
}

#int_timer0
void interruptTimer0()
{
tmr0ovrf+=1;
TMRO=0;
}

5
void uart(char data)
{
while(!TRMT);
TXREG=data;
}
void main()
{
TRISC=0b11000001;
OPREG=0b00000111; // Timer0 prescaler 1:256
INTCON=0b11100000; // Enable interrupts
PIE1=0b00000001;
T1CON=0;
PR2=0xff;
CCP1CON=0b00001100;
T2CON=0b00000111; // CCP1 with PWM mode, Timer 2 prescaler 1:16a and
postscale 1:1
TMR2=0;
BRGH=0; // Set UART to low-speed asynchronous mode, Baudrate 9600 bps
SPBRG=31;
SYNC=0;
SPEN=1;
RCIE=1;
PEIE=1;
GIE=1;
CREN=1;
TXEN=1;
CCPR1L=0;
PINC1=0;
int1 hasSent=0;

6
int32 time,sec;
float encoderS,maximumVoltage=12,kp=0,ki=0.2,kd=0; // PID parameters
double
setPoint=0,voltage=0,inputSpeed=0,samplingTime=0.02,integral=0,lastErr=0,err=0,diff=0;
while(TRUE)
{
if(atol(speedValue)!=0)
{
if(hasSent==0) // Send number value to terminal
{
hasSent=1;
for(int i=0;i<5;i++) uart(x[i]);
uart(' ');
for(int j=0;j<5;j++) uart(y[j]);
uart(' ');
for(int k=0;k<posD-13;k++) uart(pwmValue[k]);
uart(' ');
for(int l=0;l<posE-posD-1;l++) uart(speedValue[l]);
setPoint=atol(speedValue);
}
TMRO=0; // Counting sampling time
tmr0ovrf=0;
err=setPoint-inputSpeed;
diff=(err-lastErr)/samplingTime;
integral+=err*samplingTime;
lastErr=err;
voltage=kp*err+ki*integral+kd*diff;
if (voltage<0)
{

7
PINC1=1;
if (voltage<-12) voltage=-maximumVoltage;
voltage=-voltage;
}
else
{
PINC1=0;
if (voltage>12) voltage=maximumVoltage;
} // Direction control and setting limits
CCPR1L=(unsigned int)((255*(voltage/maximumVoltage))+0.5); // Scale to
PWM
TMR1L=0;
TMR1H=0;
while(PINC0==1);
while(PINC0==0);
TMR1ON=1;
while(PINC0==1);
while(PINC0==0);
TMR1ON=0;
time=(((int16)TMR1H<<8)|TMR1L)+(tmr1ovrf*65536);
encoderS=((20000000/4.0)/time)/36*60; // Speed from the encoder
tmr1ovrf=0;
inputSpeed=(unsigned int16)(encoderS+0.5);
sec=TMRO+256*tmr0ovrf;
samplingTime=sec*256*1/(20000000/4.0); // Sampling time
delay_ms(20);
}
}
}

8
3. EXPLANATION

 Our group used COM11 for the COM port connected to the PIC16F877 TX/RX pins.

char x[10],y[10],pwmValue[10],speedValue[10],inputString[30];
int characterCounter=0,posD=0,posE=0;
int16 tmr1ovrf=0,tmr0ovrf=0;
int1 hasSent=0;
int32 time,sec;
float encoderS,maximumVoltage=12,kp=0,ki=0.2,kd=0; // PID parameters
double
setPoint=0,voltage=0,inputSpeed=0,samplingTime=0.02,integral=0,lastErr=0,err=0,dif
f=0;
 Variables declaration.

9
#int_rda
void interruptUART()
{
switch(RCREG)
{
case 'd': posD=characterCounter;
case 'e': posE=characterCounter;
default:
if((RCREG>=46)&&(RCREG<=101))
{
inputString[characterCounter]=RCREG;
characterCounter+=1;
}
}
if(posE!=0)
{
for(int i=1;i<6;i++)
{
x[i-1]=inputString[i];
}
for(int j=1;j<6;j++)
{
y[j-1]=inputString[j+6];
}
for(int k=13;k<posD;k++)
{
pwmValue[k-13]=inputString[k];
}

10
for(int l=posD+1;l<posE;l++)
{
speedValue[l-1-posD]=inputString[l];
}
}
}

#int_timer1
void interruptTimer1()
{
tmr1ovrf+=1;
TMR1L=0;
TMR1H=0;
}

#int_timer0
void interruptTimer0()
{
tmr0ovrf+=1;
TMRO=0;
}

 #int_timer0, #int_timer1, #int_rda: defined interrupt functions of Timer0, Timer1 and


UART signal.
 In interruptUART() function, If the received character is 'd', it records the current
character count as posD. If the received character is 'e', it records the current character
count as posE.
 Otherwise, if the received character is within the ASCII range 46 to 101, it adds it to
an inputString array and increments the character count. After processing, if posE is
non-zero, it extracts specific portions of the received data into arrays x, y, pwmValue,
and speedValue based on the positions of 'd' and 'e' in the inputString.

11
 In interruptTimer1()function, the tmr1ovrf variable is incremented to 1 as Timer1
overflows, then Timer1 is reset to 0.
 In interruptTimer0()function, the tmr0ovrf variable is incremented to 1 as Timer0
overflows if overflow is allowed, then Timer0 is reset to 0.

void uart(char data)


{
while(!TRMT);
TXREG=data;
}

 Above is a function designed to transmit a single character over RS232


communication. It first checks the Transmit Shift Register Empty (TRMT) flag to
ensure that the transmit buffer is not busy.
 Upon confirmation of an empty buffer, the function loads the character to be sent into
the Transmit Register (TXREG), initiating its transmission through the RS232
interface.

TRISC=0b11000001;
OPREG=0b00000111; // Timer0 prescaler 1:256
INTCON=0b11100000; // Enable interrupts
PIE1=0b00000001;
T1CON=0;
PR2=0xff;
CCP1CON=0b00001100;
T2CON=0b00000111; // CCP1 with PWM mode, Timer 2 prescaler 1:16a and
postscale 1:1
TMR2=0;
BRGH=0; // Set UART to low-speed asynchronous mode, Baudrate 9600 bps
SPBRG=31;
SYNC=0;

12
SPEN=1;
RCIE=1;
PEIE=1;
GIE=1;
CREN=1;
TXEN=1;
CCPR1L=0;
PINC1=0;

 UART
o BRGH=1: Low speed, asynchronous mode.
o SPBRG=31: Used to generate baudrate 9600 bps.
o CREN=1: Enable data reception.
o TXEN=1: Enable UART transmission.
 PWM
o CCP1CON=0b00001100: Set up CCP1 as PWM mode.
o T2CON=0b00000111: Set up timer 2 with prescaler is 16, 1:1 postcale.
 Timer1
o INTCON=0b11100000: Enable global, peripheral and TMR0 overflow
interrupts.
 Timer0
o OPREG=0b00000111: Select the prescaler 1:256

void main()
{
TRISC=0b11000001;
OPREG=0b00000111; // Timer0 prescaler 1:256
INTCON=0b11100000; // Enable interrupts
PIE1=0b00000001;

13
T1CON=0;
PR2=0xff;
CCP1CON=0b00001100;
T2CON=0b00000111; // CCP1 with PWM mode, Timer 2 prescaler 1:16a and
postscale 1:1
TMR2=0;
BRGH=0; // Set UART to low-speed asynchronous mode, Baudrate 9600 bps
SPBRG=31;
SYNC=0;
SPEN=1;
RCIE=1;
PEIE=1;
GIE=1;
CREN=1;
TXEN=1;
CCPR1L=0;
PINC1=0;
int1 hasSent=0;
int32 time,sec;
float encoderS,maximumVoltage=12,kp=0,ki=0.2,kd=0; // PID parameters
double
setPoint=0,voltage=0,inputSpeed=0,samplingTime=0.02,integral=0,lastErr=0,err=0,diff=0;
while(TRUE)
{
if(atol(speedValue)!=0)
{
if(hasSent==0) // Send number value to terminal
{
hasSent=1;

14
for(int i=0;i<5;i++) uart(x[i]);
uart(' ');
for(int j=0;j<5;j++) uart(y[j]);
uart(' ');
for(int k=0;k<posD-13;k++) uart(pwmValue[k]);
uart(' ');
for(int l=0;l<posE-posD-1;l++) uart(speedValue[l]);
setPoint=atol(speedValue);
}
TMRO=0; // Counting sampling time
tmr0ovrf=0;
err=setPoint-inputSpeed;
diff=(err-lastErr)/samplingTime;
integral+=err*samplingTime;
lastErr=err;
voltage=kp*err+ki*integral+kd*diff;
if (voltage<0)
{
PINC1=1;
if (voltage<-12) voltage=-maximumVoltage;
voltage=-voltage;
}
else
{
PINC1=0;
if (voltage>12) voltage=maximumVoltage;
} // Direction control and setting limits
CCPR1L=(unsigned int)((255*(voltage/maximumVoltage))+0.5); // Scale to
PWM

15
TMR1L=0;
TMR1H=0;
while(PINC0==1);
while(PINC0==0);
TMR1ON=1;
while(PINC0==1);
while(PINC0==0);
TMR1ON=0;
time=(((int16)TMR1H<<8)|TMR1L)+(tmr1ovrf*65536);
encoderS=((20000000/4.0)/time)/36*60; // Speed from the encoder
tmr1ovrf=0;
inputSpeed=(unsigned int16)(encoderS+0.5);
sec=TMRO+256*tmr0ovrf;
samplingTime=sec*256*1/(20000000/4.0); // Sampling time
delay_ms(20);
}
}
}

 Within an infinite loop (while(TRUE)), the code checks if a non-zero speed value has
been received. If such a value is present, it proceeds with the control algorithm.
 Initially, it checks whether the data has been sent to the terminal. If not, it sends the
received speed value along with other relevant parameters like x, y, pwmValue, etc.,
to the terminal.
 Next, the code resets timer variables and calculates the error (err) between the desired
speed (setPoint) and the measured speed (inputSpeed). It then computes the derivative
of the error (diff) and updates the integral of the error (integral). These terms are used
in the PID control equation to determine the control voltage (voltage) applied to the
motor.
 The control voltage is then adjusted based on direction control and voltage limits. If
the control voltage is negative, indicating a need for reverse rotation, the direction pin
is set accordingly (PINC1). Otherwise, the direction pin is cleared. Additionally, the
control voltage is constrained within specified maximum and minimum values.
16
 The control voltage is then converted to a PWM duty cycle value and applied to the
motor driver. The code then waits for and measures the duration of an encoder pulse
to calculate the speed of the motor.
 Finally, it adjusts the calculated speed to account for the sampling time, and the loop
is delayed by 20 milliseconds before restarting.

4. VIDEO SIMULATION
Google Drive link:
https://drive.google.com/file/d/1EWP1I3q9gFBVf-2fO1fNCpQy1pbGVlkP/view?
usp=drive_link

EXERCISE 2
1. WIRING

17
 OUTPUT
o I2C Debugger

- This component read the bits sent from one controller to another through the SDA
and SCL lines, to prove that the codes on the controller are for communicating.
o Virtual Terminal

18
- This component read the bits sent to the RXD pins and print the result on the
screen
- This is used to show the communication results on screen.

 INPUT
o Control buttons
Two buttons are used to tell the Master MCU to send or receive data from the Slave
MCU

- And the resistors R1 and R4 act as pull-up resistors so that the inputs will initially
be at a HIGH state.

19
2. PROGRAMMING
2.1. Master MCU program
2.1.1. Initial Config
8MHz crystal/resonator on OSC2/CLKOUT and OSC1/CLKIN

#include <xc.h>

#define _XTAL_FREQ 8000000

2.1.2. I2C Functions


a) I2C_Init
SSPCON = 0b00101000;: I2C master mode, clock = F /(4*(SSPADD+1))
OSC

void I2C_Init(const unsigned long c)


{
SSPCON = 0b00101000;
SSPCON2 = 0;
SSPADD = (_XTAL_FREQ/(4*c))-1;
SSPSTAT = 0;
TRISC3 = 1; //Setting as input as given in datasheet
TRISC4 = 1; //Setting as input as given in datasheet
}

b) I2C_Master_Wait

void I2C_Master_Wait()
{
while ((SSPSTAT & 0x04) || (SSPCON2 & 0x1F));
}

SSPSTAT & 0x04: This checks if transmit is still in progress.


SSPCON2 & 0x1F: This checks the acknowledgement bit status to see if the data is
received.

c) I2C_Master_Start()
Start the I2C bus

void I2C_Master_Start()
{
I2C_Master_Wait();
SEN = 1;
}

20
d) I2C_Master_Stop()
Stop the I2C bus

void I2C_Master_Stop()
{
I2C_Master_Wait();
PEN = 1;
}

e) I2C_Master_Write
Send a byte “d” through the buffer register

void I2C_Master_Write(unsigned d)
{
I2C_Master_Wait();
SSPBUF = d;
}

f) I2C_Write
Send multiple bytes (int data) with the address (int address) through the buffer
register and then delay 50 ms

void I2C_Write(int address, int data){


I2C_Master_Start(); //Start condition
I2C_Master_Write(0x10); //7 bit address + Write
I2C_Master_Write(data);
I2C_Master_Stop();
__delay_ms(50);
}

g) I2C_Master_Read
Read data from the Slave MCU

unsigned short I2C_Master_Read(unsigned short a)


{
unsigned short temp;
I2C_Master_Wait();
RCEN = 1;
I2C_Master_Wait();
temp = SSPBUF;
I2C_Master_Wait();
ACKDT = (a)?0:1;

21
ACKEN = 1;
return temp;
}

2.1.3. UART Functions


a) UART_init
Initialize the UART bus

void UART_init(void)
{
BRGH=1;
SPBRG=51;
SYNC=0;
SPEN=1;
TRISC6=1;
TXEN=1;
}

b) UART_write_c & UART_write_s


Send data through the TX and RX pins

void UART_write_c(char data)


{
while(!TRMT);
TXREG = data;
}

void UART_write_s(char *text)


{
uint16_t i;
for(i=0;text[i]!='\0';i++)
UART_write_c(text[i]);
}

2.1.4. Main Program


unsigned char readData;
void main()
{
UART_init();
I2C_Init(100000);

Initialize the UART with a baudrate of 19200 and the I2C with the clock frequency of
1MHz
if (!PORTBbits.RB7){
while(!PORTBbits.RB7);
I2C_Master_Start(); //Start condition

22
I2C_Write(0x10,'1');
I2C_Write(0x10,'2');
I2C_Write(0x10,'.');
I2C_Write(0x10,'5');
I2C_Write(0x10,'9');
I2C_Write(0x10,' ');
}

If button on pin B7 is pushed, start the I2C in data transmit mode and send “12.59”
if (!PORTBbits.RB6){
while(!PORTBbits.RB6);
for (int j=0;j<5;j++){
I2C_Master_Start(); //Start condition
I2C_Master_Write(0x11); //7 bit address + Read
readData = I2C_Master_Read(0);
I2C_Master_Stop();
UART_write_c(readData);}
}
}
}

If button on pin B6 is pushed start the I2C in data reading mode and print the data
received on to the Virtual Terminal

2.2. Slave MCU program


2.2.1. Initial config
8MHz crystal/resonator on OSC2/CLKOUT and OSC1/CLKIN
#include <xc.h>

#define _XTAL_FREQ 8000000

short z;

int count=0;

2.2.2. I2C Functions


a) I2C_Init
o Initialize the I2C bus in slave mode and a 7-bit address
void I2C_Init(short address)
{
SSPSTAT = 0x80;
SSPADD = address;
SSPCON = 0x36;
SSPCON2 = 0x01;
TRISC3 = 1;

23
TRISC4 = 1;
GIE = 1;
PEIE = 1;
SSPIF = 0;
SSPIE = 1;
}

b) I2C_Write
o It loads the data into SSPBUF (I2C Buffer Register) and waits until the buffer
is empty (BF flag is cleared) before proceeding.

void I2C_Write(unsigned char data){


BF = 0;
SSPBUF = data ;
SSPCONbits.CKP = 1;
while(SSPSTATbits.BF);
}

c) I2C_Read
o This function checks the data on the receive buffer.
o If the address bit is high (D_A == 1), clear the buffer.
o The function will continue to wait until receive buffer is full
(while(SSPIF==0);) and then output the data read.

unsigned char I2C_Read()


{
unsigned char value=0;
if(D_A){
value=SSPBUF;
value=0;
}
SSPIF=0;
SSPOV=0;
while(SSPIF==0);
CKP=0;
value=SSPBUF;
CKP=1;
while(CKP);
return value;
}

d) Zvoid __interrupt() I2C_Slave_Read()


This is the interrupt service routine (ISR) for handling I2C communication. It checks
the SSPIF flag to determine if an interrupt is triggered. It then handles read and write
operations accordingly.

unsigned char readData=0;

24
void __interrupt() I2C_Slave_Read()
{
if(SSPIF == 1)
{
SSPCONbits.CKP = 0;

if ((SSPCONbits.SSPOV) || (SSPCONbits.WCOL))
{
z = SSPBUF; // Read the previous value to clear the
buffer
SSPCONbits.SSPOV = 0; // Clear the overflow flag
SSPCONbits.WCOL = 0; // Clear the collision bit
SSPCONbits.CKP = 1;
}

if(!SSPSTATbits.D_nA && !SSPSTATbits.R_nW)


{
z = SSPBUF;
while(!BF);
readData = SSPBUF;
UART_write_c(readData);
SSPCONbits.CKP = 1;
}
else if(!SSPSTATbits.D_nA && SSPSTATbits.R_nW)
{ count++;
z = SSPBUF;
if (count==1) I2C_Write('5');
if (count==2) I2C_Write('.');
if (count==3) I2C_Write('8');
if (count==4) I2C_Write('3');
if (count==5){
I2C_Write(0x20);
count=0;
}
}

SSPIF = 0;
}
}

if(SSPIF == 1): This checks if the SSP interrupt flag (SSPIF) is set.
SSPCONbits.CKP = 0;: Disables clock stretching
if ((SSPCONbits.SSPOV) || (SSPCONbits.WCOL)):
This checks for overflow
(SSPOV) or collision (WCOL) conditions. If either condition is detected, it reads the
previous value from SSPBUF to clear the buffer and then clears the overflow and
collision flags before enabling the clock again.
SSPSTATbits.D_nA && SSPSTATbits.R_nW: Check Data/Address and Read/Write
Condition:
o If the received byte is data (D_nA bit is 0) and if it's a write operation (R_nW
bit is 0) then read data from I2C and Transmit via UART

25
o If the received byte is data (D_nA bit is 0) and if it's a read operation (R_nW
bit is 1) then write data to I2C.
o SSPIF = 0;: Finally, the SSP interrupt flag (SSPIF) is cleared to acknowledge
and handle the interrupt.
2.2.3. UART functions
a) UART_init
Initialize the UART bus

void UART_init(void)
{
BRGH=1;
SPBRG=51;
SYNC=0;
SPEN=1;
TRISC6=1;
TXEN=1;
}

b) UART_write_c & UART_write_s


Send data through the TX and RX pins

void UART_write_c(char data)


{
while(!TRMT);
TXREG = data;
}

void UART_write_s(char *text)


{
uint16_t i;
for(i=0;text[i]!='\0';i++)
UART_write_c(text[i]);
}

2.2.4. Main Program


The main program just initialize all the necessary conditions and wait for the Master
MCU’s commands.
void main()
{
UART_init();
TRISB = 0xFF; //PORTB as input
TRISD = 0x00; //PORTD as output
PORTD = 0x00;
I2C_Slave_Init(0x10);
while(1){

}
}

26
3. VIDEO SIMULATION
Google Drive Link:
https://drive.google.com/file/d/1D- c9FkZ_eiZFB5yWYTj4jZljEj8TqJLz/view?
usp=drive_link

27
EXERCISE 3
1. REQUIREMENT

Program the transmission from master to slave number 12.XY if you press button B7 on the
Master and receive the transmission value from slave number 15.XY if you press button B7
on the slave. XY is the number after the dot. (2.5 pts)

2. PROGRAMMING
Master:
#pragma config FOSC = XT // Oscillator Selection bits (XT oscillator)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage In-Circuit Serial Programming Enable bit
#pragma config CPD = OFF // Data EEPROM Memory Code Protection bit
#pragma config WRT = OFF // Flash Program Memory Write Enable bits

28
#pragma config CP = OFF // Flash Program Memory Code Protection bit

#include <xc.h>
#include <pic16f877a.h>
#define _XTAL_FREQ 20000000
char number[]={0x31,0x32,0x2E,0x36,0x39,0x20};//12.69
void spiInit(char sType, char sDataSample, char sClockIdle, char sTransmitEdge)
{
TRISC5 = 0;
if(sType & 0b00000100) //If Slave Mode
{
SSPSTAT = sTransmitEdge;
TRISC3 = 1;
}
else //If Master Mode
{
SSPSTAT = sDataSample | sTransmitEdge;
TRISC3 = 0;
}
SSPCON = sType | sClockIdle;
}
static void spiReceiveWait()
{
while ( !SSPSTATbits.BF ); // Wait for Data Receipt complete
}
void spiWrite(char dat) //Write data to SPI bus
{
SSPBUF = dat;
}

29
char spiRead() // Read the received data
{
spiReceiveWait(); // Wait until all bits receive
return(SSPBUF); // Read the received data from the buffer
}
void UART_write_char(char data)
{
while(TRMT==0);
TXREG = data;
}
void main()
{
TRISA5=1;
ADCON1=0x07;
BRGH=1;
SPBRG=51;
SYNC=0;
SPEN=1;
TRISC6=1;
TXEN=1;
spiInit(0x20, 0, 0, 0);
unsigned char data=0x30;
__delay_ms(50);
while(1)
{
PORTAbits.RA5=1;
if (PORTBbits.RB7==0){
while(PORTBbits.RB7==0);
for(int j=0; j<=5;j++){

30
spiWrite(number[j]);
data=spiRead();
__delay_ms(10);
}
}
if (!PORTAbits.RA5) {
for(int j=0; j<=5;j++){
__delay_ms(10);
spiWrite(0x31);
data=spiRead();
if (data <=0x5A) UART_write_char(data);
}
UART_write_char(0xD);
}
while(!PORTAbits.RA5);
}
}

Slave:
#pragma config FOSC = XT // Oscillator Selection bits (XT oscillator)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage In-Circuit Serial Programming Enable bit
#pragma config CPD = OFF // Data EEPROM Memory Code Protection bit
#pragma config WRT = OFF // Flash Program Memory Write Enable bits
#pragma config CP = OFF // Flash Program Memory Code Protection bit
#include <xc.h>
#include <pic16f877a.h>

31
#define _XTAL_FREQ 20000000
char number[]={0x31,0x35,0x2E,0x36,0x39,0x20};//15.69
void spiInit(char sType, char sDataSample, char sClockIdle, char sTransmitEdge)
{
TRISC5 = 0;
if(sType & 0b00000100)
{
SSPSTAT = sTransmitEdge;
TRISC3 = 1;
}
else
{
SSPSTAT = sDataSample | sTransmitEdge;
TRISC3 = 0;
}
SSPCON = sType | sClockIdle;
}
void spiWrite(char dat)
{
SSPBUF = dat;
}
char spiRead()
{
while ( !SSPSTATbits.BF );
return(SSPBUF);
}
void UART_write_char(char data)
{
while(TRMT==0);

32
TXREG = data;
}
int count=0;
int index=0;
void __interrupt() SPI_Slave_Read()
{
if(SSPIF == 1)
{
unsigned char readData = 0;
if (count==0){
readData=spiRead();
if (readData!=0x3A)
UART_write_char(readData);
}
if (count==1){
while(index<=5){
readData=spiRead();
spiWrite(number[index]);
index++;
}
index=0;
}
SSPIF = 0;
}
}

void main()
{
GIE = 1;

33
PEIE = 1;
SSPIF = 0;
SSPIE = 1;
ADCON1 = 0x07;
TRISA5 = 1;
BRGH=1;
SPBRG=51;
SYNC=0;
SPEN=1;
TRISC6=1;
TXEN=1;
spiInit(0x25, 0, 0, 0);
TRISA0=0;
char data=0;
while(1)
{ PORTAbits.RA0=PORTBbits.RB7;
if (PORTBbits.RB7==0){
count=1;
PORTAbits.RA0=PORTBbits.RB7;
while(PORTBbits.RB7==0);
count=0;
}
}
}

2. PROGRAMMING EXPLANATION
Master
Configuration Bits
#pragma config FOSC = XT: Selects the XT oscillator (crystal/resonator) for the system

34
clock.
#pragma config WDTE = OFF: Disables the Watchdog Timer.
#pragma config PWRTE = OFF: Disables the Power-up Timer.
#pragma config BOREN = OFF: Disables Brown-out Reset.
#pragma config LVP = OFF: Disables Low-Voltage In-Circuit Serial Programming.
#pragma config CPD = OFF: Disables Data EEPROM Memory Code Protection.
#pragma config WRT = OFF: Disables Flash Program Memory Write Enable bits.
#pragma config CP = OFF: Disables Flash Program Memory Code Protection.

Libraries and Constants


#include <xc.h>: Includes the XC8 compiler header for PIC microcontrollers.
#include <pic16f877a.h>: Includes the specific header for the PIC16F877A.
#define _XTAL_FREQ 8000000: Defines the clock frequency as 8 MHz, used for delays.
char number[]={0x31,0x32,0x2E,0x36,0x39,0x20}: The array includes the ASCII
characters of each number in 12.69.
void spiInit(char sType, char sDataSample, char sClockIdle, char sTransmitEdge)
{
TRISC5 = 0;
if(sType & 0b00000100) //If Slave Mode
{
SSPSTAT = sTransmitEdge;
TRISC3 = 1;
}
else //If Master Mode
{
SSPSTAT = sDataSample | sTransmitEdge;
TRISC3 = 0;
}

35
SSPCON = sType | sClockIdle;
}

This function initializes SPI communication with specific parameters, setting up whether the
SPI module is in master or slave mode, the data sampling point, the clock idle state, and the
transmission edge.
 TRISC5 = 0;: Configures pin RC5 (SPI data output, SDO) as an output.
 if (sType & 0b00000100): Checks whether the SPI type indicates slave mode.
 If it is in slave mode:
 SSPSTAT = sTransmitEdge;: Sets the SPI status register with the specified transmit
edge.
 TRISC3 = 1;: Configures pin RC3 (SPI clock, SCK) as an input (common for slave
mode).
 If it is in master mode:
 SSPSTAT = sDataSample | sTransmitEdge;: Sets the SPI status register with the data
sampling point and transmit edge.
 TRISC3 = 0;: Configures pin RC3 as an output (common for master mode).
 SSPCON = sType | sClockIdle;: Configures the SPI control register with the specified
type and clock idle state.

static void spiReceiveWait()


{
while ( !SSPSTATbits.BF ); // Wait for Data Receipt complete
}

This function waits for data to be received on the SPI bus.


 while (!SSPSTATbits.BF);: Waits until the Buffer Full bit in the SPI status register is
set, indicating that data has been received.

void spiWrite(char dat) //Write data to SPI bus


{
SSPBUF = dat;
}
This function writes data to the SPI bus.

36
 SSPBUF = dat;: Writes the specified data to the SPI buffer, initiating the SPI
transmission.

char spiRead() // Read the received data


{
spiReceiveWait(); // Wait until all bits receive
return(SSPBUF); // Read the received data from the buffer
}
This function reads data from the SPI bus.
 spiReceiveWait();: Waits for the SPI buffer to indicate that data is ready to be read.
 return SSPBUF;: Returns the data from the SPI buffer.

void UART_write_char(char data)


{
while(TRMT==0);
TXREG = data;
}
This function writes a single character to the UART transmit register.
 while (TRMT == 0);: Waits until the UART transmit register is ready to send more
data.
 TXREG = data;: Writes the specified character to the UART transmit register, sending
it over UART.

The main function initializes the PIC16F877A and performs a basic loop to control SPI and
UART communication:
void main()
{
TRISA5=1;
ADCON1=0x07;
BRGH=1;
SPBRG=51;

37
SYNC=0;
SPEN=1;
TRISC6=1;
TXEN=1;
spiInit(0x20, 0, 0, 0);
unsigned char data=0x30;
__delay_ms(50);
while(1)
{
PORTAbits.RA5=1;
if (PORTBbits.RB7==0){
while(PORTBbits.RB7==0);
for(int j=0; j<=5;j++){
spiWrite(number[j]);
data=spiRead();
__delay_ms(10);
}
}
if (!PORTAbits.RA5) {
for(int j=0; j<=5;j++){
__delay_ms(10);
spiWrite(0x31);
data=spiRead();
if (data <=0x5A) UART_write_char(data);
}
UART_write_char(0xD);
}
while(!PORTAbits.RA5);
}

38
}

 TRISA5 = 1;: Configures pin RA5 as an input.


 ADCON1 = 0x07;: Configures all analog-to-digital converter (ADC) inputs as digital
(no analog inputs).
 BRGH = 1;: Sets the UART high baud rate setting.
 SPBRG = 51;: Sets the baud rate generator to produce a 9600 baud rate for an 8 MHz
oscillator.

Loop Structure
 The infinite loop controls SPI communication based on button states and sends data
over UART when certain conditions are met.
 PORTAbits.RA5 = 1;: Sets pin RA5 high (indicating an inactive state).
 if (PORTBbits.RB7 == 0): If RB7 is low (button pressed):
 Waits for the button to be released (while (PORTBbits.RB7 == 0);).
 Sends a series of 6 characters over SPI (number[j]).

Slave:
Configuration Bits
#pragma config FOSC = XT: Selects the XT oscillator (crystal/resonator) for the system
clock.
#pragma config WDTE = OFF: Disables the Watchdog Timer.
#pragma config PWRTE = OFF: Disables the Power-up Timer.
#pragma config BOREN = OFF: Disables Brown-out Reset.
#pragma config LVP = OFF: Disables Low-Voltage In-Circuit Serial Programming.
#pragma config CPD = OFF: Disables Data EEPROM Memory Code Protection.
#pragma config WRT = OFF: Disables Flash Program Memory Write Enable bits.
#pragma config CP = OFF: Disables Flash Program Memory Code Protection.

Libraries and Constants


#include <xc.h>: Includes the XC8 compiler header for PIC microcontrollers.
#include <pic16f877a.h>: Includes the specific header for the PIC16F877A.
#define _XTAL_FREQ 8000000: Defines the clock frequency as 8 MHz, used for delays.

39
char number[]={0x31,0x35,0x2E,0x36,0x39,0x20}: The array includes the ASCII
characters of each number in 15.69.
void spiInit(char sType, char sDataSample, char sClockIdle, char sTransmitEdge)
{
TRISC5 = 0;
if(sType & 0b00000100)
{
SSPSTAT = sTransmitEdge;
TRISC3 = 1;
}
else
{
SSPSTAT = sDataSample | sTransmitEdge;
TRISC3 = 0;
}
SSPCON = sType | sClockIdle;
}

This function initializes the SPI configuration.


 TRISC5 = 0;: Sets pin RC5 (SDO, SPI Data Output) as output.
 if (sType & 0b00000100): Checks if SPI should operate in slave mode.
 If in slave mode:
 SSPSTAT = sTransmitEdge;: Sets SPI status register for transmission edge.
 TRISC3 = 1;: Sets RC3 (SPI Clock) as input.
 If in master mode:
 SSPSTAT = sDataSample | sTransmitEdge;: Sets SPI status register for data sample
point and transmission edge.
 TRISC3 = 0;: Sets RC3 as output.
 SSPCON = sType | sClockIdle;: Configures SPI control register with the given type
and clock idle state.

void spiWrite(char dat)

40
{
SSPBUF = dat;
}
Writes data to the SPI buffer.
SSPBUF = dat;: Sets the data to be transmitted over SPI.
char spiRead()
{
while ( !SSPSTATbits.BF );
return(SSPBUF);
}

 Reads data from the SPI buffer.


 while (!SSPSTATbits.BF);: Waits until the Buffer Full bit is set, indicating that data
has been received.
 return SSPBUF;: Returns the data from the SPI buffer.

void UART_write_char(char data)


{
while(TRMT==0);
TXREG = data;
}
 Writes a character to the UART transmit register.
 while (TRMT == 0);: Waits until the UART transmit buffer is empty.
 TXREG = data;: Writes the data to the UART transmit register for transmission.

int count=0;
int index=0;
void __interrupt() SPI_Slave_Read()
{
if(SSPIF == 1)

41
{
unsigned char readData = 0;
if (count==0){
readData=spiRead();
if (readData!=0x3A)
UART_write_char(readData);
}
if (count==1){
while(index<=5){
readData=spiRead();
spiWrite(number[index]);
index++;
}
index=0;
}
SSPIF = 0;
}
}

 Handles SPI interrupts for data reception.


 if (SSPIF == 1): Checks if the SPI interrupt flag is set.
 If count is 0:
 readData = spiRead();: Reads the received data from SPI.
 if (readData != 0x3A): If the data is not a colon (':'), write it to UART.
 If count is 1:
 while (index <= 5): Loops through an index to send a series of data over SPI.
 Reads the received data (readData = spiRead()) and writes the current indexed data to
SPI (spiWrite(number[index])).
 Resets the index after the loop.
 SSPIF = 0;: Resets the SPI interrupt flag.

void main()

42
{
GIE = 1;
PEIE = 1;
SSPIF = 0;
SSPIE = 1;
ADCON1 = 0x07;
TRISA5 = 1;
BRGH=1;
SPBRG=51;
SYNC=0;
SPEN=1;
TRISC6=1;
TXEN=1;
spiInit(0x25, 0, 0, 0);
TRISA0=0;
char data=0;
while(1)
{ PORTAbits.RA0=PORTBbits.RB7;
if (PORTBbits.RB7==0){
count=1;
PORTAbits.RA0=PORTBbits.RB7;
while(PORTBbits.RB7==0);
count=0;
}
}
}

Initializes the microcontroller and contains the main loop.

43
 spiInit(0x25, 0, 0, 0);: Initializes SPI in slave mode with Slave Select disabled
(SPI_SLAVE_SS_DIS), data sample in the middle
(SPI_DATA_SAMPLE_MIDDLE), clock idle low (SPI_CLOCK_IDLE_LOW), and
idle-to-active transmission edge.
 TRISA0 = 0;: Configures RA0 as output.
 PORTAbits.RA0 = PORTBbits.RB7;: Mirrors the state of RB7 to RA0.
 if (PORTBbits.RB7 == 0): If RB7 is low (button pressed):
 Sets count = 1.
 Waits until the button is released (while (PORTBbits.RB7 == 0);).

4. RESULT

44
5. VIDEO SIMULATION
Google Drive link
https://drive.google.com/file/d/1_ReS6fP8KHAh-0EAb1MuMopzHznBzfqk/view?
usp=drive_link

45

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy