Electronics > Microcontrollers

PIC16F676 three phase waveform generation help

(1/5) > >>

krish2487:
Hello,

The company where i presently work has given me an assignment with several constraints.

1)We have a legacy design whose black-box inputs and outputs must remain the same
(This is essential as we have about 3000 such machines on the field)

The design is that of a three phase waveform generator card with all the three phase sinewave references and a zero crossing (high low) as outputs(apart from other IO signals)

My job is to come up with the 3 phase waveform generator and the ZCD outputs.

2) The penultimate stage to the three phase waveform outputs should be three DAC0808

My design should drive the three DACs directly.

The previous design used a 40 pin PIC16F877A driving the DACS directly.
Due to firmware issues we have to redo the 3 phase section and I have been given the responsibility.
The engineer who worked on the earlier design has left the company and code documentation is non-existant for the older version.

I should NOT use the older PIC16F877A card due to hardware limitations on the older rev board.

I have come up with a 16F676 clocked at 20 Mhz driving three cascaded 595 to drive the DAC inputs.
This is the basic idea :-
Calculate the values to send to the dac,
send it to the 595,
latch it in the ISR so that the data is latched for all the three ports simultaneously.
and when the look up index values wraps around to zero toggle the zero crossing high low bit.

Here is the code i have come up with
(I am unable to paste the code here so i am just attaching it.)


As a first step i have attached three leds to the RC3, RC4, RC5  i.e the zero crossing indicator pins with external current limiting resistors.
But the leds behave erratically.
I expect to see all the leds on with equal intensity,

but only RC4 led blinks with highest intensity and RC3 is VERY dim and RC5 does not light up at all.

Probing the MCU signals with a Open logic sniffer i saw that the data what i expect to see on the serial line and the zero crossing high low toggle line is... garbage, and some times non existant.

I should mention that i have tested this only on a breadboard and only the power supply is connected apart from the leds.
So what do you guys think??

Code errors or breadboard issues due to 20MHz clock??

Instruction timing miscalculations are abundant but atleast the code should blink all the leds equally as the same process is done for all the three phases.

PS: This is not the full version of the code, but this version itself is not working as expected to enable me to progress further.
I  do apologize for the lack of more information in the post but the code is reasonably detailed in terms of calculations if not operation.

Thanks in advance!!!

PS again : I was able to edit and insert code after posting. so here it is folks.



--- Code: ---/*
 * File:   main.c
 * Author: N S Krishna Chaitanya
 *
 * Created on 8 August, 2012, 1:24 PM
 * Three phase waveform generator
 * Processor           : PIC16F676
 * Clock Frequency     : 20MHz
 *
 * Brief Explanation :
 *
 * The PIC is connected to three daisy-chained 74HC595 serial - to parallel
 * shift registers. The output of the shift registers are connected to 3 DAC0808
 * which convert the 8 bit sinewave data into reference voltages for the 3 phases
 *
 * Three more pins are used to trigger a HIGH-LOW signal for driving the IGBTs.
 *
 * Pin Connections
 *
 * RC0 -> Serial Data
 * RC1 -> Serial Clock
 * RC2 -> Data Latch
 *
 * RC3 -> R Phase High Low bit
 * RC4 -> Y Phase High Low bit
 * RC5 -> B Phase High Low bit
 *
 * Look-up table for DAC consists of 240 samples for half a cycle i.e 10 milliseconds
 * Each Phase is associated with an index and the offset w.r.t 0 is added at initialization
 * and subsequently index is only incremented by 1 in each invocation of ISR
 *
 * A Pseudo circular buffer ensures that the index never overflows and wraps around to
 * 0th element at which point the respective phase High Low bit is toggled indicating a
 * zero crossing.
 *
 * The PIC "looks ahead" for the next data to be sent presends it in this invocation
 * and only "latches" the data on the next invocation of the ISR
 * Ensuring that the data appears simultaneously on all the DAC inputs
 */

//Necessary include files and #defines for compiler and __CONFIG() BLOCK

#include <stdint.h>
#include <stdlib.h>
#include <htc.h>
#include <pic.h>

//__CONFIG()
/*
 *XT crystal input
 *Watchdog timer off
 * _MCLRE functions as reset input
 * all code protections off
 */
__CONFIG(FOSC_XT & WDTE_OFF & MCLRE_ON & CPD_OFF & CP_OFF & BOREN_OFF);

//External crystal frequency 20MHz with external 22pf ceramic load capacitors
//Required to get the least instruction cycle time
#define _XTAL_FREQ 20000000

//#defines, global variables and function definitions for main()

//Total number of samples in the lookup table for DAC
#define DAC_BUFFER_SIZE 240

//HIGH LOW zero crossing indicators for R,Y,B Phases
#define R_PHASE_HIGH_LOW RC3
#define Y_PHASE_HIGH_LOW RC4
#define B_PHASE_HIGH_LOW RC5

//Serial Data, clock and latch for the PIC
#define SERIAL_DATA      RC0
#define SERIAL_CLOCK     RC1
#define DATA_LATCH       RC2

//Inline function to latch the existing data in the 595 storage register
//to the inputs of the DAC to update the dac in the ISR
#define STROBE_SERIAL_DATA() {DATA_LATCH=0; NOP(); NOP(); DATA_LATCH=1;}

void SETUP_TIMER0(void);
void SEND_SERIAL_DATA();

//240 samples representing half sine wave
const uint8_t DAC_LOOKUP_TABLE[DAC_BUFFER_SIZE]={0,3,5,8,11,13,16,19,21,24,27,29,32,35,37,40,42,45,48,50,53,55,58,
60,63,66,68,71,73,76,78,81,83,85,88,90,93,95,97,100,102,104,107,
109,111,113,116,118,120,122,124,126,128,130,132,135,137,138,140,
142,144,146,148,150,152,153,155,157,159,160,162,163,165,167,168,
170,171,173,174,175,177,178,179,181,182,183,184,185,186,187,188,
189,190,191,192,193,194,195,196,196,197,198,198,199,200,200,201,
201,201,202,202,203,203,203,203,204,204,204,204,204,204,204,204,
204,204,204,203,203,203,203,202,202,201,201,201,200,200,199,198,
198,197,196,196,195,194,193,192,191,190,189,188,187,186,185,184,
183,182,181,179,178,177,175,174,173,171,170,168,167,165,163,162,
160,159,157,155,153,152,150,148,146,144,142,140,138,137,135,132,
130,128,126,124,122,120,118,116,113,111,109,107,104,102,100,97,95,
93,90,88,85,83,81,78,76,73,71,68,66,63,60,58,55,53,50,48,45,42,40,
37,35,32,29,27,24,21,19,16,13,11,8,5,3};


static uint8_t SERIAL_DATA_BUFFER[3];
//SERIAL_DATA_BUFFER[0] is for R Phase
//SERIAL_DATA_BUFFER[1] is for Y Phase
//SERIAL_DATA_BUFFER[2] is for B Phase

static uint8_t R_PHASE_INDEX=0,Y_PHASE_INDEX=80,B_PHASE_INDEX=160;
// Offsets for the lookup table
// 120 degrees phase difference between phases
// 6.6 milliseconds phase difference (time)
// R Phase = 0 milliseconds with High Low Bit = 1
// Y Phase = 6.6 milliseconds with High Low Bit = 0
// B Phase = 13.2 milliseconds with High Low bit = 1

int main()
{

    __delay_ms(10);
    //Initialize delay for startup time and oscillator stabilization

    //Data direction registers and initializations
    //Set all the PORTC to outputs and preload to 0
    TRISC=0x00;
    PORTC=0x00;
    NOP();
    NOP();
    //Phase R and Phase B should trigger high
    //But index of Phase R being 0, ISR automatically
    //toggles the Phase R High Low Bit

    R_PHASE_HIGH_LOW=0;
    NOP();
    NOP();
    Y_PHASE_HIGH_LOW=0;
    NOP();
    NOP();

    B_PHASE_HIGH_LOW=1;
    NOP();
    NOP();

    //Setup TIMER0 to give interrupt every 10 milli-seconds / 240 parts
    //Clock is 20 MHz divided by 4 i.e 4 MHz internal clock
    //Instruction cycle is 200 nano-seconds
    //Minimum prescaler for TIMER0 is 2
   
    SETUP_TIMER0();

    //The first element in the serial buffer is R Phase
    //The second element in the serial buffer is Y Phase
    //The third element in the serial buffer is B Phase
    SERIAL_DATA_BUFFER[0]=DAC_LOOKUP_TABLE[R_PHASE_INDEX]; //R Phase Serial Data
    SERIAL_DATA_BUFFER[1]=DAC_LOOKUP_TABLE[Y_PHASE_INDEX]; //Y Phase Serial Data
    SERIAL_DATA_BUFFER[2]=DAC_LOOKUP_TABLE[B_PHASE_INDEX]; //B Phase Serial data

    //Set the Timer0/Counter input to Internal Clock
    T0CS=0;

        while(1)
        {
            //Wait for Timer0 interrupt
        }
    return (EXIT_SUCCESS);
}

void SETUP_TIMER0(void)
{
    //Registers associated with TIMER0
    //TMR0
    //INTCON
    //OPTION_REG
    //TRISA
    //Enable TMR0 interupts, peripheral interrupts, Clear TMR0IF
    //Set prescaler to TMR0 and prescaler value to 2


    INTCON=0x60;
    //Enable Peripheral interrupts, TMR0 interrupts, clears TMR0IF
    OPTION_REG=0xF0;
    //Set transition to RA2 input, assign 000 prescaler to TMR0
    TMR0=255 - 105;
    //Preload the timer
    //Overflow value for timer is 255
    // TIMER0 interrupt should happen every 10 milliseconds / 240 parts
    // 41.6 ~ 42 useconds
    // with 200 nanoseconds instruction cycle 5 instructions to a useconds
    // and 42 * 5 = 210 instructions that can be executed.

    // Timer0 preload value
    // Full overflow of Timer0 takes 255 * 0.2 useconds = 51 useconds
    // With default prescaler of 2 to Timer0 it takes 102 useconds
    // i.e 255 count = 102 useconds
    // 42 useconds = 105 count
    // Value to preload into Timer is (255 - 105)
   
    ei();
    //Enable Interrupts
}

void SEND_SERIAL_DATA()
{
    //Select each element in the serial buffer from 0 thru 2
    for(char i=0;i<=2;i++)
    {
       //Flush each bit in the present buffer element in the reverse order
        //MSB first, LSB last
         for(char j=0;j<=7;j++)
         {

             SERIAL_DATA=(SERIAL_DATA_BUFFER[i])>>(7-j);
             //Toggle the serial clock
             SERIAL_CLOCK=1;
             NOP();
             SERIAL_CLOCK=0;
         }
    }

}


void interrupt Interrupt_Routine(void)
{
    //Clears the TIMER0 Interrupt flag
    TMR0IF=0;

    //Preload timer to give 42 useconds interrupt
    //Total instructions available is 210 instructions
    //that can be executed before the interrupt occurs again
    TMR0=255 - 105;

    //Check if any index has wrapped around to 0 and toggle the respective High Low bit
    //Indicating that zero-crossing has occured for the respective phase
        if(R_PHASE_INDEX==0)
            R_PHASE_HIGH_LOW^=1;

        if(Y_PHASE_INDEX==0)
            Y_PHASE_HIGH_LOW^=1;

        if(B_PHASE_INDEX==0)
            B_PHASE_HIGH_LOW^=1;

    //Latch the data already present on the output of the 595 to the DAC input
    STROBE_SERIAL_DATA();

    //Pseudo circular buffer for the R,Y,B indexes with wrap around when index reaches
    //239th element
     R_PHASE_INDEX++ % DAC_BUFFER_SIZE;
     Y_PHASE_INDEX++ % DAC_BUFFER_SIZE;
     B_PHASE_INDEX++ % DAC_BUFFER_SIZE;

    //Acquire the lookup values for the DAC based on present index value
    //and store in the serial data buffer
     SERIAL_DATA_BUFFER[0]=DAC_LOOKUP_TABLE[R_PHASE_INDEX]; //R Phase Serial Data
     SERIAL_DATA_BUFFER[1]=DAC_LOOKUP_TABLE[Y_PHASE_INDEX]; //Y Phase Serial Data
     SERIAL_DATA_BUFFER[2]=DAC_LOOKUP_TABLE[B_PHASE_INDEX]; //B Phase Serial data
     //Send the present data to the 74HC595 and wait for the next Interrupt
     //to signal any zero crossing and latch the 8 bit data to the 595 output
     SEND_SERIAL_DATA();
}

--- End code ---

TerminalJack505:
There's at least one problem that I see. 

The following code doesn't do what you think:


--- Code: ---     R_PHASE_INDEX++ % DAC_BUFFER_SIZE;
     Y_PHASE_INDEX++ % DAC_BUFFER_SIZE;
     B_PHASE_INDEX++ % DAC_BUFFER_SIZE;

--- End code ---

You need to say something like this:


--- Code: ---    R_PHASE_INDEX = (R_PHASE_INDEX + 1) % DAC_BUFFER_SIZE;

--- End code ---

This code shows the behavior of both statements:


--- Code: ---#include <stdio.h>

#define USE_ORIGINAL_CODE

typedef unsigned char uint8_t;

#define DAC_BUFFER_SIZE 240

const uint8_t DAC_LOOKUP_TABLE[DAC_BUFFER_SIZE]={0,3,5,8,11,13,16,19,21,24,27,29,32,35,37,40,42,45,48,50,53,55,58,
                 60,63,66,68,71,73,76,78,81,83,85,88,90,93,95,97,100,102,104,107,
                 109,111,113,116,118,120,122,124,126,128,130,132,135,137,138,140,
                 142,144,146,148,150,152,153,155,157,159,160,162,163,165,167,168,
                 170,171,173,174,175,177,178,179,181,182,183,184,185,186,187,188,
                 189,190,191,192,193,194,195,196,196,197,198,198,199,200,200,201,
                 201,201,202,202,203,203,203,203,204,204,204,204,204,204,204,204,
                 204,204,204,203,203,203,203,202,202,201,201,201,200,200,199,198,
                 198,197,196,196,195,194,193,192,191,190,189,188,187,186,185,184,
                 183,182,181,179,178,177,175,174,173,171,170,168,167,165,163,162,
                 160,159,157,155,153,152,150,148,146,144,142,140,138,137,135,132,
                 130,128,126,124,122,120,118,116,113,111,109,107,104,102,100,97,95,
                 93,90,88,85,83,81,78,76,73,71,68,66,63,60,58,55,53,50,48,45,42,40,
                 37,35,32,29,27,24,21,19,16,13,11,8,5,3};

int main(void)
{
    uint8_t R_PHASE_INDEX = 0;

    int i;

    for (i = 0; i < 1000; ++i)
    {
        if(R_PHASE_INDEX == 0)
            printf("Zero Crossing:");

#ifdef USE_ORIGINAL_CODE
        R_PHASE_INDEX++ % DAC_BUFFER_SIZE;
#else
        R_PHASE_INDEX = (R_PHASE_INDEX + 1) % DAC_BUFFER_SIZE;
#endif

        if (R_PHASE_INDEX < DAC_BUFFER_SIZE)
            printf("%d:", (int)DAC_LOOKUP_TABLE[R_PHASE_INDEX]);
        else
            printf("INVALID INDEX:");
    }

    printf("\n\n");

    return 0;
}

--- End code ---

Use your favorite Linux/Windows/OSX compiler to run it.  You'll see that your code doesn't wrap around when the index gets to 240.  Comment out the line that #defines USE_ORIGINAL_CODE to see the code that I suggested at work.

krish2487:
Thank you Jack!!

I had a doubt about shortening the code
 indeed it was Index = (Index +1) % buffer size for actual implementation

I guess i ll try after changing the code back again....
i shall be posting updates soon....

Thanks for the help!!!

krish2487:
Update on the code..

Its definitely not a RMW issue.
After i modified the code to use shadow registers and write to PORTC as a byte, it still showed the same symptoms.

So to first identify the problem i eliminated the rest of the code and wrote a bit to only blink the HIGH LOW leds after overflow and the circular buffer implementation.

i.e used the timer0 to generate an interrupt every 42 us and increased the index value using the pseudo circular buffer and toggled the leds incase the index wrapped around to 0.

and yes ,the code still did not work as i expected it to.

So i scrapped the buffer implementation and used a regular if-then statement for resetting the index and then it worked partially.

All the three leds lit up but with unequal intensity.

probing further, i increased the prescaler of the timer0 to 4 and lo behold,

all the leds light up now with equal intensity,

so it made me think that maybe the statement index=(index+1) % 240 is using up too many processor cycles to complete the look up and flush the data to the 595s.

I shall tweak the timer further to see its behavior but my present question is

is there a less processor intensive menthod to implement a pesudo ring buffer lookup???


i admit assembly is not my forte and i seek a pointer (pun intended) for a c implementation with minimal processor cycle overhead.


thanks in advance!!!

TerminalJack505:
I think an if statement would be the fastest way to do it.  That shouldn't take more than a half dozen instructions.

Navigation

[0] Message Index

[#] Next page

There was an error while thanking
Thanking...
Go to full version