thomasonw / ATmegaxxM1-C1

Arduino IDE support files for CAN enabled Atmel AVR chips: ATmega64M1, ATmega32M1, ...
GNU Lesser General Public License v2.1
31 stars 18 forks source link

Using the PSC in M1 variants #2

Closed mattmon closed 7 years ago

mattmon commented 7 years ago

Does this allow for use of the power stage controller in M1 versions of these chips?

Say, to drive an H-bridge with ultrasonic PWM without touching TIMER 0 & 1?

thomasonw commented 7 years ago

Yes - I initially did this port as part of a MPPT controller design, with specific goal of the PSC subsystems.

Take note the PSCRB fuse is set by default, causing all the PSC outputs to be pulled LOW at hardware power-on. This was to prevent unintended issues in the power subsystem until the startup code (bootloader, etc) completed its execution.

mattmon commented 7 years ago

Thanks for the added info, these chips seem great for extending arduino into harsh environments.

That said, the datasheet's section on PSC isn't particularly easy to understand.

Do you have any examples of code to initialize and manipulate the PSC outputs?

thomasonw commented 7 years ago

Not a lot - I stopped working on the MPPT controller once I found another open source project. (I am still usign the chip for its CAN module on my regulator though!) But here are a couple of snips that might be helpful?

#define TURN_ON_MOSFETS  PCTL = _BV(PCLKSEL) | _BV(PCCYC) | _BV(PRUN)   // enable MOSFET driver
#define TURN_OFF_MOSFETS PCTL = _BV(PCLKSEL) | _BV(PCCYC)               // disable MOSFET driver
                                                                        // (Use PLL clock, complete PWM cycle before stopping)

void setup()                                            // run once, when the sketch starts
{

   . . . . . .

    //-----  Startup the PSC (Power Stage Controllers, aka fancy PWMs)
    //        Do this BEFORE issuing a pinMode() command, as it will 'free up' the PSC outputs which were locked=0 at hardware reset.
  PLLCSR = _BV(PLLF) | _BV(PLLE);                       // Enable the PLL @ 64Mhz - as that is the clock source for the PSC.
  while (!(PLLCSR & (_BV(PLOCK))));                     // Wait until the PLL stabilizes.

  POC = _BV(POEN0A) | _BV(POEN0B) | _BV(POEN1A) | _BV(POEN1B);
                                                        // Configure PSC channel 0 and 1 as A & B outputs.

 . . . . .

}

void set_pwm_duty(void) {

  pwm = constrain(pwm, PWM_MIN, PWM_MAX);                               // check limits of PWM duty cycle

  PCNF = _BV(PULOCK) | _BV(PMODE) | _BV(POPA) |  _BV(POPB);             // Lock the PSC so we can update the configuration registers and implement all changes at one time.
                                                                        // Mode = Center, and both A and B outputs are 'active' high (High = FET-on).

  #define  PSC_CYCLE_CNT     (64000000UL/(2*PWM_FREQ) -1)               // Determine needed cycle count of PSC based on 64Mhz PLL clock and defined PWM frequency.
  #define  PSC_DEADBAND_CNT  ((64*PWM_DEADBAND)/1000)                   // Convert requested DB in nS to count

  POCR_RBH = highByte(PSC_CYCLE_CNT);                                   // Set PWM frequency - all channels get the same overall timing.
  POCR_RBL = lowByte (PSC_CYCLE_CNT);                                   // Always write out High Byte followed by Low Byte to 16 bit registers in the PCS engine.

        //----- Send out values to PSC controllers.
        //      Bank-A utilizes PCS controller #1, and is 'centered' around the PCS counter = 0.
        //      Bank-B utilizes PCS controller #0, and it is centered around PCD counter=MAX (319 in case of 100Khz)
        //         Take note that Bank-B has the High and Low side FET drivers backwards of what is typically expected.
        //         This is because bank-B is ran 180-degrees out of phase, and as such the driver outputs need to be reversed to get things
        //         to work like we want them to.

  POCR1SAH = highByte((PSC_CYCLE_CNT*pwm)/100 - PSC_DEADBAND_CNT/2);                            // Bank-A, High Side FET. 
  POCR1SAL =  lowByte((PSC_CYCLE_CNT*pwm)/100 - PSC_DEADBAND_CNT/2);
  POCR1SBH = highByte((PSC_CYCLE_CNT*pwm)/100 + (PSC_DEADBAND_CNT-(PSC_DEADBAND_CNT/2)));       // Bank-A, Low Side FET. 
  POCR1SBL =  lowByte((PSC_CYCLE_CNT*pwm)/100 + (PSC_DEADBAND_CNT-(PSC_DEADBAND_CNT/2)));       // Using 'remainder' math approch to prevent lose of one count with odd deadtime values.
  POCR0RAh = 0;                                                                                 // Set ADC 'syncronization' around peak point of on cycle
  POCR0RAl = 1;                                                                                 // (1 is 'min value' for POCnRA registers..)

  POCR0SBH = highByte((PSC_CYCLE_CNT*(100-pwm))/100 + PSC_DEADBAND_CNT/2);                      // Bank-B, High Side FET
  POCR0SBL =  lowByte((PSC_CYCLE_CNT*(100-pwm))/100 + PSC_DEADBAND_CNT/2);
  POCR0SAH = highByte((PSC_CYCLE_CNT*(100-pwm))/100 - (PSC_DEADBAND_CNT-(PSC_DEADBAND_CNT/2))); // Bank-B, Low Side FET
  POCR0SAL =  lowByte((PSC_CYCLE_CNT*(100-pwm))/100 - (PSC_DEADBAND_CNT-(PSC_DEADBAND_CNT/2)));
  POCR0RAh = highByte(PSC_CYCLE_CNT);                                                           // Set ADC 'syncronization' around peak point of on cycle
  POCR0RAl =  lowByte(PSC_CYCLE_CNT);

  PCNF = _BV(PMODE) | _BV(POPA) |  _BV(POPB);                                                   // Release the changes to the PSC, retaining mode and polarity as before.

}

Oh, I should perhaps add: My design was a 2-phase buck MPPT controller - 2-phase, which is why I was using 2x of the PSC channels. Sorry I do not have more, but hope this helps some. If by change you end up creating a more useful PSC lib - make sure to drop a link here!

mattmon commented 7 years ago

Yeah, hard to argue with AEC-Q100, built-in CAN and 64K flash for < $2.

Though, I guess the PSC does cost pins, especially when used with SPI.

Really appreciate your porting the core and your help, thanks again!