Author Topic: PIC16F676 three phase waveform generation help  (Read 9464 times)

0 Members and 1 Guest are viewing this topic.

Online krish2487Topic starter

  • Frequent Contributor
  • **
  • Posts: 504
  • Country: dk
PIC16F676 three phase waveform generation help
« on: August 10, 2012, 01:56:11 pm »
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: [Select]
/*
 * 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();
}
« Last Edit: August 10, 2012, 01:57:42 pm by krish2487 »
If god made us in his image,
and we are this stupid
then....
 

Offline TerminalJack505

  • Super Contributor
  • ***
  • Posts: 1310
  • Country: 00
Re: PIC16F676 three phase waveform generation help
« Reply #1 on: August 11, 2012, 01:38:01 am »
There's at least one problem that I see. 

The following code doesn't do what you think:

Code: [Select]
     R_PHASE_INDEX++ % DAC_BUFFER_SIZE;
     Y_PHASE_INDEX++ % DAC_BUFFER_SIZE;
     B_PHASE_INDEX++ % DAC_BUFFER_SIZE;

You need to say something like this:

Code: [Select]
    R_PHASE_INDEX = (R_PHASE_INDEX + 1) % DAC_BUFFER_SIZE;

This code shows the behavior of both statements:

Code: [Select]
#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;
}

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.
 

Online krish2487Topic starter

  • Frequent Contributor
  • **
  • Posts: 504
  • Country: dk
Re: PIC16F676 three phase waveform generation help
« Reply #2 on: August 11, 2012, 03:39:42 am »
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!!!
If god made us in his image,
and we are this stupid
then....
 

Online krish2487Topic starter

  • Frequent Contributor
  • **
  • Posts: 504
  • Country: dk
Re: PIC16F676 three phase waveform generation help
« Reply #3 on: August 11, 2012, 04:18:16 pm »
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!!!
If god made us in his image,
and we are this stupid
then....
 

Offline TerminalJack505

  • Super Contributor
  • ***
  • Posts: 1310
  • Country: 00
Re: PIC16F676 three phase waveform generation help
« Reply #4 on: August 11, 2012, 04:36:07 pm »
I think an if statement would be the fastest way to do it.  That shouldn't take more than a half dozen instructions.
 

Online krish2487Topic starter

  • Frequent Contributor
  • **
  • Posts: 504
  • Country: dk
Re: PIC16F676 three phase waveform generation help
« Reply #5 on: August 11, 2012, 05:34:18 pm »
Okay..

i guess i ll have to rewrite it using an if statement..

i had a doubt about the processor overheads....

thanks jack..
If god made us in his image,
and we are this stupid
then....
 

Offline TerminalJack505

  • Super Contributor
  • ***
  • Posts: 1310
  • Country: 00
Re: PIC16F676 three phase waveform generation help
« Reply #6 on: August 11, 2012, 06:22:36 pm »
I'd just replace those three lines with these:

Code: [Select]
    if (++R_PHASE_INDEX == DAC_BUFFER_SIZE) R_PHASE_INDEX = 0;
    if (++Y_PHASE_INDEX == DAC_BUFFER_SIZE) Y_PHASE_INDEX = 0;
    if (++B_PHASE_INDEX == DAC_BUFFER_SIZE) B_PHASE_INDEX = 0;
 

Online krish2487Topic starter

  • Frequent Contributor
  • **
  • Posts: 504
  • Country: dk
Re: PIC16F676 three phase waveform generation help
« Reply #7 on: August 12, 2012, 09:43:15 am »
Thanks jack..

tried it...

Still the same effect.

I have posted the question on microchips 14bit forums.
Ian.M over there suggested that i am using up precious processor cycles in bit-banging the serial data.

He suggested i move over to a PIC with hardware SPI port and free up the processor to other tasks.

I ll be updating here shortly after i port the code to a hardware SPI capability PIC.

Thanks for the inputs!!!
If god made us in his image,
and we are this stupid
then....
 

Offline amyk

  • Super Contributor
  • ***
  • Posts: 8284
Re: PIC16F676 three phase waveform generation help
« Reply #8 on: August 12, 2012, 12:41:14 pm »
Not sure if this might be too late or outside your requirements, but have you considered an analog solution?
 

Online krish2487Topic starter

  • Frequent Contributor
  • **
  • Posts: 504
  • Country: dk
Re: PIC16F676 three phase waveform generation help
« Reply #9 on: August 12, 2012, 04:52:38 pm »
@amyk

Yes and it was decided to use a micro to do the job
It does not meet the requirements of the legacy design.

The DACs in the output stage are there for a reference regulation in the design of the feedback loop.

Thanks for the input, it is an interesting avenue to explore (on my own, not related to the company..)
If god made us in his image,
and we are this stupid
then....
 

Offline poorchava

  • Super Contributor
  • ***
  • Posts: 1672
  • Country: pl
  • Troll Cave Electronics!
Re: PIC16F676 three phase waveform generation help
« Reply #10 on: August 13, 2012, 08:55:19 am »
Check the silicon errata for the MCU. It's Microchip, errata is more important than the datasheet itself. Maybe you will find something like "port c pins are limited in switching frequency to [xyz] Hz". Wouldn't be the firsnt, second.. or tenth fail like that from Microchip.

I love the smell of FR4 in the morning!
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26994
  • Country: nl
    • NCT Developments
Re: PIC16F676 three phase waveform generation help
« Reply #11 on: August 13, 2012, 04:17:01 pm »
I'd go for an ARM based microcontroller when re-designing old stuff. No need to worry about CPU cycles and most of them have hardware SPI ports. NXP's ARM controllers have buffered SPI interfaces so you could dump 3 bytes into them without waiting and the hardware does the rest.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Online krish2487Topic starter

  • Frequent Contributor
  • **
  • Posts: 504
  • Country: dk
Re: PIC16F676 three phase waveform generation help
« Reply #12 on: August 13, 2012, 05:19:21 pm »
@poorchava..
Yep.. i am presently doing that...

:-D


@nctnico
I would if i could.

1.I do not know anything about ARM.
2. I am on a deadline for delivery to spend time acquiring experience on ARM.
3. Its an overkill for the intended application.
If god made us in his image,
and we are this stupid
then....
 

Online jahonen

  • Super Contributor
  • ***
  • Posts: 1054
  • Country: fi
Re: PIC16F676 three phase waveform generation help
« Reply #13 on: August 13, 2012, 05:59:14 pm »
Since you seem to be implementing a DDS, may I suggest that you make your sine table size equal to 2^8, so you can replace the modulo operator by just an and operation, even better, let the binary arithmetic do it for you, by using a 8 bit index variable so an overflow will occur naturally correct manner.

Regards,
Janne
 

Online krish2487Topic starter

  • Frequent Contributor
  • **
  • Posts: 504
  • Country: dk
Re: PIC16F676 three phase waveform generation help
« Reply #14 on: August 15, 2012, 08:48:04 pm »
Sorry for the delay, I had a hectic start this week and things are not improving with the code

I have also posted the same issue on microchips forums and between eevblog and microchips forums,
The code is "somewhat working", for the lack of a better word.


@jahonen
Thanks,
 that made a lot of sense and i curse myself for not thinking of the beauty and simplicity in what you suggested.

I replaced the 16f676 with a 16f883 at 20MHz for the hardware SPI.
 
I noticed 2 problems
1->The zero crossing indicators RC3, RC4, RC5 are at 33% duty cycle instead of the expected 50%
 
2-> The serial data is getting "corrupted" not what i expect.
   
So i updated the code and the initial offset and still the same result.
 
I hooked up the pins serial clock, serial data, strobe data, 3 zero crossing indicators, debug pin out to a logic analyzer and captured the data.
(I used a debug pin RC2 to toggle every time i update the index values and sent the data serially to check for the duration of the loop)
 
1) The duration of looking up, sending the data serially is "deterministically" not constant.
  The entire time required for looking up the values and sending it is much less than the Timer0 interrupt-to-interrupt duration. I expect that the time taken for one interrupt-to-interrupt is constant throughout the execution of the program - This is not so.
 
2) The serial data between the strobe data toggles is not constant. I am sending only 3 bytes. each for R, Y, B. Some times i see 2, sometimes none. When such an anomaly occurs the index of one phase goes nuts and the subsequent values corrupt the intended waveform.
 
I am assuming that the above two issues are inter related. As i mentioned i use the lite compiler and i assume that because of the simplicity of the operations and the sparse usage of RAM and flash, that optimization is not a reason for this misbehavior??
Is it possible that no optimization of the code is causing this behavior?
 
Of course, it is highly possible that my code has bugs that i am blind to
(It is well past 2 in the midnight and my faculties are sluggish to say the least)
I am attaching here a C file containing the present version of the code and a html file containing the logic analyzer capture.
The forums do not allow uploading the html file. Please remove the ".txt" extension and it should work fine.
 
Thanks for the ernest support and help guys!!
If god made us in his image,
and we are this stupid
then....
 

Offline poorchava

  • Super Contributor
  • ***
  • Posts: 1672
  • Country: pl
  • Troll Cave Electronics!
Re: PIC16F676 three phase waveform generation help
« Reply #15 on: August 16, 2012, 09:42:26 am »
You need to remember that when using timer interrupts some jitter may happen. I've stumbled upon this problem when generating waveforms for driving stepper motors from pic18f2550. This is (i think...) bacause interrupt event can happen in different points in time. Especially if you are using C statements that don;t translate directly into asm (like division or modulo in most cases). This leads to interrupt reaction time not being fully deterministic.

You need to make sure that any eventual 'if' statement in the interrupt takes same amount of cycles no matter if result of test is true or false.

As you are lacking interrupt-to-interrupt time i would seriously consider using some more powerful device. Pic 16f is a total crap in this departament. Even an AVR in this price/complexity range has ~4x processing speed of the pic. I'd rather go for something like LPC11xx family (cortex m0) or STM32F0 (also cortex m0) or dspic33/pic24h. i would consider ARM, simply because they put a lot of effort in providing highly/full deterministic interrupt execution timing (it's actually one of the main advertising points of ARM cortex architectures)
I love the smell of FR4 in the morning!
 

Offline TerminalJack505

  • Super Contributor
  • ***
  • Posts: 1310
  • Country: 00
Re: PIC16F676 three phase waveform generation help
« Reply #16 on: August 16, 2012, 06:25:22 pm »
The one big problem I notice with your latest code is that you have split-up the task with one part in the timer interrupt handler and the other in the main loop.

The potential problem with this is that the timer code can interrupt and execute while the mainline code is executing.  This may be the cause of your corrupt data.  Imagine that the mainline code gets only halfway through the code in the if (UPDATE_FLAG == 1) {} block.  The indexes will be incremented in the interrupt handler and control will be returned to the mainline code.  These newly changed index values will then be used from that point forward but it is the previous values what should be used.

Also, the variables that are shared between the mainline code and the interrupt need to be declared as volatile.  This will prevent the compiler from 'caching' the variable's value.  This may also be the cause of the corruption you are seeing.  See this Wiki article for more information on the volatile keyword.
 

Online krish2487Topic starter

  • Frequent Contributor
  • **
  • Posts: 504
  • Country: dk
Re: PIC16F676 three phase waveform generation help
« Reply #17 on: August 16, 2012, 08:09:50 pm »
Thanks for pointing out the possible errors jack

Here is the updated version

Code: [Select]
#include <pic.h>
#include <htc.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>

__CONFIG(FOSC_HS & WDTE_OFF & MCLRE_ON & CP_OFF & CPD_OFF & LVP_OFF & BOREN_OFF);

#define _XTAL_FREQ 20000000

#define DAC_BUFFER_SIZE 256

#define R_PHASE_HIGH_LOW RB0
#define Y_PHASE_HIGH_LOW RB1
#define B_PHASE_HIGH_LOW RB2

#define SERIAL_DATA_STROBE_PIN RC6

#define STROBE_SERIAL_DATA() {SERIAL_DATA_STROBE_PIN=0; NOP();NOP();NOP();NOP();NOP(); SERIAL_DATA_STROBE_PIN=1;}

//The indexes represent the power on LUT index values for R,Y,B
static volatile uint8_t R_PHASE_INDEX=0,Y_PHASE_INDEX=85,B_PHASE_INDEX=170;
//A Flag to indicate to "look ahead" for the next iteration values from the LUT and serial stream them
static volatile bit UPDATE_FLAG=0;
//A 256 entry LUT for 0 - 180 degrees representing 0 - 5V peak sine wave
const uint8_t DAC_LOOKUP_TABLE[DAC_BUFFER_SIZE]={0,3,6,9,13,16,19,22,25,28,31,34,38,41,44,
                                                 47,50,53,56,59,62,65,68,71,74,77,80,83,86,
                                                 89,92,95,98,101,104,107,109,112,115,118,121,
                                                 123,126,129,132,134,137,140,142,145,147,150,
                                                 152,155,157,160,162,165,167,169,172,174,176,
                                                 179,181,183,185,187,190,192,194,196,198,200,
                                                 202,203,205,207,209,211,213,214,216,218,219,
                                                 221,222,224,225,227,228,230,231,232,234,235,
                                                 236,237,238,239,241,242,243,243,244,245,246,
                                                 247,248,248,249,250,250,251,251,252,252,253,
                                                 253,254,254,254,254,255,255,255,255,255,255,
                                                 255,255,255,255,254,254,254,254,253,253,252,
                                                 252,251,251,250,250,249,248,248,247,246,245,
                                                 244,243,243,242,241,239,238,237,236,235,234,
                                                 232,231,230,228,227,225,224,222,221,219,218,
                                                 216,214,213,211,209,207,205,203,202,200,198,
                                                 196,194,192,190,187,185,183,181,179,176,174,
                                                 172,169,167,165,162,160,157,155,152,150,147,
                                                 145,142,140,137,134,132,129,126,123,121,118,
                                                 115,112,109,107,104,101,98,95,92,89,86,83,80,
                                                 77,74,71,68,65,62,59,56,53,50,47,44,41,38,34,
                                                 31,28,25,22,19,16,13,9,6,3,0};
void Setup_Timer0(void);
void Setup_Ports(void);
void Setup_Serial_Port(void);
void Send_Serial_Byte(uint8_t Serial_Data);

int main()
{
    //Inital delay for letting the system startup
    __delay_ms(10);

    //Setup ports for Serial commn, I/O
    Setup_Ports();
    Setup_Serial_Port();
       

    //Preload the index values and send to serial port for the first run
    // R_PHASE_HIGH_LOW = 0
    // Y_PHASE_HIGH_LOW = 0
    // B_PHASE_HIGH_LOW = 1
    // Y_PHASE_HIGH_LOW automatically gets toggled to 1 in ISR
    //
    PORTB=0x04;
   
    //Send Serial data for the first run before entering the while(1) loop
    //Index values for R,Y,B are preloaded
    ei();
    Send_Serial_Byte(DAC_LOOKUP_TABLE[R_PHASE_INDEX]);

    Send_Serial_Byte(DAC_LOOKUP_TABLE[Y_PHASE_INDEX]);

    Send_Serial_Byte(DAC_LOOKUP_TABLE[B_PHASE_INDEX]);

    //Setup timer0 to give interrupts every 42us
    Setup_Timer0();
    //Enable timer0 to increment on internal clock cycle
    T0CS=0;
   
    while(1)
    {
    }

}

void interrupt Interrupt_Routine()
{
    //If timer0 is generating the interrupt
    if(T0IF==1)
    {
        RC2=1;
        //Latch the data sent in the previous invocation of the interrupt
        STROBE_SERIAL_DATA();

       //Clear Timer0 Interrupt
        T0IF=0;
        //Check for 256 is not required as 255 + 1 overflows to 0
        ++R_PHASE_INDEX;
        ++Y_PHASE_INDEX;
        ++B_PHASE_INDEX;


        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;


         //Lookup the value of the DAC data from the look up table and serial flush
         Send_Serial_Byte(DAC_LOOKUP_TABLE[R_PHASE_INDEX]);
         Send_Serial_Byte(DAC_LOOKUP_TABLE[Y_PHASE_INDEX]);
         Send_Serial_Byte(DAC_LOOKUP_TABLE[B_PHASE_INDEX]);


         //Debug indicator bit
         RC2=0;
 
       //Reload Timer0 with Preload value
        //92 preload values gave approx 50 cycles freq
        //actual calculated value to be preloaded is 105
        TMR0=255 - 92;
       
    }
    //If the serial data is generating the interrupt
    if(SSPIF==1)
    {
        //Clear the interrupt flag and return to main
        SSPIF=0;
       
    }

}

void Setup_Timer0(void)
{
    /*
     *Registers associated with TIMER0
     * TMR0
     * INTCON
     * OPTION_REG
     * TRISA
     *
     * Steps to configure TIMER0
     *
     * Disable all analog inputs in ANSEL and check comparator inputs
     * Set prescaler to WDT
     *
     * Divide 10 ms (Half cycle into 240 parts)
     * 42 uSeconds per TIMER0 interrupt
     * Instruction time per clock is 200 nSeconds
     * Therefore 210 instructions reqd for 42 uSeconds interrupt
     *  Preload 255-210 into Timer 0
     * Enable Interrupts
     * DO NOT clear T0CS

    */
   
    OPTION_REG=0xF1;
    INTCON=0xE0;
    TMR0=255 - 92;
}
void Setup_Serial_Port(void)
{
    //Registers associated with SPI protocol
    //INTCON
    //PIE1
    //PIR1
    //SSPBUF
    //SSPCON
    //SSPSTAT
    //TRISA
    //TRISC
    // Data is latched into the 595 on rising edge of clock
    // Clock idle is LOW
    // Clock Polarity is transmitted on rising edge of SCK
    // Mode of operation of SPI should be 0
    // CKP = 0
    // CKE = 1
    // Disable interrupts , check if the transmitted/received bit if full
    // Enable MSSP interrupt and clear the interrupt flag
    PIE1=0x80;
    PIR1=0x00;

    SSPSTAT=0x40;
    SSPCON=0x20;
   // SSPCON2=0x00;

}
void Send_Serial_Byte(uint8_t Serial_Data)
{
   
    //Load SSPBUF to start serial data transmission
    SSPBUF=Serial_Data;
    //Wait till the SSPIF interrupt interrupt is triggered
    __delay_us(2);

}

void Setup_Ports()
{
    TRISB=0x00;
    TRISC=0x00;

    PORTB=0x00;
    PORTC=0x00;

    //Disables all analog inputs
    //All pins only Digital I/O
    ANSEL=0x00;

    //Disable comparator1 and 2
    CM1CON0=0x00;
    CM2CON0=0x00;
}

But still the same issue
1-> Missing serial clock or an extra one during some transmissions.
2-> Corrupt data sent to DAC

I noticed that this misbehaviour is mostly when one phase index approaches 0 then the entire data goes bonkers and starts spewing out wrong data.

Any other obvious pitfall that may lead to inconsistent behavior of the code???

Thank you..
If god made us in his image,
and we are this stupid
then....
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26994
  • Country: nl
    • NCT Developments
Re: PIC16F676 three phase waveform generation help
« Reply #18 on: August 16, 2012, 08:18:46 pm »
You need to remember that when using timer interrupts some jitter may happen. I've stumbled upon this problem when generating waveforms for driving stepper motors from pic18f2550. This is (i think...) bacause interrupt event can happen in different points in time. Especially if you are using C statements that don;t translate directly into asm (like division or modulo in most cases). This leads to interrupt reaction time not being fully deterministic.
When processing signals I do all the processing inside the interrupt. To avoid jitter the first thing I do is output the data values and then calculate the values that need to be output at the next interrupt.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Online krish2487Topic starter

  • Frequent Contributor
  • **
  • Posts: 504
  • Country: dk
Re: PIC16F676 three phase waveform generation help
« Reply #19 on: August 16, 2012, 08:27:18 pm »
@nctnico

Precisely what i have done now...

Kept the while(1) loop completely empty and moved all the processing into the interrupt handler.

Even inside the interrupt handler, latch the data first and then look ahead and stream the data.

Even so, it is still not deterministic.

Rather it is unpredictable in terms of serial data and serial clock which makes the timing in-deterministic.

If god made us in his image,
and we are this stupid
then....
 

Offline poorchava

  • Super Contributor
  • ***
  • Posts: 1672
  • Country: pl
  • Troll Cave Electronics!
Re: PIC16F676 three phase waveform generation help
« Reply #20 on: August 17, 2012, 12:15:10 am »
I would consider fully drivinf serial comms with interrupts. Currently you are using
Code: [Select]
//Wait till the SSPIF interrupt interrupt is triggered
    __delay_us(2);

I think this may be source of your problem. Using hardcoded loop-type delay isn't something to do on a resource limited MCU in the first place. On the to of that I strongly suspect that Microchip's implementation of Delay functions may disable interrupts (eg. pending interrupt is not serviced as long as delay loop lasts).

You may implement some kind of queue, write on byte for transmiting and then go to other tastks. When transmit is complete you get an interrupt and send another byte in queue. Of course this would be lower priority than signal generation. This way you omit the hardcoded delay loop and also probably save some CPU cycles.
I love the smell of FR4 in the morning!
 

Online krish2487Topic starter

  • Frequent Contributor
  • **
  • Posts: 504
  • Country: dk
Re: PIC16F676 three phase waveform generation help
« Reply #21 on: August 17, 2012, 03:33:18 am »
Actually the comments are self explanatory.

I am using interrupt driven SPI transfer.
If you look at the interrupt routine, you ll see

Code: [Select]
//If the serial data is generating the interrupt
    if(SSPIF==1)
    {
        //Clear the interrupt flag and return to main
        SSPIF=0;
       
    }

i used a small delay of about 3 useconds to make sure that a particular SPI byte transfer takes place and generates an interrupt.

the SPI is set up to run at FoSC/4.
With a 20MHz cyrstal thats about 5 MHz.

the serial clock freq should be about 200ns.

It takes about 1.6us for 8 clock cycles.

I am using a 2 useconds delay to make sure that the interrupt is triggered before the  __delay_us(2) finishes and that the interrupt is attended to.

If you look at the code, in the interrupt section


Code: [Select]
Send_Serial_Byte(DAC_LOOKUP_TABLE[R_PHASE_INDEX]);
         Send_Serial_Byte(DAC_LOOKUP_TABLE[Y_PHASE_INDEX]);
         Send_Serial_Byte(DAC_LOOKUP_TABLE[B_PHASE_INDEX]);

Because all the serial data is sent one after another and the MSSP port being a hardware SPI port, i wasnt sure that between one serial transfer to the next the processor would generate a proper serial byte transfer interrupt. Hence, i assumed it would be prudent to give a small delay between transfers to ensure that the interrupt is definitely generated and serviced.

However, now that you mention it, it might be worth trying to see how it performs without the __delay_us(2) in the serial byte transfer routine.
If god made us in his image,
and we are this stupid
then....
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf