Add to Project SoftPWM library by Brett Hagman for ATtiny84A

Hi everybody,

I’m using VSCode/PlatformIO to develop for an ATtiny84A. I need 6 PWM channels to control the brightness of 6 LEDs. I can get the 4 hardware PWM channels to work right away. I’m trying to use SoftPWM library by Brett Hagman to get two more of the pins to output a PWM signal. I used the Library Manager to search the Registry for SoftPWM library and clicked on “Add to Project”. This is my current platformio.ini:

[env:attiny84]
platform = atmelavr
board = attiny84
framework = arduino
board_build.f_cpu = 8000000L
upload_protocol = stk500
board_fuses.efuse = 0xFF
board_fuses.hfuse = 0xDF
board_fuses.lfuse = 0xE2
upload_flags = 
    -P$UPLOAD_PORT
    -b$UPLOAD_SPEED
upload_speed = 115200
upload_port = COM7
lib_deps = bhagman/SoftPWM@^1.0.1

However, when I add

#include <SoftPWM.h>

to my main.cpp and try to compile the project, I get the following output:

In file included from .pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:42:0:
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp: In function 'void TIMER2_COMPA_vect()':
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:36:36: warning: 'TIMER2_COMPA_vect' appears to be a misspelled 'signal' handler, missing '__vector' prefix [-Wmisspelled-isr]
 #define SOFTPWM_TIMER_INTERRUPT    TIMER2_COMPA_vect
                                    ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:85:5: note: in expansion of macro 'SOFTPWM_TIMER_INTERRUPT'
 ISR(SOFTPWM_TIMER_INTERRUPT)
     ^~~~~~~~Compiling .pio\build\attiny84\FrameworkArduino\new.cpp.o
~~~~~~~~~~~~~~~
In file included from .pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:44:0:
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp: In function 'void SoftPWMBegin(uint8_t)':
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:39:3: error: 'TIFR2' was not declared in this scope
   TIFR2 = (1 << TOV2);    /* clear interrupt flag */ \
   ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~~~~~~~
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:39:3: note: suggested alternative: 'TIFR0'
   TIFR2 = (1 << TOV2);    /* clear interrupt flag */ \
   ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~Compiling .pio\build\attiny84\FrameworkArduino\wiring.c.o
~~~~~~
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:39:17: error: 'TOV2' was not declared in this scope
   TIFR2 = (1 << TOV2);    /* clear interrupt flag */ \
                 ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~~~~~~~Compiling .pio\build\attiny84\FrameworkArduino\wiring_analog.c.o

.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:39:17: note: suggested alternative: 'TOV0'
   TIFR2 = (1 << TOV2);    /* clear interrupt flag */ \
                 ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~~~~~~~
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:40:3: error: 'TCCR2B' was not declared in this scope
   TCCR2B = (1 << CS21);   /* start timer (ck/8 prescalar) */ \
   ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~~~~~~~
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:40:3: note: suggested alternative: 'TCCR0B'
   TCCR2B = (1 << CS21);   /* start timer (ck/8 prescalar) */ \
   ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~~~~~~~
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:40:18: error: 'CS21' was not declared in this scope
   TCCR2B = (1 << CS21);   /* start timer (ck/8 prescalar) */ \
                  ^
Compiling .pio\build\attiny84\FrameworkArduino\wiring_digital.c.o
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~~~~~~~
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:40:18: note: suggested alternative: 'CS01'
   TCCR2B = (1 << CS21);   /* start timer (ck/8 prescalar) */ \
                  ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~~~~~~~
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:41:3: error: 'TCCR2A' was not declared in this scope
   TCCR2A = (1 << WGM21);  /* CTC mode */ \
   ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~~~~~~~
Compiling .pio\build\attiny84\FrameworkArduino\wiring_pulse.S.o
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:41:3: note: suggested alternative: 'TCCR0A'
   TCCR2A = (1 << WGM21);  /* CTC mode */ \
   ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~~~~~~~
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:41:18: error: 'WGM21' was not declared in this scope
   TCCR2A = (1 << WGM21);  /* CTC mode */ \
                  ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~~~~~~~
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:41:18: note: suggested alternative: 'WGM01'
   TCCR2A = (1 << WGM21);  /* CTC mode */ \
                  ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~~~~~~~
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:42:3: error: 'OCR2A' was not declared in this scope
   OCR2A = (ocr);          /* We want to have at least 30Hz or else it gets choppy */ \
   ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~~~~~~~
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:42:3: note: suggested alternative: 'OCR0A'
   OCR2A = (ocr);          /* We want to have at least 30Hz or else it gets choppy */ \
Compiling .pio\build\attiny84\FrameworkArduino\wiring_pulse.c.o
   ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~~~~~~~
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:43:3: error: 'TIMSK2' was not declared in this scope
   TIMSK2 = (1 << OCIE2A); /* enable timer2 output compare match interrupt */ \
   ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~~~~~~~
Compiling .pio\build\attiny84\FrameworkArduino\wiring_shift.c.o
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:43:3: note: suggested alternative: 'TIMSK0'
   TIMSK2 = (1 << OCIE2A); /* enable timer2 output compare match interrupt */ \
   ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~~~~~~~
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:43:18: error: 'OCIE2A' was not declared in this scope
   TIMSK2 = (1 << OCIE2A); /* enable timer2 output compare match interrupt */ \
                  ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~~~~~~~
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:43:18: note: suggested alternative: 'OCIE0A'
   TIMSK2 = (1 << OCIE2A); /* enable timer2 output compare match interrupt */ \
                  ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:171:3: note: in expansion of macro 'SOFTPWM_TIMER_INIT'
   SOFTPWM_TIMER_INIT(SOFTPWM_OCR);
   ^~~~~~~~~~~~~~~~~~
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp: In function 'void SoftPWMSet(int8_t, uint8_t, uint8_t)':
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:37:37: error: 'TCNT2' was not declared in this scope
 #define SOFTPWM_TIMER_SET(val)     (TCNT2 = (val))
                                     ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:220:5: note: in expansion of macro 'SOFTPWM_TIMER_SET'
     SOFTPWM_TIMER_SET(0);
     ^~~~~~~~~~~~~~~~~
.pio\libdeps\attiny84\SoftPWM\SoftPWM_timer.h:37:37: note: suggested alternative: 'TCNT0'
 #define SOFTPWM_TIMER_SET(val)     (TCNT2 = (val))
                                     ^
.pio\libdeps\attiny84\SoftPWM\SoftPWM.cpp:220:5: note: in expansion of macro 'SOFTPWM_TIMER_SET'
     SOFTPWM_TIMER_SET(0);
     ^~~~~~~~~~~~~~~~~
*** [.pio\build\attiny84\lib978\SoftPWM\SoftPWM.cpp.o] Error 1
In file included from C:\Users\stefan\.platformio\packages\framework-arduino-avr-attiny\cores\tiny\Arduino.h:187:0,
                 from C:\Users\stefan\.platformio\packages\framework-arduino-avr-attiny\cores\tiny\wiring_private.h:35,
                 from C:\Users\stefan\.platformio\packages\framework-arduino-avr-attiny\cores\tiny\wiring_digital.c:29:
C:\Users\stefan\.platformio\packages\framework-arduino-avr-attiny\variants\tinyX4/pins_arduino.h:170:2: warning: #warning "This is the COUNTERCLOCKWISE pin mapping - make sure you're using the pinout diagram with the pins in counter clockwise order" [-Wcpp]
 #warning "This is the COUNTERCLOCKWISE pin mapping - make sure you're using the pinout diagram with the pins in counter clockwise order"
  ^~~~~~~

I suspect that either I didn’t include the library in the correct way, or the library is not compatible with the platform, board or framework (don’t know the exact difference between these three) I’m using. As I find PlatformIO quite confusing I don’t know what to do now. Therefore any help is very much apprechiated.

Thank you!

BR,
Stefan

The library attempts to make use of the Timer2 peripheral (or rather, an interrupt vector exposed by that peripheral), which your hardware doesn’t have. Look in the ATTiny84 datasheet and you’ll see from the chapters that there’s no Timer2, only Timer0 and Timer1. Also visible in the interrupt table info

If you look at SoftPWM/SoftPWM_timer.h at master · bhagman/SoftPWM · GitHub you will see the possible timer options that this library has – for __AVR_ATmega32U4__ it uses Timer4, for Teensy ARM devices it uses an IntervalTimer, and for all else it attempts to use Timer2, which you don’t have.

So, you can’t really use this library. Without re-writing at least, to timer1 (with caveats below). People have already asked this but gotten no response.

More info at GitHub - SpenceKonde/ATTinyCore: Arduino core for ATtiny 1634, 828, x313, x4, x41, x5, x61, x7 and x8 regarding the PWM and timers.

Thank you very much for your helpful and detailed answer, although it’s not that good news, because rewriting the library to use Timer1 is still well above my horizon. :grin: After all, I now know what to look for. Maybe I can find a library which implements software PWM on an ATtiny84 using Timer1.

Also the link to the ATTinyCore you provided is very helpful. However I’m wondering how do I know from within PlatformIO that it’s using this “Core” for the ATtiny84? You know, I just installed PlatformIO, selected ATtiny84 as my “Board” and it just downloads something which I didn’t know that it was actually the ATTinyCore core. Where is PlatformIO showing this information?

The library at DigisparkArduinoIntegration/libraries/DigisparkTinySoftPwm/TinySoftPwm.h at master · digistump/DigisparkArduinoIntegration · GitHub states it can control all pins of a port with software PWM, and the ATTiny84A pinout has pins PA0 to PA7 and PB0 to PB3 – so maybe it works for port A.

I’ve also found code at Soft-PWM – Mikrocontroller.net that uses Timer1 and can handle 8 PWM channels.

Lastly, you might be able to rewrite the library quite easily if you understand the register layout, e.g. explained at How to set the PWM-frequency for the Attiny84 (and the datasheet of course) – after all, all it needs is a generic timer that throws an interrupt at some rate, then the interrupt handler will figure out which pins to set to what value.

You can try and start copying the library into the lib/ folder of the project, remove the old .pio folder of the project (now the SoftPWM library will be sourced from lib/, then changing the block

to the interrupt vector name for Timer1 and the register names and constants for Timer1. In most cases that is just changing a 2 to a 1. (TCNT1, TIMER1_COMPA_VECT, …).

PlatformIO will show you the name of th used package at the start of the compilation.

PACKAGES:
 - framework-arduino-avr-attiny 1.5.2
 - toolchain-atmelavr 1.70300.191015 (7.3.0)

Packages are stored in <user folder>/.platformio/packages/<package name>. The meta-info file package.json (and of course the files themselves) will tell you more about the core. In this case the package.json reads

{
    "name": "framework-arduino-avr-attiny",
    "description": "Arduino Wiring-based Framework (ATtinyCore)",
    "url": "https://github.com/SpenceKonde/ATTinyCore",
    "version": "1.5.2"
}

which shows that it’s using the SpenceKonde ATTinyCore, which is exactly the core to which documentation I’ve linked you to in the last post.

Internally, PlatformIO uses the core attribute of the board definition to determine which core to use.

Oh I actually retract the previous library recommendations.

^ looks like this lib specifically knows about the ATTiny84 (and related chips) and uses Timer1. Exactly what you need.