/****************************************************************************
DC MOTOR SPEED CONTROL
PICUINO APPLICATION BOARD
https://sites.google.com/site/picuino
****************************************************************************/
#include <p18cxxx.h>
#include <stdio.h>
#define FOSC 20000000
#define BAUD 57600
#define DCMOT_TRIS_AN0 TRISAbits.TRISA0
#define DCMOT_AN0 LATAbits.LATA0
#define DCMOT_TRIS_ENABLE TRISCbits.TRISC0
#define DCMOT_ENABLE LATCbits.LATC0
#define DCMOT_TRIS_MOT1 TRISCbits.TRISC1
#define DCMOT_MOT1 LATCbits.LATC1
#define DCMOT_TRIS_MOT2 TRISCbits.TRISC2
#define DCMOT_MOT2 LATCbits.LATC2
/****************************************************************************
INTERRUPTS
****************************************************************************/
#define TMR0_COUNT (FOSC/((unsigned long)4*4*250*1000)) // Number of carrys per second
unsigned char milli_second, sys_clk;
void isr_main(void);
#pragma code HIGH_INTERRUPT_VECTOR = 0x0008
void High_ISR (void) {
_asm goto isr_main _endasm
}
#pragma interrupt isr_main
void isr_main(void) {
if (INTCONbits.TMR0IF==1) { // if TIMER0 overflow interrupt
INTCONbits.TMR0IF = 0; // Clear Timer0 interrupt flag
TMR0L += 6+3;
sys_clk--;
if (sys_clk==0) {
sys_clk = TMR0_COUNT;
milli_second++;
}
}
}
/*
Initialize isr routine and Timer0
*/
void isr_init(void) {
INTCON = 0b11100000; // Enable interrupts
T0CON = 0b11000001; // Prescaler = 4
TMR0L = 6+3;
sys_clk = TMR0_COUNT;
milli_second = 0;
}
/****************************************************************************
ADC FUNCTIONS
****************************************************************************/
/*
Read Analog Input AN0 or AN1
*/
unsigned short adc_read(unsigned char channel) {
union {
struct {
unsigned char lob;
unsigned char hib;
};
unsigned short word;
} adc_val;
// Configure ADC
ADCON0 = 0b00000001 + (channel<<2); // ADC on
ADCON1 = 0b00001101; // Analog inputs = AN0, AN1
ADCON2 = 0b10010101; // TAD
// Make an ADC conversion
ADCON0bits.GO = 1; // ADC Hold and Start conversion
while (ADCON0bits.GO==1); // Conversion
// Configure ADC
ADCON0 = 0b00000000; // ADC off
ADCON1 = 0b00001111; // All pins to digital inputs
// Return ADC conversion
adc_val.lob = ADRESL;
adc_val.hib = ADRESH;
return adc_val.word;
}
/****************************************************************************
PWM FUNCTIONS
****************************************************************************/
/*
Initialize and configure PWM module
D = Duty cycle in range 0 .. 200
*/
void pwm1_init(int D) {
T2CON = 0b00000100; // Timer2 on, prescaler = 1
PR2 = 250; // PWM Period = [(PR2) + 1] • TCY •(TMR2 Prescale Value)]
if (D > 1000) D = 1000;
if (D<0) D = 0;
DCMOT_MOT1 = 0;
CCP1CON = 0b00001100;
if (D & 1) CCP1CON |= (1<<4);
if (D & 2) CCP1CON |= (1<<5);
D = D>>2;
CCPR1L = D;
}
/****************************************************************************
RS232 FUNCTIONS
****************************************************************************/
/*
Initialize USART for RS232 comunications
*/
void rs232_init(void) {
PIE1bits.TXIE = 0; // Disable RS232 interrupts
BAUDCONbits.BRG16 = 0; // BRG16: 16-Bit Baud Rate Register Enable bit
SPBRGH = 0;
SPBRG = (FOSC/(16*BAUD))-1; // Real Baud = FOSC/(16*(SPBRG+1))
TXSTA = 0b00100110;
RCSTA = 0b10010000;
TRISCbits.TRISC6 = 0; // Enable TX output
}
/*
Send char to USART with pooling
*/
int rs232_puts(char *buff, char pos) {
if (buff[pos] == 0)
return -1;
if (PIR1bits.TXIF == 1) {
TXREG = buff[pos];
return pos+1;
}
else
return pos;
}
/****************************************************************************
PID CONTROL
****************************************************************************/
#define KP 1.0
#define KI 3.3
signed int Vref, Vmot, Err, Integ, Vo;
void pid_control(int Vref) {
char sample;
if (milli_second == 10) {
milli_second = 0;
}
//*********************************
// BACK EMF SENSE
//*********************************
while (milli_second == 0) {
DCMOT_ENABLE = 0;
sample = 0;
}
if (milli_second == 1 && sample == 0) {
Vmot = adc_read(0);
DCMOT_ENABLE = 1;
sample = 1;
}
//*********************************
// PID Speed control
//*********************************
if (sample==1) {
sample = 2;
Err = Vref - Vmot;
Integ += Err;
if (Integ>30000) Integ = 30000;
if (Integ<-30000) Integ = -30000;
Vo = Vref + Err*KP + Integ/((int)(100.0/KI));
pwm1_init(Vo);
}
}
/****************************************************************************
MAIN PROGRAM
****************************************************************************/
void main(void) {
char buff[80], buff_p;
char send;
//*********************************
// Initialize
//*********************************
CMCON = 0b00000111;
ADCON1 = 0b00001111;
rs232_init();
isr_init();
DCMOT_MOT1 = 0;
DCMOT_MOT2 = 0;
DCMOT_ENABLE = 0;
DCMOT_TRIS_MOT1 = 0;
DCMOT_TRIS_MOT2 = 0;
DCMOT_TRIS_ENABLE = 0;
DCMOT_TRIS_AN0 = 1;
fprintf (_H_USART
, "\r\nSystem OK.\r\n");
//*********************************
// DC MOTOR SPEED CONTROL
//*********************************
Integ = 0;
send = 0;
while(1) {
pid_control(10);
//*********************************
// Print PID values
//*********************************
TRISBbits.TRISB5 = 0;
if (milli_second==3 && send == 0) {
sprintf (buff
, "Vm=%4d\tInteg=%5d\tVo=%4d\r\n", Vmot
, Integ
, Vo
); buff_p = 0;
send = 1;
}
if (send) {
buff_p = rs232_puts(buff, buff_p);
if (buff_p == -1)
send = 0;
}
}
}