[Solved]ATTiny 402 Setting up Dual PWM pins - Pin 2 (PA6) & Pin 5(PA1)

Go To Last Post
22 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Guys,

 

First off, I'm a beginner when it comes to AVR programming, so my apologies in advance. I've been trying to get dual PWM outputs on Pin 2 (PA6) & Pin 5(PA1) on the ATTiny 402 (looking to control 2 separate LEDs at the same time with different outputs), but I have had no luck on getting any output on Pin 5(PA1). I can get an output for Pin 2(PA6) & an output on Pin 7(PA3). However, I can't get any output on Pin 5(PA1). I want to keep Pin 7(PA3) for a crystal clock oscillator for the future. 

 

I'm not sure what I'm missing, but I believe based on the data sheet I should be able to get a PWM output on Pin 5(PA1). I've attached the code that works with activating Pin 2(PA6) for a PWM pin. I'm still working on the code & trying different things, so I apologies if it looks bare. Any help & guidance would be much appreciated.

 

 

Thanks,

Ankit 

 

#include <avr/io.h>
#define TCB_CMP_EXAMPLE_VALUE (0x40FF) //0x40 dutycycle  FF-Period
void CLOCK_init (void);
void PORT_init (void);
void TCB3_init (void);
void CLOCK_init (void)
{
 /* Enable writing to protected register */
 CPU_CCP = CCP_IOREG_gc;
 /* Enable Prescaler and set Prescaler Division to 64 */
 CLKCTRL.MCLKCTRLB = CLKCTRL_PDIV_64X_gc | CLKCTRL_PEN_bm;

 /* Enable writing to protected register */
 CPU_CCP = CCP_IOREG_gc;
 /* Select 32KHz Internal Ultra Low Power Oscillator (OSCULP32K) */
 CLKCTRL.MCLKCTRLA = CLKCTRL_CLKSEL_OSCULP32K_gc;

 /* Wait for system oscillator changing to finish */
 while (CLKCTRL.MCLKSTATUS & CLKCTRL_SOSC_bm)
 {
 ;
 }
}
void PORT_init (void)
{
 PORTA_DIR |= PIN6_bm;
 PORTA_OUT |= PIN6_bm;
}
void TCB3_init (void)
{
 /* Load CCMP register with the period and duty cycle of the PWM */
 TCB0.CCMP = TCB_CMP_EXAMPLE_VALUE;
 /* Enable TCB3 and Divide CLK_PER by 2 */
 TCB0.CTRLA |= TCB_ENABLE_bm;
 TCB0.CTRLA |= TCB_CLKSEL_CLKDIV2_gc;

 /* Enable Pin Output and configure TCB in 8-bit PWM mode */
 TCB0.CTRLB |= TCB_CCMPEN_bm;
 TCB0.CTRLB |= TCB_CNTMODE_PWM8_gc;
}
int main(void)
{
 CLOCK_init();
 PORT_init();
 TCB3_init();

 while (1)
 {
 ;
 }
}

 

This topic has a solution.
Last Edited: Tue. Sep 24, 2019 - 05:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The waveform output of TCB0 is only PA6.
And Pin 5 is PA2 and not PA1.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That makes sense, I wasn't sure if Pin 5 was PA2 or PA1. The data sheet I found on their site had conflicting data, so it had me confused (see attachments).

 

Thanks for the help. I'm going to setup Pin 5 now with the TCA0 waveform output, hopefully I can get the dual outputs working on both Pins 2 & 5 without any issues.

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I dont know what version of datasheets you are using for tiny402, however, recently it has been updated and some bugs/mistakes were corrected.

The tiny402 had some mistakes or missing information in the MUX table. make sure to always check the table instead of these colored MCU's diagrams.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1


Hi,

 

i would like to add one piece of information that i got crossed while looking for this problem, If I understood you correctly, even if the TCA0(split mode) works perfectly for the pin, but if you use the PORTMUX.CTRLC then this will shift the pin position for other waveforms. this is a recent published Errata in the tiny0 and 1 series:

 

 

I am sorry, but it looks there is no workaround for your problem. check this datatsheet for more info:

http://ww1.microchip.com/downloa...

 

Page 4.

 

Regards,

Moe

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Moe,

 

Sorry for the delay. I read through the data sheet you provided. That is unfortunate that there isn't a work around. I'm wondering though since I'm using TCB0 & TCA0 for generating different waveforms on two separate pins [Pin 2 (PA6) & 5 (PA2)] vs. TCA0 only if this would work? I attached the skeleton code I'm using to verify if this would work. Unfortunately, I'm not getting an output on Pin 5 just quite yet. Before I keep tweaking this code, I would like to figure out if there is anything I can do with the ATTiny 402 to get two separate waveforms or if it's a lost cause.

 

Any help is much appreciated, & thanks in advance.

 

 

Regards,

Ankit

#define PERIOD_EXAMPLE_VALUE (0x008A)
#define DUTY_CYCLE_EXAMPLE_VALUE (0x0044)

#define TCB_CMP_EXAMPLE_VALUE (0x40FF)  // Period and dutycycle for pwm in pin2

#include <avr/io.h>
/*Using default clock 3.33MHz */

void TCA0_init(void);
void PORT5_init(void);
unsigned int sample;

void CLOCK_init (void);
void PORT2_init (void);
void TCB3_init (void);
void delay(int);

void CLOCK_init (void)
{
 /* Enable writing to protected register */
 CPU_CCP = CCP_IOREG_gc;
 /* Enable Prescaler and set Prescaler Division to 64 */
 CLKCTRL.MCLKCTRLB = CLKCTRL_PDIV_64X_gc | CLKCTRL_PEN_bm;

 /* Enable writing to protected register */
 CPU_CCP = CCP_IOREG_gc;
 /* Select 32KHz Internal Ultra Low Power Oscillator (OSCULP32K) */
 CLKCTRL.MCLKCTRLA = CLKCTRL_CLKSEL_OSCULP32K_gc;

 /* Wait for system oscillator changing to finish */
 while (CLKCTRL.MCLKSTATUS & CLKCTRL_SOSC_bm)
 {
 ;
 }
}
void PORT2_init (void)
{
 PORTA_DIR |= PIN6_bm;
 PORTA_OUT |= PIN6_bm;
}
void TCB3_init (void)  
{
 /* Load CCMP register with the period and duty cycle of the PWM */
 TCB0.CCMP = TCB_CMP_EXAMPLE_VALUE;
 /* Enable TCB3 and Divide CLK_PER by 2 */
 TCB0.CTRLA |= TCB_ENABLE_bm;
 TCB0.CTRLA |= TCB_CLKSEL_CLKDIV2_gc;

 /* Enable Pin Output and configure TCB in 8-bit PWM mode */
 TCB0.CTRLB |= TCB_CCMPEN_bm;
 TCB0.CTRLB |= TCB_CNTMODE_PWM8_gc;
}

void delay(int time)
{
    int i,j;
    for(i=0;i<time;i++)
        for(j=0;j<1275;j++);
}


void TCA0_init(void)
{
 
/* set waveform output on PORT A */
 PORTMUX.CTRLC |= (PORTMUX_TCA02_bm);
 TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP2EN_bm | TCA_SINGLE_WGMODE_DSBOTH_gc; /* set dual-slope PWM
mode */

 /* disable event counting */
 TCA0.SINGLE.EVCTRL &= ~(TCA_SINGLE_CNTEI_bm);

 /* set PWM frequency and duty cycle (50%) */
 TCA0.SINGLE.PERBUF = PERIOD_EXAMPLE_VALUE;
 TCA0.SINGLE.CMP0BUF = DUTY_CYCLE_EXAMPLE_VALUE;


 TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV4_gc /* set clock source
(sys_clk/4) */
 | TCA_SINGLE_ENABLE_bm; /* start timer */
}
void PORT5_init(void)
{
 /* set pin 5 of PORT A as output */
	
 PORTA_DIR |= PIN2_bm;
 PORTA_OUT |= PIN2_bm;
}

int main(void)
{
 CLOCK_init();
 PORT2_init();
 TCB3_init();

 TCA0_init();
 PORT5_init();
 /* Replace with your application code */
 PORTA.DIR=0X01;
 unsigned int sample;

    
         // adc //
         PORTA.PIN7CTRL=0x04;// input disabled to read continous data.
         ADC0.CTRLA=0x04; // RESOLUTION IS SELECTED AS 8BIT.
         ADC0.CTRLC=0x03; // internal reference and pre-scalar clk/16.
         ADC0.MUXPOS=0x07; // ADC input in pin 7
         ADC0.CTRLA=0x05; //adc enabled

 
 while (1)
 {
   ADC0.COMMAND=0X01;
   while((ADC0.INTFLAGS&0x01)==0);
   sample=ADC0.RES;
 }
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Does TCA0 work with the following code?

 

    // TCA0 setup (WO2 only)
    PORTA.OUTCLR = PORTA.DIRSET = PIN2_bm;  // WO2(PA2:PIN5) OUTPUT
    TCA0.SINGLE.PER = PERIOD_EXAMPLE_VALUE;
    TCA0.SINGLE.CMP2 = DUTY_CYCLE_EXAMPLE_VALUE;
    TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP2EN_bm | TCA_SINGLE_WGMODE_SINGLESLOPE_gc;
    TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV4_gc | TCA_SINGLE_ENABLE_bm;

Use CMP2BUF to change the duty later.

 

If pin assignments can be changed, TCA alone can generate two PWM signals.

 

    // TCA0 setup (WO1 & WO2)
    PORTA.OUTCLR = PORTA.DIRSET = PIN1_bm | PIN2_bm;    // WO1(PA1:PIN4) & WO2(PA2:PIN5) OUTPUT
    TCA0.SINGLE.PER = PERIOD_EXAMPLE_VALUE;
    TCA0.SINGLE.CMP1 = DUTY_CYCLE_EXAMPLE_VALUE;    // WO1 Duty
    TCA0.SINGLE.CMP2 = DUTY_CYCLE_EXAMPLE_VALUE;    // WO2 Duty
    TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP1EN_bm | TCA_SINGLE_CMP2EN_bm | TCA_SINGLE_WGMODE_SINGLESLOPE_gc;
    TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV4_gc | TCA_SINGLE_ENABLE_bm;

 

But unfortunately I can not check the operation because I do not have tiny402.
I checked the operation with the tiny1616.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Kasaban,

 

Sorry for the extremely delayed message, I didn't get a chance to test this until recently. It was a really busy summer & I'm so new to hardware programming, so I'm trying to learn as much as I can...it's like drinking water from a fire hose. I relooked at the code I posted originally, what a mess. 

 

I tried your code with some edits, & it does indeed produce two outputs on PA1 & PA2! The only question I have is, is it possible to have two different frequencies? I don't think it will since it's in single slope, but I figured it doesn't hurt to ask.

 

If I also want to use an external crystal oscillator, could I use it for the ATtiny 402? I'm using PK 4 for programming, & when programming the device I only see two options for the internal oscillator (16 MHz & 20 MHz). The data sheet does say an external clock can be used, but I don't see that option anywhere when configuring the Fuse.

 

I attached the code you provided with the setup just in case anyone had a similar question in the future.

 

 

Cheers,

Ankit

#define F_CPU 16000000UL
#include <avr/io.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <time.h>
#include <util/delay.h>

int main(void)
{
  	  /* Set pins as i/o */
  	  //  PORTA.DIRCLR |= PIN1_bm; // input. not used
  	     PORTA.OUTCLR = PORTA.DIRSET = PIN1_bm | PIN2_bm; // output.
  	  //  PORTA.DIRCLR |= PIN3_bm; // input. not used
  	  //  PORTA.DIRSET |= PIN6_bm; // output.not used
  	  //  PORTA.DIRCLR |= PIN7_bm; // input. not used
		
		
		TCA0.SINGLE.CTRLA  = TCA_SINGLE_CLKSEL_DIV4_gc | TCA_SINGLE_ENABLE_bm;
		TCA0.SINGLE.CTRLB  = TCA_SINGLE_CMP1EN_bm | TCA_SINGLE_CMP2EN_bm | TCA_SINGLE_WGMODE_SINGLESLOPE_gc;
		TCA0.SINGLE.PER    = 0x061C; // frequency = 85 Hz, but we are looking for 505 Hz
		TCA0.SINGLE.CMP2   = 0x0400; // duty cycle= 51.74%
        TCA0.SINGLE.CMP1   = 0x0400;
		
    while (1) 
    {
		
		TCA0.SINGLE.CTRLC |= 0x04;	
		PORTA.DIRSET      |= PIN1_bm;   // output.
		PORTA.DIRSET      |= PIN2_bm;   // output.
    }
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ankit.Patel wrote:

The only question I have is, is it possible to have two different frequencies?

 

Is it really "two different duty" instead of "two different frequencies"?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It would be both that are needed. Two different duty and two different frequencies. I’m trying to control two separate inductive loads with different duty cycles and frequencies

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I knew that request for the first time.
Since there is only one TCA cycle, it is impossible to output different frequencies.
There seems to be no choice but to use TCB's 8-bit PWM together.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

The following is a sample of 85Hz PWM output by TCA0 in 16MHz operation.

 

#define F_CPU 16000000UL
#include <avr/io.h>

FUSES = {
    .WDTCFG     = FUSE_WDTCFG_DEFAULT,
    .BODCFG     = FUSE_BODCFG_DEFAULT,
    .OSCCFG     = FREQSEL_16MHZ_gc,     //Select 16MHz OSC
    .SYSCFG0    = FUSE_SYSCFG0_DEFAULT,
    .SYSCFG1    = FUSE_SYSCFG1_DEFAULT,
    .APPEND     = FUSE_APPEND_DEFAULT,
    .BOOTEND    =FUSE_BOOTEND_DEFAULT,
};

int main(void)
{
    // Set Clock Div1
    _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, CLKCTRL_PDIV_2X_gc | !CLKCTRL_PEN_bm);
    _PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, !CLKCTRL_CLKOUT_bm | CLKCTRL_CLKSEL_OSC20M_gc);

    /* Set pins as i/o */
    PORTA.OUTCLR = PORTA.DIRSET = PIN1_bm; // output.

    // Set TCA0
    TCA0.SINGLE.PER    = 47058; // frequency = 85 Hz
    TCA0.SINGLE.CMP1   = 4706;  // duty cycle = 10%
    TCA0.SINGLE.CTRLB  = TCA_SINGLE_CMP1EN_bm | TCA_SINGLE_WGMODE_SINGLESLOPE_gc;
    TCA0.SINGLE.CTRLA  = TCA_SINGLE_CLKSEL_DIV4_gc | TCA_SINGLE_ENABLE_bm;  // Stop until the setting is complete.
    
    while (1)
    {
    }
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That code you sent worked beautifully. I tested 4 boards, & all of them rounded up to an output of 85 Hz on the scope. 

 

I apologize also for not being clear on the two different frequency pins. I can at least now get one pin working, just need to figure out the best way to get the second PWM (changing frequency and duty)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PWM by TCB has a resolution of only 8 bits and has few options for clock prescaler, so it is significantly less flexible than TCA.
How much frequency range and duty resolution do you need?
tiny412 has an additional timer, TCD.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would need frequency range between 400 Hz to 525 Hz, & a duty cycle range of 15% to 55% (I think between 6 & 8 bit should be fine). 

Last Edited: Fri. Sep 20, 2019 - 01:52 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Such a low frequency PWM is not possible with a 16MHz TCB.
With TCD, a prescaler with a division of 16 can be set, so a waveform with sufficient resolution is likely to be obtained.

Does the cost difference between tiny402 and tiny412 matter to you?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cost difference shouldn’t be an issue, it’s pretty similar (maybe $.05 difference at most)

I checked the data sheet on the 412, seems to be very similar to the 402 so it should fit the current board I have very nicely.

I’ll order some 412’s tomorrow to test out. Any watch outs when setting up TCD? I’m hoping it’s not too difficult to setup lol. Seems like I can utilize PA6 based on the data sheet.

Thanks again,
Ankit

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

TCD is designed for asynchronous operation, so it is somewhat difficult to set synchronous operation.

 

A sample program is shown. Try it once you have the chip.

(I added the frequency and duty to make it easier to change)

 

// for tiny412
//#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/atomic.h>

#define TCA_TOP_85HZ    ((uint16_t)((16000000.0f / 4 / 85) + 0.5) - 1)
#define TCD_TOP_400HZ   ((uint16_t)((16000000.0f / 16 / 400) + 0.5) - 1)

static inline void tca_pwm_update(uint16_t top, uint16_t cmp){
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
        TCA0.SINGLE.CMP1 = cmp;
        TCA0.SINGLE.PER = top;
    }
}

static inline void tcd_pwm_update(uint16_t top, uint16_t cmp){
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
        TCD0.CMPASET = cmp;
        TCD0.CMPACLR = top;
        TCD0.CMPBSET = 0;
        TCD0.CMPBCLR = top; // <--- Not omissible
    }
}

FUSES = {
    .WDTCFG     = FUSE_WDTCFG_DEFAULT,
    .BODCFG     = FUSE_BODCFG_DEFAULT,
    .OSCCFG     = FREQSEL_16MHZ_gc,     //Select 16MHz OSC
    .TCD0CFG    = FUSE_TCD0CFG_DEFAULT,
    .SYSCFG0    = FUSE_SYSCFG0_DEFAULT,
    .SYSCFG1    = FUSE_SYSCFG1_DEFAULT,
    .APPEND     = FUSE_APPEND_DEFAULT,
    .BOOTEND    = FUSE_BOOTEND_DEFAULT,
};

int main(void)
{
    // Set Clock Div1
    _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, CLKCTRL_PDIV_2X_gc | !CLKCTRL_PEN_bm);
    _PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, !CLKCTRL_CLKOUT_bm | CLKCTRL_CLKSEL_OSC20M_gc);

    /* Set pins as i/o */
    PORTA.OUTCLR = PORTA.DIRSET = PIN1_bm | PIN6_bm; // output.

    // Set TCA0(PA1)
    tca_pwm_update(TCA_TOP_85HZ, (TCA_TOP_85HZ / 10));  // frequency = 85Hz, duty = 10%
    TCA0.SINGLE.CTRLB  = TCA_SINGLE_CMP1EN_bm | TCA_SINGLE_WGMODE_SINGLESLOPE_gc;
    TCA0.SINGLE.CTRLA  = TCA_SINGLE_CLKSEL_DIV4_gc | TCA_SINGLE_ENABLE_bm;  // Stop until the setting is complete.

    // Set TCD0(PA6)
    tcd_pwm_update(TCD_TOP_400HZ, (TCD_TOP_400HZ / 4)); // frequency = 400Hz, duty = 25%
    _PROTECTED_WRITE (TCD0.FAULTCTRL, TCD_CMPAEN_bm);
    TCD0.CTRLC = TCD_AUPDATE_bm;    // <--- Not omissible
    TCD0.CTRLB = TCD_WGMODE_ONERAMP_gc;
    while (!(TCD0.STATUS & TCD_ENRDY_bm));
    TCD0.CTRLA = TCD_CLKSEL_SYSCLK_gc | TCD_CNTPRES_DIV4_gc | TCD_SYNCPRES_DIV4_gc | TCD_ENABLE_bm;

    while (1)
    {
    }
}

 

This is the waveform operated by tiny1616.

Note that the polarity is reversed due to the timer specifications.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is awesome, thanks! Polarity shouldn't be a big problem, I can work around that. Once I get the 412 in, I'll test it out. I built the project in MPLAB, & all that is needed now is the hardware.

 

One quick question, I know when using TCA I can change the reference from the internal clock to the external clock using EXTCLK vs. OSC20M. Will there be any issues if TCD uses an external crystal oscillator (16 MHz Crystal Oscillator)? I don't think it should, but I've been wrong plenty of times before.

 

 

Cheers,

Ankit

Last Edited: Fri. Sep 20, 2019 - 02:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, TCD can set the clock source to external.
I have done it, so if you are in trouble, I may be able to help.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just got the 412, & tested it and it works perfectly! No issues setting up the external clock & everything is working as intended now. Thank you so much, this really helped me out tremendously, & saves me a huge headache.

 

 

Cheers,

Ankit

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Glad it worked out.

cheers!