PWM on ATTiny841: Paging Dr. Azzy!

Hi all, I have a project where I am using timers 1 and 2 of an ATTiny841. I am wanting to configure all of them to be 10-bit, fast PWM with no prescaler. I am using Dr. Azzy's ATTiny Core, running the chip at 8 MHz. I have read out the fuses to verify. I have also spent a ton of time in the data sheet trying to figure out what is going on. My code is posted below, but first, the issues I see. TOCC3 and TOCC4 are showing the same pulse train of 28.5 us on, 95 us off, roughly a 20% duty cycle. TOCC2 shows a pulse train of 79 ms on, 79 ms off. TOCC1 shows a static +5V (100% duty cycle). This really has me scratching my head, but it must be something stupid that I just must have glossed over in the data sheet or something. Any help would be greatly appreciated. Also, I have been trying some different ways of representing the data, so you will see binary, bit-shifting, etc. in the code. I know it's inconsistent, but the compiler doesn't mind right now. I'll fix it when I get this actually working. Thanks in advance!

  //Set up the timer outputs to the correct pins. Don't touch timer 0 so that millis() works correctly
  //Pin 8 -> PWM2 -> TOCC4 (make timer OC2B)
  //Pin 9 -> PWM1 -> TOCC3 (make timer OC2A)
  //Pin 10 -> PWM3 -> TOCC2 (make timer OC1B)
  //Pin 11 -> PWM4 -> TOCC1 (make timer OC1A)
  TOCPMSA0 = (1<<TOCC1S0) | (1<<TOCC2S0) | (1<<TOCC3S1); //This sets TOCC1 throught TOCC3
  TOCPMSA1 = (1<<TOCC4S1); //This sets TOCC4 to be OC2B
  TOCPMCOE = (1<<TOCC1OE) | (1<<TOCC2OE) | (1<<TOCC3OE) | (1<<TOCC4OE); //Enable the time/output compare on TOCC1-4

  //Disable interrupts on timers
  TIMSK1 = 0;
  TIMSK2 = 0;

  //Set up 16 bit timers so that we can use PWM
  //PWM is 10-bit fast, 0x03FF TOP, no prescaler
  //Set to Fast, 10-bit PWM, max value of 1024
  TCCR1A = 0b10100011; //TCCR1A = _BV(COM1A1) | _BV(WGM10) | _BV(WGM11); 
  TCCR1B = 0b00001001; //No prescaler,  | _BV(WGM12)
  TCCR1C = 0b00000000; //Set to zero when in PWM mode
  
  TCCR2A = 0b10100011; //TCCR2A = _BV(COM2A1) | _BV(WGM20) | _BV(WGM21); //Set to Fast, 10-bit PWM, max value of 1024
  TCCR2B = 0b00001001; //No prescaler,  | _BV(WGM22)
  TCCR2C = 0b00000000; //Set to zero when in PWM mode

  ICR1 = 20000 - 1;
  ICR2 = 20000 - 1;
  
  //Set up an ISR for the encoder switch pin so that it reacts instantly
  //and to reduce loop execution time with determining multiplier
  GIMSK = 0b00110000; //enable pin change interrupts for all pins
  PCMSK0 = 0b00000011; //enable PCINT0/Pin13 and PCINT1/Pin12 as pin change interruptible for encoder pins
  PCMSK1 = 0b00000011;

Shhh ... I think Dr. Azzy's working on finishing the core for the AVR-DA series... :zipper_mouth_face:
Not an expert, but when working with registers I normally use |= to set desired bits high and leave all other bits undisturbed.
To clear desired bits and leave others undisturbed, I use &= ~.
In your code, you are always clearing all remaining bits.
Are you using the latest version of the core?

You have not posted all of your code; for example, you do not include the part where you configure the pins as outputs (hopefully not by using the pin numbers you posted, as they do not match either pin mapping) nor have you posted where you set the OCRnx registers so that I could interpret the reported output.

What pin numbers are you using when you refer to pins? Are those... physical pin numbers, rather than Arduino pin numbers? We generally do not refer to pins that way when talking of Arduino sketches - either use the Arduino pin numbers, or (with ATTinyCore 1.4.0 or later), by their port and bit via PIN_Pxn (eg, PIN_PA2, PIN_PA3, etc). I recommend the PIN_Pxn notation, as that isolates you from the confusion that comes with choosing the right pin mapping.

Your code and comments don't match regarding the COMx bits. Code looks right though?

But yeah, i can't assess since you haven't posted full code, and I don't know what duty cycle you're trying for.

If that's internal oscillator, though, the ones on Timer1 look correct though... (sum of those times, multiplied by 8 gives 988, but at 5v the internal oscillator is running around 4% fast, so that's what you'd expect to see from 10 bit unprescaled PWM.

I suspect that something elsewhere in your code is altering the configuration of Timer2.

Your setting of ICR1 and ICR2 also makes me suspicious (of your doing something elsewhere in the code that's messing that up), as they do nothing in that configuration, as I suspect you know...

TOCPMCOE = (1<<TOCC1OE) | (1<<TOCC2OE) | (1<<TOCC3OE) | (1<<TOCC4OE);

dlloyd:
Not an expert, but when working with registers I normally use |= to set desired bits high and leave all other bits undisturbed.
To clear desired bits and leave others undisturbed, I use &= ~.
In your code, you are always clearing all remaining bits.
Are you using the latest version of the core?

Setting registers where you know the desired configuration of all bits with = is appropriate.
Setting them with |= when they were preconfigured by the core is not. He wants to set all bits and blow away whatever was there before.

I apologize for the incomplete code. I have a bunch of other variables for other tasks that are not PWM related, but please see below for a more complete picture of how I am configuring/assigning things that are PWM related. After the setup portion, the only thing I adjust is the duty cycle of each PWM pin by using analogWrite(PWMpin,dutyCycle). If there is anything else you need to know, please let me know. I included physical pin numbers to help with making sure my schematic in Eagle is connected correctly. I actually referenced Dr. Azzy's awesome pinout for the pin assignments. Here we are:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/atomic.h>

// Assign pins of ATTiny841, make sure it runs at 8 MHz internal oscillator

//DigitalPin Assignments
const unsigned int encB = 10;
const unsigned int encA = 9;
const unsigned int pwm1 = 6;
const unsigned int pwm2 = 5;
const unsigned int pwm3 = 7;
const unsigned int pwm4 = 8;
const unsigned int tapSwitch = 2;
const unsigned int divSwitch1 = 0; //This will be an analog read
const unsigned int divSwitch2 = 1; //This will be an analog read
const unsigned int ledOut = 3;
const unsigned int testLED = 4;

//Tons of global parameter definitions removed for clarity. None do anything with pin assignments, etc.

void setup() {
  //Define what each pin is
  pinMode(encA, INPUT);
  pinMode(encB, INPUT);
  pinMode(divSwitch1, INPUT);
  pinMode(divSwitch2, INPUT);
  pinMode(tapSwitch, INPUT);
  pinMode(pwm1, OUTPUT);
  pinMode(pwm2, OUTPUT);
  pinMode(pwm3, OUTPUT);
  pinMode(pwm4, OUTPUT);
  pinMode(ledOut, OUTPUT);
  pinMode(testLED, OUTPUT);

  //Set up the initial state of the pins
  digitalWrite(encA, HIGH);
  digitalWrite(encB, HIGH);
  digitalWrite(divSwitch1, LOW);
  digitalWrite(divSwitch2, LOW);
  digitalWrite(tapSwitch, LOW);
  digitalWrite(ledOut, LOW);
  digitalWrite(testLED, LOW);


  //Unrelated assignments edited for clarity

  

  //Set up the timer outputs to the correct pins. Don't touch timer 0 so that millis() works correctly
  //Pin 8 -> PWM2 -> TOCC4 (make timer OC2B)
  //Pin 9 -> PWM1 -> TOCC3 (make timer OC2A)
  //Pin 10 -> PWM3 -> TOCC2 (make timer OC1B)
  //Pin 11 -> PWM4 -> TOCC1 (make timer OC1A)
  TOCPMSA0 = (1<<TOCC1S0) | (1<<TOCC2S0) | (1<<TOCC3S1); //This sets TOCC1 throught TOCC3
  TOCPMSA1 = (1<<TOCC4S1); //This sets TOCC4 to be OC2B
  TOCPMCOE = (1<<TOCC1OE) | (1<<TOCC2OE) | (1<<TOCC3OE) | (1<<TOCC4OE); //Enable the time/output compare on TOCC1-4

  //Disable interrupts on timers
  TIMSK1 = 0;
  TIMSK2 = 0;

  //Set up 16 bit timers so that we can use PWM
  //PWM is 10-bit fast, 0x03FF TOP, no prescaler
  //Set to Fast, 10-bit PWM, max value of 1024
  TCCR1A = 0b10100011; //TCCR1A = _BV(COM1A1) | _BV(WGM10) | _BV(WGM11); 
  TCCR1B = 0b00001001; //No prescaler,  | _BV(WGM12)
  TCCR1C = 0b00000000; //Set to zero when in PWM mode
  
  TCCR2A = 0b10100011; //TCCR2A = _BV(COM2A1) | _BV(WGM20) | _BV(WGM21); //Set to Fast, 10-bit PWM, max value of 1024
  TCCR2B = 0b00001001; //No prescaler,  | _BV(WGM22)
  TCCR2C = 0b00000000; //Set to zero when in PWM mode

  ICR1 = 20000 - 1;
  ICR2 = 20000 - 1;

  analogWrite(pwm1, 200);
  analogWrite(pwm2, 400);
  analogWrite(pwm3, 600);
  analogWrite(pwm4, 800);

ripthorn:
the only thing I adjust is the duty cycle of each PWM pin by using analogWrite(PWMpin,dutyCycle).

Well there's your problem!
Don't use analogWrite(). analogWrite() assumes the default mapping, and calls digitalWrite() when called with numbers outside the range of normal PWM values. As soon as you reconfigure the underlying peripheral, you should generally assume that all the Arduino wrappers around that will no longer work until proven otherwise...
I don't even know off-hand how the "turn off pwm" functionality is implemented - the three parts on the other core have code that is utterly impenetrable (that's why I'm going to be moving them to the other core - even I can't maintain that mess. The code I started from was worked over by some dude who was too clever for his (or her) own good and had an unhealthy obsession with macros and metaprogramming - I think his goal was to have macros that write all the part-specific code. Classic case of someone who had recently learned how to use macros, but not when to use them - or in this case, when NOT to)

Set the duty cycle by assigning to OCR1A/OCR1B/OCR2A/OCR2B. IIRC, you need to unset the bit in TOCPMCOE or the COMxny bit(s); the TOCPMOE is certainly the right choice between those two. You could even throw together a simple function for it, like

pinToTOCPMCOE(uint8_t pin){
if(pin==pwm1) {
return 1<<1;
} else if (pin==pwm2){
return 1<<2;
//and so on
}
writeDutyCycle(uint8_t pin, uint16_t duty) {
//optionally catch pin not one of the PWM pins - but if you're writing the code, you're probably confident you aren't calling it with an invalid pin.
//If I were doing this, would instead pass a number 1~4 to it, so the TOCPMCOE writes would be a simple 1<<pwmchan instead of a helper function or if/elseif...
if (duty==0) {
digitalWrite(pin,0);
TOCPMCOE&=~pinToTOCPMCOE(pin);
} else if (duty>1023){
digitalWrite(pin,1);
TOCPMCOE&=~pinToTOCPMCOE(pin);
} else {
if(pin==pwm1) {
OCR1B=duty;
} else if (pin==pwm2) {
//you get the idea
}
TOCPMCOE|=pinToTOCPMOE(pin);
}
}

Oh, and setting ICRn is doing nothing. You're suffering from copy-pastitis; that was from code you found somewhere that was using WGM 8, 10, or 14 (phase & freq. correct, phase correct, or fast PWM respectively) where TOP=ICRn, where they wanted Fpwm=CLK(Tn)/20000 (w/no prescale 8 MHz clock, 8000000/20000 = 400Hz) where they'd feed duty cycle as uint16_t between 0 and 19999), but you're using WGM 7, 10-bit fast PWM, so TOP=1023, and ICRn is ignored unless you reconfigure the timer to either use it as top or do input capture). Of course, you could also use WGM 14 yourself, and use IRCn = 1023 to get 10-bit, or adjust it to change the frequency (being sure to also adjust the duty cycles you passed it, of course)

Thanks for the fantastic help! I do have one question, though. Dr. Azzy said "IIRC, you need to unset the bit in TOCPMCOE". Does this mean only when I have a 0 duty cycle, or does it mean I have to unset the bit, change duty cycle, and then reset the bit? I'll try it both ways and see what happens, I just want to make sure I'm clear. Thanks again!

Only needs to be unset when turning off PWM with 0% duty cycle (IIRC when the compare value is set to 0, you still get a tiny spike at every overflow; when compare value > TOP, the same is not true - it just stays HIGH, so it doesn't even matter... That's if I remember how the classic AVRs work correctly - I think they work the same though).

But, you also need to set it any time you set a non-0% duty cycle, since you don't know what the current duty cycle is.

Oh - one important note: that implementation is not interrupt safe, so if you ever call from an interrupt, you need make that safe (it's the read-modify-write of that register that's the issue)

So I am seeing some strange behavior. Below is my entire code right now, just to get the PWM stuff sorted. I see that TOCC4 is working correctly. I can change the duty cycle number and it works fine. However, the output from TOCC1, TOCC2, and TOCC3 show all high with now PWM action. This is strange because both TOCC3 and 4 are on timer 2, so timer2 is clearly working. The only thing I can see that separates TOCC4 from the others is that TOCC4 is in TOCPMSA1 while the others are in TOCPMSA0. However, I am setting both using the same method. Any idea what is going on here? I'm really scratching my head over this.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/atomic.h>

// Assign pins of ATTiny841, make sure it runs at 8 MHz internal oscillator

//DigitalPin Assignments
const unsigned int pwm1 = 6;
const unsigned int pwm2 = 5;
const unsigned int pwm3 = 7;
const unsigned int pwm4 = 8;

void setup() {
  //Define what each pin is
  pinMode(pwm1, OUTPUT);
  pinMode(pwm2, OUTPUT);
  pinMode(pwm3, OUTPUT);
  pinMode(pwm4, OUTPUT);

  //Set up the timer outputs to the correct pins. Don't touch timer 0 so that millis() works correctly
  //Pin 8 -> PWM2 -> TOCC4 (make timer OC2B)
  //Pin 9 -> PWM1 -> TOCC3 (make timer OC2A)
  //Pin 10 -> PWM3 -> TOCC2 (make timer OC1B)
  //Pin 11 -> PWM4 -> TOCC1 (make timer OC1A)
  TOCPMSA0 = (1<<TOCC1S0) | (1<<TOCC2S0) | (1<<TOCC3S1); //This sets TOCC1 throught TOCC3
  TOCPMSA1 = (1<<TOCC4S1); //This sets TOCC4 to be OC2B
  TOCPMCOE = (1<<TOCC1OE) | (1<<TOCC2OE) | (1<<TOCC3OE) | (1<<TOCC4OE); //Enable the time/output compare on TOCC1-4

  //Disable interrupts on timers
  TIMSK1 = 0;
  TIMSK2 = 0;

  //Set up 16 bit timers so that we can use PWM
  //PWM is 10-bit fast, 0x03FF TOP, no prescaler
  //Set to Fast, 10-bit PWM, max value of 1024
  TCCR1A = 0b10100011; //TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM10) | _BV(WGM11); 
  TCCR1B = 0b00001001; //No prescaler,  | _BV(WGM12)
  TCCR1C = 0b00000000; //Set to zero when in PWM mode
  
  TCCR2A = 0b10100011; //TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20) | _BV(WGM21); //Set to Fast, 10-bit PWM, max value of 1024
  TCCR2B = 0b00001001; //No prescaler,  | _BV(WGM22)
  TCCR2C = 0b00000000; //Set to zero when in PWM mode

  int dutyCycle1 = 200;
  int dutyCycle2 = 400;
  int dutyCycle3 = 600;
  int dutyCycle4 = 800;

  writeDutyCycle(3,dutyCycle1); //PWM1 is on TOCC3
  writeDutyCycle(4,dutyCycle2); //PWM2 is on TOCC4
  writeDutyCycle(2,dutyCycle3); //PWM3 is on TOCC2
  writeDutyCycle(1,dutyCycle4); //PWM4 is on TOCC1
}


void loop() {
}


void writeDutyCycle(uint8_t tocc, uint16_t duty) {

  // Set duty cycle based on the TOCC pin being specified
  if(tocc == 1) {
    OCR1A = duty;
  } 
  else if (tocc == 2) {
    OCR1B = duty;
  }
  else if (tocc == 3) {
    OCR2A = duty;
  }
  else if (tocc == 4) {
    OCR2B = duty;
  }
  TOCPMCOE |= 1<<tocc;
  
}

Shouldn't these be uint16_t?

int dutyCycle1 = 200;
  int dutyCycle2 = 400;
  int dutyCycle3 = 600;
  int dutyCycle4 = 800;

EDIT: Whoops ... deleted comment (I miscounted bits).

What I usually do is have a small init function or section in the setup, then just use |= and &= ~ to manipulate the bits. Using the bit shift macros is less error prone and easier to troubleshoot.

You make a good point about using the bit shift macros. I did that and also made the duty cycle numbers uint16_t. Revised code is below. I am seeing the exact same behavior as before. Not sure what the issue is...?

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/atomic.h>

// Assign pins of ATTiny841, make sure it runs at 8 MHz internal oscillator

//DigitalPin Assignments
const unsigned int pwm1 = 6;
const unsigned int pwm2 = 5;
const unsigned int pwm3 = 7;
const unsigned int pwm4 = 8;

void setup() {
  //Define what each pin is
  pinMode(pwm1, OUTPUT);
  pinMode(pwm2, OUTPUT);
  pinMode(pwm3, OUTPUT);
  pinMode(pwm4, OUTPUT);

  //Set up the timer outputs to the correct pins. Don't touch timer 0 so that millis() works correctly
  //Pin 8 -> PWM2 -> TOCC4 (make timer OC2B)
  //Pin 9 -> PWM1 -> TOCC3 (make timer OC2A)
  //Pin 10 -> PWM3 -> TOCC2 (make timer OC1B)
  //Pin 11 -> PWM4 -> TOCC1 (make timer OC1A)
  TOCPMSA0 = (1<<TOCC1S0) | (1<<TOCC2S0) | (1<<TOCC3S1); //This sets TOCC1 throught TOCC3
  TOCPMSA1 = (1<<TOCC4S1); //This sets TOCC4 to be OC2B
  TOCPMCOE = (1<<TOCC1OE) | (1<<TOCC2OE) | (1<<TOCC3OE) | (1<<TOCC4OE); //Enable the time/output compare on TOCC1-4

  //Disable interrupts on timers
  TIMSK1 = 0;
  TIMSK2 = 0;

  //Set up 16 bit timers so that we can use PWM
  //PWM is 10-bit fast, 0x03FF TOP, no prescaler
  //Set to Fast, 10-bit PWM, max value of 1024
  TCCR1A = (1<<COM1A1) | (1<<COM1B1) | (1<<WGM11) | (1<<WGM10); //TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM10) | _BV(WGM11); 
  TCCR1B = (1<<WGM12) | (1<<CS10); //No prescaler,  | _BV(WGM12)
  TCCR1C = 0b00000000; //Set to zero when in PWM mode
  
  TCCR2A = (1<<COM2A1) | (1<<COM2B1) | (1<<WGM21) | (1<<WGM20); //TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20) | _BV(WGM21); //Set to Fast, 10-bit PWM, max value of 1024
  TCCR2B = (1<<WGM22) | (1<<CS20); //No prescaler,  | _BV(WGM22)
  TCCR2C = 0b00000000; //Set to zero when in PWM mode

  uint16_t dutyCycle1 = 200;
  uint16_t dutyCycle2 = 400;
  uint16_t dutyCycle3 = 600;
  uint16_t dutyCycle4 = 800;

  writeDutyCycle(3,dutyCycle1); //PWM1 is on TOCC3
  writeDutyCycle(4,dutyCycle2); //PWM2 is on TOCC4
  writeDutyCycle(2,dutyCycle3); //PWM3 is on TOCC2
  writeDutyCycle(1,dutyCycle4); //PWM4 is on TOCC1
}


void loop() {
}


void writeDutyCycle(uint8_t tocc, uint16_t duty) {

  // Set duty cycle based on the TOCC pin being specified
  if(tocc == 1) {
    OCR1A = duty;
  } 
  else if (tocc == 2) {
    OCR1B = duty;
  }
  else if (tocc == 3) {
    OCR2A = duty;
  }
  else if (tocc == 4) {
    OCR2B = duty;
  }
  TOCPMCOE |= 1<<tocc;
  
}

Sorry, I miscounted the bit positions in the previous suggestion. I didn't look too closely at your code and I haven't used the 841 yet, but I would suggest printing out the register values first thing in setup, then after you change the duty cycle in the loop. Comparing the values might show the problem.

So I think I have tracked down the problem, but I don't know what the root cause is. The fact that Timer 2 was outputting on TOCC4 but not TOCC3 as assigned, I assigned OC1A to TOCC7, OC1B to TOCC6, and OC2A to TOCC5. This all works wonderfully, with the correct duty cycles and everything. Unfortunately, this routing doesn't work for my project/layout. But it appears that for some reason, TOCC0-3 do not output PWM with this core. I don't know if that is a core issue, a settings issue, or something else, but I have used the same code both times, just changing pin assignments, and the fact of the matter is that TOCC4-7 work just fine while TOCC0-3 does not. Dr. Azzy, I would love to hear your thoughts here. Should I create a bug ticket on gitHub, or am I just missing a small, crucial detail? Thanks!

I don't have any problem getting TOCC3 to output pwm, using analogWrite() or when I configure Timer 2 (OC2A) to use that pin. Have you tried analogWrite(), it's the default output for OC0A. Either pin 4 (cw pins) or pin 6 (ccw pins). Maybe you have a problem with your board or the chip.

Using the code you posted above I cannot reproduce your problem. I don't suppose you had the wrong pinout mapping selected? And TOCC1 is on the serial RX line- you didn't have a serial adapter plugged in by any chance did you? Depending on it's design, it's TX line might have been forceful enough to keep the tiny from driving the pin low (which would be potentialyl damaging to the tiny, as it would involve excessive current - most adapters have a 1K resistor in series with their TX pin, so if it finds itself fighting a low impedance pin trying to drive the line the other direction, they don't damage eachother, though...)

Code you posted on github is a mess, presumably from frantically changing things in hope of finding clues - but once I changed the TOCC* register writes to do what your comments and posts said you were trying to do, (and what you do here), that did not reproduce any bad behavior either.

Thank you so much for looking into this. I got some new chips in and just tested with a new one. I'm seeing the same issue still, even with switching to different breadboard location. My setup is literally just the chip with power, and my little 24 MHz logic analyzer connected to one pin at a time. I am using no serial communications at all. I have gone back to the code posted above and I am still seeing my problems.

Here is a question that might shed some light on things. I am using a nano to program the Tiny. I have nano pin 10 to reset (PB3), nano pin 11 to SDA/MOSI(PA6), nano pin 12 to TXD1/MISO (PA5), nano pin 13 to RXD1/SCK (PA4). Is this the correct pin mapping? I would love to understand what I'm doing wrong here, both for resolving the issue as well as just the learning of it. Any insight is, again, greatly appreciated.

Please post the exact code that is behaving in an unexpected manner, and explain what behavior you expect and what you see, and I will test if I can reproduce.

Below is the exact code that I am using and seeing my issue with. I don't mean to be a bother with this, I just don't understand, which is what gets me the most. Sure I can work around this by remapping my PWM outs, but that still leaves the mystery...

Expectation:
TOCC3 -> ~20% duty cycle
TOCC4 -> ~40% duty cycle
TOCC2 -> ~60% duty cycle
TOCC1 -> ~80% duty cycle

What I see:
TOCC3 -> +5V DC
TOCC4 -> ~40% duty cycle
TOCC2 -> +5V DC
TOCC1 -> +5V DC

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/atomic.h>

// Assign pins of ATTiny841, make sure it runs at 8 MHz internal oscillator

//DigitalPin Assignments
const unsigned int pwm1 = 6;
const unsigned int pwm2 = 5;
const unsigned int pwm3 = 7;
const unsigned int pwm4 = 8;
void setup() {
  //Define what each pin is
  pinMode(pwm1, OUTPUT);
  pinMode(pwm2, OUTPUT);
  pinMode(pwm3, OUTPUT);
  pinMode(pwm4, OUTPUT);
  //Set up the timer outputs to the correct pins. Don't touch timer 0 so that millis() works correctly
  //Pin 8 -> PWM2 -> TOCC4 (make timer OC2B)
  //Pin 9 -> PWM1 -> TOCC3 (make timer OC2A)
  //Pin 10 -> PWM3 -> TOCC2 (make timer OC1B)
  //Pin 11 -> PWM4 -> TOCC1 (make timer OC1A)
  TOCPMSA0 = (1<<TOCC1S0) | (1<<TOCC2S0) | (1<<TOCC3S1); //This sets TOCC1 throught TOCC3
  TOCPMSA1 = (1<<TOCC4S1); //This sets TOCC4 to be OC2B
  TOCPMCOE = (1<<TOCC1OE) | (1<<TOCC2OE) | (1<<TOCC3OE) | (1<<TOCC4OE); //Enable the time/output compare on TOCC1-4
  //Disable interrupts on timers
  TIMSK1 = 0;
  TIMSK2 = 0;
  //Set up 16 bit timers so that we can use PWM
  //PWM is 10-bit fast, 0x03FF TOP, no prescaler
  //Set to Fast, 10-bit PWM, max value of 1024
  TCCR1A = (1<<COM1A1) | (1<<COM1B1) | (1<<WGM11) | (1<<WGM10); //TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM10) | _BV(WGM11);
  TCCR1B = (1<<WGM12) | (1<<CS10); //No prescaler,  | _BV(WGM12)
  TCCR1C = 0b00000000; //Set to zero when in PWM mode
  TCCR2A = (1<<COM2A1) | (1<<COM2B1) | (1<<WGM21) | (1<<WGM20); //TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20) | _BV(WGM21); //Set to Fast, 10-bit PWM, max value of 1024
  TCCR2B = (1<<WGM22) | (1<<CS20); //No prescaler,  | _BV(WGM22)
  TCCR2C = 0b00000000; //Set to zero when in PWM mode
  uint16_t dutyCycle1 = 200;
  uint16_t dutyCycle2 = 400;
  uint16_t dutyCycle3 = 600;
  uint16_t dutyCycle4 = 800;
  writeDutyCycle(3,dutyCycle1); //PWM1 is on TOCC3
  writeDutyCycle(4,dutyCycle2); //PWM2 is on TOCC4
  writeDutyCycle(2,dutyCycle3); //PWM3 is on TOCC2
  writeDutyCycle(1,dutyCycle4); //PWM4 is on TOCC1
}
void loop() {
}
void writeDutyCycle(uint8_t tocc, uint16_t duty) {
  // Set duty cycle based on the TOCC pin being specified
  if(tocc == 1) {
    OCR1A = duty;
  }
  else if (tocc == 2) {
    OCR1B = duty;
  }
  else if (tocc == 3) {
    OCR2A = duty;
  }
  else if (tocc == 4) {
    OCR2B = duty;
  }
  TOCPMCOE |= 1<<tocc;
}

Also, attached is what my configuration settings are for programming.

config.png

So I just hooked up my brand new Espotek Labrador for troubleshooting purposes. I used it's oscilloscope function to see what the time waveform looks like instead of what the logic analyzer said. The story is interesting. On TOCC4, I get a nice, strong pulse train, peaking at or above 4.8V. On TOCC3, it is a weak pulse train centered at about 1.6V and clearly not a square wave. It gets worse for TOCC2, and by the time I measure TOCC1, it is almost a static 1.6V DC on the pin. Very weird. I have attached screen shots for each case.

ripthorn:
Below is the exact code that I am using and seeing my issue with. I don't mean to be a bother with this, I just don't understand, which is what gets me the most. Sure I can work around this by remapping my PWM outs, but that still leaves the mystery...

Expectation:
TOCC3 -> ~20% duty cycle
TOCC4 -> ~40% duty cycle
TOCC2 -> ~60% duty cycle
TOCC1 -> ~80% duty cycle

What I see:
TOCC3 -> +5V DC
TOCC4 -> ~40% duty cycle
TOCC2 -> +5V DC
TOCC1 -> +5V DC

#include <avr/io.h>

#include <avr/interrupt.h>
#include <util/atomic.h>

// Assign pins of ATTiny841, make sure it runs at 8 MHz internal oscillator

//DigitalPin Assignments
const unsigned int pwm1 = 6;
const unsigned int pwm2 = 5;
const unsigned int pwm3 = 7;
const unsigned int pwm4 = 8;
void setup() {
  //Define what each pin is
  pinMode(pwm1, OUTPUT);
  pinMode(pwm2, OUTPUT);
  pinMode(pwm3, OUTPUT);
  pinMode(pwm4, OUTPUT);
  //Set up the timer outputs to the correct pins. Don't touch timer 0 so that millis() works correctly
  //Pin 8 -> PWM2 -> TOCC4 (make timer OC2B)
  //Pin 9 -> PWM1 -> TOCC3 (make timer OC2A)
  //Pin 10 -> PWM3 -> TOCC2 (make timer OC1B)
  //Pin 11 -> PWM4 -> TOCC1 (make timer OC1A)
  TOCPMSA0 = (1<<TOCC1S0) | (1<<TOCC2S0) | (1<<TOCC3S1); //This sets TOCC1 throught TOCC3
  TOCPMSA1 = (1<<TOCC4S1); //This sets TOCC4 to be OC2B
  TOCPMCOE = (1<<TOCC1OE) | (1<<TOCC2OE) | (1<<TOCC3OE) | (1<<TOCC4OE); //Enable the time/output compare on TOCC1-4
  //Disable interrupts on timers
  TIMSK1 = 0;
  TIMSK2 = 0;
  //Set up 16 bit timers so that we can use PWM
  //PWM is 10-bit fast, 0x03FF TOP, no prescaler
  //Set to Fast, 10-bit PWM, max value of 1024
  TCCR1A = (1<<COM1A1) | (1<<COM1B1) | (1<<WGM11) | (1<<WGM10); //TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM10) | _BV(WGM11);
  TCCR1B = (1<<WGM12) | (1<<CS10); //No prescaler,  | _BV(WGM12)
  TCCR1C = 0b00000000; //Set to zero when in PWM mode
  TCCR2A = (1<<COM2A1) | (1<<COM2B1) | (1<<WGM21) | (1<<WGM20); //TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20) | _BV(WGM21); //Set to Fast, 10-bit PWM, max value of 1024
  TCCR2B = (1<<WGM22) | (1<<CS20); //No prescaler,  | _BV(WGM22)
  TCCR2C = 0b00000000; //Set to zero when in PWM mode
  uint16_t dutyCycle1 = 200;
  uint16_t dutyCycle2 = 400;
  uint16_t dutyCycle3 = 600;
  uint16_t dutyCycle4 = 800;
  writeDutyCycle(3,dutyCycle1); //PWM1 is on TOCC3
  writeDutyCycle(4,dutyCycle2); //PWM2 is on TOCC4
  writeDutyCycle(2,dutyCycle3); //PWM3 is on TOCC2
  writeDutyCycle(1,dutyCycle4); //PWM4 is on TOCC1
}
void loop() {
}
void writeDutyCycle(uint8_t tocc, uint16_t duty) {
  // Set duty cycle based on the TOCC pin being specified
  if(tocc == 1) {
    OCR1A = duty;
  }
  else if (tocc == 2) {
    OCR1B = duty;
  }
  else if (tocc == 3) {
    OCR2A = duty;
  }
  else if (tocc == 4) {
    OCR2B = duty;
  }
  TOCPMCOE |= 1<<tocc;
}




Also, attached is what my configuration settings are for programming.

You picked pin numbers based on the counterclockwise pin mapping, but are using the clockwise pin mapping. Hence, they were not set output first - except on pin 5 (bit 5 in port A, PIN_PA5, TOCC4), which happens to b arduino pin 5 in both pin mappings) When you compile for an 841, the core even outputs a warning reminding you which pinout you have selected. Which I belive I asked you about a while earlier in the thread based on your initial description of the symptoms.

This is precisely why I now use the PIN_Pxn notation (introduced on ATTinyCore in 1.4.0 - to my embarassment, I thought I'd added them last year until a month or so ago as I was getting ready to release 1.4.0, and tried to use them in a test sketch)) basically exclusively to refer to pins, and avoid "digital pin numbers" or "arduino pin numbers" whenever possible. The PIN_Pxn macros are all just #defined to the Arduino pin numbers... but using those, when you change the pin numbering, the values that those constants are #defined as changes transparently and your sketch continues unfazed by it. And there is not even a shadow of ambiguity about which pin you intend to refer to. The fact that there are "physical pin numbers" and ":arduino pin numbers" alone causes a great deal of confusion among new Arduino users.

The noise you're picking up on the scope is just that - electrical noise on a tristated input pin, probably most of it capacitively coupled in from the one pin that is PWMing;

DrAzzy:
You picked pin numbers based on the counterclockwise pin mapping, but are using the clockwise pin mapping. Hence, they were not set output first - except on pin 5 (bit 5 in port A, PIN_PA5, TOCC4), which happens to b arduino pin 5 in both pin mappings) When you compile for an 841, the core even outputs a warning reminding you which pinout you have selected. Which I belive I asked you about a while earlier in the thread based on your initial description of the symptoms.

This is precisely why I now use the PIN_Pxn notation (introduced on ATTinyCore in 1.4.0 - to my embarassment, I thought I'd added them last year until a month or so ago as I was getting ready to release 1.4.0, and tried to use them in a test sketch)) basically exclusively to refer to pins, and avoid "digital pin numbers" or "arduino pin numbers" whenever possible. The PIN_Pxn macros are all just #defined to the Arduino pin numbers... but using those, when you change the pin numbering, the values that those constants are #defined as changes transparently and your sketch continues unfazed by it. And there is not even a shadow of ambiguity about which pin you intend to refer to. The fact that there are "physical pin numbers" and ":arduino pin numbers" alone causes a great deal of confusion among new Arduino users.

The noise you're picking up on the scope is just that - electrical noise on a tristated input pin, probably most of it capacitively coupled in from the one pin that is PWMing;

Well, that was the issue. In your first response you did mention pin mapping, but I guess it just didn't sink in. You are exactly right and changing my pin mapping to the following totally fixed the problem:

const unsigned int pwm1 = PIN_A4;
const unsigned int pwm2 = PIN_A5;
const unsigned int pwm3 = PIN_A3;
const unsigned int pwm4 = PIN_A2;

I tried to set them as PIN_PAn, but it didn't recognize it, turns out it doesn't like that second P, just PIN_An in my case.

Thank you so much for helping me through this. I know it must have been frustrating, but I really do appreciate it!