I've got two (hopefully) simple questions with regards to attiny412:
I need three PWMs and looking at megaTinyCore it seems like it's there. But it also looks like it's only PA1, 3 and 7 supporting hardware PWM. Is that true or is it possible to use PA1, 6 and 7? Hoping to keep all on the same side for easier PCB routing.
For UPDI in place programming: Assuming the device is running its code and peripherals are connected. Is it enough to have a connector for the UPDI port and GND (for shared ground)?
TCA0 can output on PA1, PA2, PA3 and PA7, but I remember vaguely, you have to set it in split mode then (I have to look that up again).
TCB0 is on PA6.
TCD0 is on PA6 and PA7, but a pain in the neck to configure.
I use the two wire connection (UPDI and GND) for programming all the time and it works fine.
Are you sure that screen shot refers to the attiny412 ?
My recent experience is that you have a wave out (WO3) on PA3 from timer TCA0 split mode and that is it. No other pin is directly used by a timer.
The bad news is that it has a bug and does not work for PA6
Based on what I saw in that bug I adopted the workaround sketch to provide PWM on pins on PA1, Pa6 and PA7
void setup() {
pwm_init();
pwm_start();
}
void loop() {
static uint8_t pwm_value = 0;
analogWrite(PIN_PA1, pwm_value);
analogWrite(PIN_PA7, pwm_value);
TCD0_CMPACLR = pwm_value;
TCD0_CTRLE |= TCD_SYNC_bm; // send synchronization command to load double buffer to TCD0
pwm_value++;
delay(10);
}
void pwm_init(void) {
// Enable-protected reg must have 0 written to enable bit before any other bits can be changed, and it defaults to
// enabled on startup
TCD0.CTRLA &= ~TCD_ENABLE_bm;
// Don't need overlapping PWM signals so just do oneramp
TCD0.CTRLB = TCD_WGMODE_ONERAMP_gc;
// Disable all input control
TCD0.INPUTCTRLA = TCD_INPUTMODE_NONE_gc;
// Set/clear values to create desired duty cycle. Don't care about CMPB (outputs on PA7) at all so leave it off
TCD0_CMPASET = 0x000;
TCD0_CMPACLR = 0x0FF;
// System 1MHz clock w/ DIV4 standard prescaler but no synchronization prescaler (I think they multiply together, not add).
// Must be done as last operation before starting timer with ENABLE bit
// NOTE :: DEFAULT TIMER FOR MILLIS/MICROS FUNCTIONS ON 1-SERIES ATTINIES IS TCD. I think this still works with it set as the default,
// but if not, try switching it to TCB0.
TCD0.CTRLA = TCD_CLKSEL_SYSCLK_gc | TCD_CNTPRES_DIV4_gc | TCD_SYNCPRES_DIV1_gc;
}
void pwm_start(void) {
// Turn off output override (because we want pwm to run it duh)
TCD0.CTRLC &= ~TCD_CMPOVR_bm;
// Enable WOA (PA6) but not and WOB/C/D (PA7/?/?). Since FAULTCTRL reg is write protected we need to make it editable in
// CCP reg. Must be done in start func because we can't take over output pins in stop func unless we disable them in FAULTCTRL.
CPU_CCP = CCP_IOREG_gc;
TCD0.FAULTCTRL = TCD_CMPAEN_bm;
while (!(TCD0.STATUS & TCD_ENRDY_bm)) {
;
}
TCD0.CTRLA |= TCD_ENABLE_bm;
}
And btw you need to keep millis enabled on it's default timer (TCD0).
I haven't got an ATtiny412 to test, unfortunately. I'll now have to research this more deeply because I've just written some code for someone who intends to use an ATtiny412 to replace some discrete components in a circuit design.
Anyway, thanks for your contribution to this.
Here you have a small demonstration that generates 4 PWM channels (WO0-WO3) out of TCA0 in split mode. WO4 and 5 are not broken out on the 8 pin parts.
void setup() {
// No need to enable split mode - core has already done that for us.
pinModeFast (PIN_PA1, OUTPUT);
pinModeFast (PIN_PA2, OUTPUT);
pinModeFast (PIN_PA3, OUTPUT);
pinModeFast (PIN_PA7, OUTPUT);
TCA0_SPLIT_CTRLB = TCA_SPLIT_HCMP0EN_bm | TCA_SPLIT_LCMP2EN_bm | TCA_SPLIT_LCMP1EN_bm | TCA_SPLIT_LCMP0EN_bm; // WO0 - WO3 on
TCA0_SPLIT_LPER = 255; // Count all the way down from 255 on WO0/WO1/WO2
TCA0_SPLIT_HPER = 170; // Count down from only 170 on WO3, giving higher frequency PWM with lower resolution
TCA0_SPLIT_CTRLA = TCA_SPLIT_CLKSEL_DIV64_gc | TCA_SPLIT_ENABLE_bm; // enable the timer with prescaler of 64
}
void loop() {
static uint8_t pwm_value = 0;
TCA0_SPLIT_LCMP0 = pwm_value; // Lowbyte timer dutycycle WO0 on PA7
TCA0_SPLIT_LCMP1 = pwm_value + 85; // Lowbyte timer dutycycle WO1 on PA1
TCA0_SPLIT_LCMP2 = pwm_value + 170; // Lowbyte timer dutycycle WO2 on PA2
TCA0_SPLIT_HCMP0 = pwm_value / 3 * 2 ; // Highbyte timer dutycycle WO3 on PA3
pwm_value--;
delay(10);
}
And for the OP, who was asking for PA6: This is possible via the CCL module of the Attiny412. PA6 happens to be the output pin of LUT0, so by not activating the output pin WO2 but instead making a lookup table and truth table in LUT0 you can have the WO2 output on pin PA6 instead.
void setup() {
// No need to enable split mode - core has already done that for us.
pinModeFast (PIN_PA1, OUTPUT);
pinModeFast (PIN_PA2, OUTPUT);
pinModeFast (PIN_PA3, OUTPUT);
pinModeFast (PIN_PA7, OUTPUT);
pinModeFast (PIN_PA6, OUTPUT);
TCA0_SPLIT_CTRLB = TCA_SPLIT_HCMP0EN_bm | TCA_SPLIT_LCMP1EN_bm | TCA_SPLIT_LCMP0EN_bm; // WO0, WO1 and WO3 on
// TCA0_SPLIT_CTRLB |= TCA_SPLIT_LCMP2EN_bm;// uncomment to also get WO2 output on PA2
TCA0_SPLIT_LPER = 255; // Count all the way down from 255 on WO0/WO1/WO2
TCA0_SPLIT_HPER = 170; // Count down from only 170 on WO3, giving higher frequency PWM with lower resolution
TCA0_SPLIT_CTRLA = TCA_SPLIT_CLKSEL_DIV64_gc | TCA_SPLIT_ENABLE_bm; // enable the timer with prescaler of 64
// Use CCL to (also) redirect the WO2 output to PA6
CCL_LUT0CTRLB = 0; // nothing needed for the first two LUT input channels
CCL_LUT0CTRLC = 0x08; // connect TCA0 WO2 input into the third LUT input channel
CCL_TRUTH0 = (1<<4); // TRUE when third LUT channel is TRUE ('b100')
CCL_LUT0CTRLA = CCL_OUTEN_bm | CCL_ENABLE_bm; // enable LUT0 output pin (PA6) and enable LUT0
CCL_CTRLA = CCL_ENABLE_bm;// enable the whole CCL peripheral
}
void loop() { // nothing to do here
static uint8_t pwm_value = 0;
TCA0_SPLIT_LCMP0 = pwm_value; // Lowbyte timer dutycycle WO0 on PA7
TCA0_SPLIT_LCMP1 = pwm_value + 85; // Lowbyte timer dutycycle WO1 on PA1
TCA0_SPLIT_LCMP2 = pwm_value + 170; // Lowbyte timer dutycycle WO2 on PA6 (and PA2)
TCA0_SPLIT_HCMP0 = pwm_value / 3 * 2 ; // Highbyte timer dutycycle WO3 on PA3
pwm_value--;
delay(10);
}
That is a very interesting trick to get around the limitations of the portmux. I've not seen many practical applications of the CCL peripheral before although there must be many more.
Sorry about the diversion caused by my use of the early ATtiny412 datasheet. I guess the port matrix is one the last things to get right and the compromises necessary cause a lot of discussion. Otherwise I can't see why a datasheet would have been released in that state.
Wow, thanks everyone! I've ordered a couple of them now and will be testing things using your code @hmeijdam. I've only been using the attiny85 before so it'll be funt testing UPDI and these newer chips.