Hi Mike
Sure thing but for those reading this - please remember this was my first ATtiny1604 project - I’d do it differently today.
I’ve had a quick scan through the code and it’s not very nice.
For starters - check out what Spence Konde has to say about direct port manipulation - these series 0, 1 and 2 devices are a bit different from the old ATtiny84 / ATtiny85 devices. Direct Port Manipulation
Don’t use (for example) VPORTA.OUT ^= (1<<BUZ_bp);
Instead use PORTA_OUTTGL = (1<<BUZ_bp);
I was trying to save space by using flags but have since come to realise that using a boolean variable uses less flash in the long run when you try to update and read them.
My pinChange()
routine sucks - there is absolutely no need to pass the variable I use for debounce as this is defined globally.
I think I’d use a serial library like SendOnlySoftwareSerial now in place of initializing the USART directly - I only used this for debug anyway (oh and I’d use the UPDI pin as GPIO for the debug output - that way I could keep the debug output in the final code - this adds some complexity as you then need an HV UPDI programmer but you can then use the same serial card for programming and TTY debug output.
I am a numpty at manipulating strings - I should have included #include <string.h>
to make life easier.
I used a tick counter incremented by the RTC overflow and used this for button debounce timing - not sure why I didn’t use millis()
like everyone else.
The one thing I struggled with was getting the serial coms to work in the first place - I had to tune the device as per the reference in the comments.
So here’s an example that worked for me - warts 'n all…
// cSpell:includeRegExp CStyleComment
// ATtiny1604_Magpie_TX
// written for ATtiny1604
// Peter Charles-Jones
// 19 August 2023
/* What does this do
This is the RF button part of my Magpie geocache
In essence the unit consumes little power until a button is pressed.
The one on the outside of the box (SW_M), needs to be pressed a set number (7) times for the unit to
power up the RF transmitter and transmit a P to the main unit to cause it to unlock.
The main unit is fashioned as a bird box and will wind out a cache on fishing line.
The main unit will retract the cache if the same button sequence is pressed.
The main unit will auto retract if it senses no PIR activity.
There is another button inside the unit. If this is pressed, it will cause the main unit
to go into one of two programming modes depending on whether the cache is home inside the
main unit or extended. This allows for on site setting of working light level and the amount
of line unreeled when the cache is unlocked.
*/
/* Principles of operation
The unit is normally in POWER DOWN sleep mode. Either button will wake up unit.
If the main external button is pressed (SW_M), the units starts a RTC and PIT to run in STANDBY
mode. The unit briefly wakes up on RTC overflow to check how many times the button has been pressed.
The RTC overflow interrupt is used to increment a tick counter which is used in much the same way as millis()
to debounce the buttons.
Each time a button is pressed, a feed back LED is set on and the RTC overflow interrupt is used to count how
long to keep the LED on. The unit goes back into STANDBY sleep mode between successive RTC overflow events.
If the PIT triggers (no button activity for a while), it measures how many button presses there were
and if it matches the target (7), a sequence of LED flashes - again controlled by the RTC overflow interrupt - is started.
At the end of the LED flash sequence, the MCU switches on for a chunk of time to run the RF transmission payload.
The situation with the P button is slightly different in that only one button press is required.
*/
/* Things I learned
You need to stop and restart the RTC and PIT to ensure that the first PIT timeout occurs at the same lapse time.
During initialization, the data sheet says you need to check the RTC and PIT status flags are 0 before making changes
but mid programme, doing this can cause the code to hang, waiting for status to be 0.
The RTC and PIT didn't perform consistently when started and stopped so I mostly just enabled and disabled them
generating interrupts.
*/
/* Device clock tuning
Device was tuned to 16M <https://github.com/SpenceKonde/megaTinyCore/blob/master/megaavr/extras/Ref_Tuning.md>
First the device had a bare bones sketch loaded using Arduino IDE and a boot loader burnt at 16M with Millis disabled
Then the MegaTinyTuner example sketch was loaded into target and run
Then the boot loader was re-loaded with 16M tuned selected as clock
PA5 is the LED_BUILTIN
*/
/* Issue - if the P button is not held down long enough, the button up isn't detected and the next button press is missed.
I've spent too long looking for a solution so plan to just live with it.
*/
#include <Arduino.h>
#define F_CPU 16000000
#include <avr/sleep.h> // useful macros for putting the unit into sleep mode
#include <RH_ASK.h>
// You can initialise like this: RH_ASK driver(2000, 6, 7); which will transmit on digital pin 7 == PB0 == physical pin 9 on Attiny x14 and receive on digital pin 6 == PB1 == physical pin 8 on Attiny x16 Uses Timer B1.
/* Arduino PIN numbering
This core uses a simple scheme for assigning the Arduino pin numbers:
Pins are numbered starting from the the I/O pin closest to Vcc as pin 0
and proceeding counterclockwise, skipping the (mostly) non-usable UPDI pin.
The UPDI pin is then assigned to the last pin number (as noted above, it is possible to read the UPDI pin
(both analog and digital reads work) even if it is not set as GPIO).
We recommend this as a last resort: the UPDI pin always has its pullup enabled when not set as a GPIO pin,
and a signal which looks too much like the UPDI enable sequence will cause undesired operation.
*/
/* ATtiny1604 / ARDUINO pin out
// _____
// VDD 1|* |14 GND
// (nSS) (AIN4) PA4 0~ 2| |13 10~ PA3 (AIN3)(SCK)(EXTCLK)
// (AIN5) PA5 1~ 3| |12 9 PA2 (AIN2)(MISO)
// (DAC) (AIN6) PA6 2 4| |11 8 PA1 (AIN1)(MOSI)
// (AIN7) PA7 3 5| |10 11 PA0 (nRESET/UPDI)
// (RXD) (TOSC1) PB3 4 6| |9 7~ PB0 (AIN11)(SCL)
// (TXD) (TOSC2) PB2 5~ 7|_____|8 6~ PB1 (AIN10)(SDA)
//
*/
/* Alternate pins
Pin Name Other ADC0 AC0 USART0 SPIO TWI0 TCA0 TCB0 CCL
1 VDD
2 PA4 AIN4 XDIR(*) SS WO4 LUT0-OUT
3 PA5 AIN5 OUT WO5 WO
4 PA6 AIN6 AINN0
5 PA7 AIN7 AINP0 LUT1-OUT
6 PB3 RxD WO0(*)
7 PB2 EVOUT1 TxD WO2
8 PB1 AIN10 XCK SDA WO1
9 PB0 AIN11 XDIR SCL WO0
10 PA0 RESET/UPDI AIN0 LUT0-IN0
11 PA1 AIN1 TxD(*) MOSI LUT0-IN1
12 PA2 EVOUT0 AIN2 RxD(*) MISO LUT0-IN2
13 PA3 EXTCLK AIN3 XCK(*) SCK WO3
14 GND
*/
#define TXD_bp 2 // PB2 - pin 7
#define RXD_bp 3 // PB3 - pin 6
#define PWR_bp 4 // PA4 - pin 2
// // PA5 - pin 3 - reserved for ASK TX (Arduino pin 1)
#define SW_P_bp 2 // PA2 - pin 12
#define SW_M_bp 1 // PA1 - pin 11
#define BUZ_bp 7 // PA7 - pin 5
#define LED_bp 6 // PA6 - pin 4
//pin 13 - seems to be fixed for PTT unless the 4th parameter is set to -1
#define rtc_period 6 // sets the RTC overflow hence periodicity. 6
#define PIT_RTC_PERIOD RTC_PERIOD_CYC2048_gc// sets the PIT periodicity
#define powerOnDelay 1 // delay to alow RF unit to settle
#define buttonDebounce 4 // tick counter 4
#define CNF_countMax 14 // how many Bips in a Confirm - must be even number 14
#define BIP_countMax 2 // how many ticks should a Bip last 4
//volatile uint8_t PINA_History;
volatile uint8_t INT_FLAG; // register of the following flags for communicating with ISR
#define PC_Interrupt_bp 1 // a Pin Change Interrupt has occurred
#define PIT_Interrupt_bp 2 // a Periodic Interrupt Timer interrupt has occurred
#define RTC_Overflow_bp 4 // a RTC Overflow interrupt has occurred
uint8_t FLAG = 0; // specific bits described below
#define input_bp 0 // button pressing in progress
#define BIP_on_bp 1 // Bip output is on
#define CNF_on_bp 2 // Confirm output is on
#define TX_P 3 // flag to trigger transmission P
#define TX_M 4 // flag to trigger transmission M
#define pressCountTarget 7 // how many times M button needs - 'secret never to be told'
unsigned long tickCount = 0;
//volatile char tempPINA; // used as snapshot for PINA status if it changes mid process
#define Serial_BAUD 19200
#define USART0_BAUD_RATE(BAUD_RATE) ((float)(F_CPU * 64 / (16 * (float)BAUD_RATE)) + 0.5)
// message code references used by RF piece
#define P 0 // to program the unit
#define M 1 // to open the unit
// RF message text defined here
char *myStrings[] = {(char*)"P", (char*)"M"};
RH_ASK driver(2000, -1, PIN_PA5, -1); // ATTiny, No RX, TX on PA5, no PTT
// put function declarations here:
void system_init();
// void USART0_init();
//void ptChar(char c);
//void ptString(char *str);
//void ptInteger(int n);
int pinChange (unsigned long* last_time, int pinID, char tPIN, char hPIN, char* pins);
int pinHIGH (int pinID, char* pins);
void goToSleep();
void transmit(int idx);
void powerDown(char * debounced_pins);
void tog(uint8_t pin); // debug
void bip(uint8_t pin); // debug
void flash(uint8_t pin, uint8_t max); // debug
void dummyPayload(); // debug
void disableRTCandPIT();
void enableRTCandPIT();
void enablePITInterrupts();
void disablePITInterrupts();
void enablePortAInterrupts(uint8_t pin);
void disablePortAInterrupts(uint8_t pin);
// void enablePortAPullup(uint8_t pin);
void disablePortAPullup(uint8_t pin);
void disableRTCInterrupts();
void enableRTCInterrupts();
int pinChange (unsigned long* last_time, int pinID, char tPIN, char hPIN, char* pins);
int pinHIGH (int pinID, char* pins);
void setup(){
// put your setup code here, to run once:
system_init();
bip(LED_bp); //debug
//Serial.begin(Serial_BAUD);
//Serial.println(F("Starting"));
//ptString("\r\nStarting\r\n");
if (!driver.init()){
//ptString("RH_ASK failed\r\n");
}
//goToSleep();
}
void loop(){
//ptString("\r\nloop");
//Serial.println(F("Loop"));
// put your main code here, to run repeatedly:
static unsigned long last_SW_P_time = tickCount; // debounce timer
static unsigned long last_SW_M_time = tickCount; // debounce timer
static unsigned long last_BIP_time = tickCount; // debounce timer
static char tempPINA; // used as snapshot for PINA status if it changes mid process
static char pinAHistory = VPORTA_IN; // the last read PINA status initialised at start of program
static int pressCount = 0; // tracks how many times button has been pressed
static int bipCount = 0; // tracks the bips in the confirm feedback
static char debounced_PINA = VPORTA_IN; // debounced copy of the PINA register
// poll the switches to see if any have been pressed
tempPINA = VPORTA_IN; // get latest PINA settings
// Check SW_M for a key press - to send an M to trigger the main unit
if (pinChange(&last_SW_M_time, SW_M_bp, tempPINA, pinAHistory, &debounced_PINA)){
if (!(pinHIGH(SW_M_bp,&debounced_PINA))){ // SW_M is LOW - CC - button pressed
Serial.println(F("M"));
FLAG |= (1<<BIP_on_bp); // set BIP on flag
VPORTA.OUT ^= (1<<BUZ_bp); // toggle the BUZ pin
last_BIP_time = tickCount; // reset the tick count for the Bip
pressCount ++; // increment press counter
enablePITInterrupts(); // start the timer to enter correct number fo button presses
}
if (!(FLAG & (1 << BIP_on_bp) || (FLAG & (1 << CNF_on_bp)))){ // if no feedback signal is in progress
disableRTCInterrupts(); // no longer needed as button confirmed as changed
}
}
// Check SW_P for a key press - to send a P to program the main unit
if (pinChange(&last_SW_P_time, SW_P_bp, tempPINA, pinAHistory, &debounced_PINA)){
//tog(LED_bp);
if (!(pinHIGH(SW_P_bp,&debounced_PINA))){ // SW_M is LOW - CC - button pressed
FLAG |= (1 << TX_P); // set flag to transmit a P
FLAG |= (1 << BIP_on_bp); // set BIP on flag
VPORTA.OUT ^= (1<<BUZ_bp); // toggle the BUZ pin
last_BIP_time = tickCount; // reset the tick count for the Bip
enableRTCInterrupts(); // enable RTC interrupts to track bip time
//disablePITInterrupts(); // disable PIT as we don't need it for P
}
if (!(FLAG & (1 << BIP_on_bp) || (FLAG & (1 << CNF_on_bp)))){ // if no feedback signal is in progress
disableRTCInterrupts(); // we're done with timing for responses
}
disablePITInterrupts(); // don't need to time for M presses
}
pinAHistory = tempPINA;
// Activity time tracking section
// test to see if we can shut down if no button activity
if ((tickCount - last_SW_M_time > (buttonDebounce + 1)) || (tickCount - last_SW_P_time > (buttonDebounce + 1))){
if (!(FLAG & (1 << BIP_on_bp) || (FLAG & (1 << CNF_on_bp)))){ // if no feedback signal is in progress
disableRTCInterrupts(); // stop detecting next button press
FLAG &= ~(1 << input_bp); // clear the input in progress flag
}
}
if (FLAG & (1<<BIP_on_bp)) { // the BIP feedback is running
if (tickCount - last_BIP_time > (BIP_countMax)){ // the Bip has been on long enough
if (!(FLAG & (1 << CNF_on_bp))){ // if not running a confirmation feedback
VPORTA.OUT ^= (1<<BUZ_bp); // toggle the BUZ pin
}
FLAG &= ~(1<<BIP_on_bp); // clear BIP on flag
if (!((FLAG & (1 << input_bp))||(FLAG & (1 << CNF_on_bp)))){// if a button is not being pressed or confirmation being sent
disableRTCInterrupts(); // stop detecting next button press
}
if (FLAG & (1 << TX_M)){
FLAG &= ~(1 << TX_M);
transmit(M);
//dummyPayload(); // M payload
powerDown(&debounced_PINA);
}
if (FLAG & (1 << TX_P)){
FLAG &= ~(1 << TX_P);
transmit(P);
//dummyPayload(); // P payload
powerDown(&debounced_PINA);
}
}
}
if (INT_FLAG & (1<<PIT_Interrupt_bp)){ // a PIT interrupt has happened - no SW_M button pressed for a while
INT_FLAG &= ~(1<<PIT_Interrupt_bp); // clear the flag
//flash(PWR_bp,1); // debug
if (pressCount == pressCountTarget){ // payload time as correct button sequence
FLAG |= (1 << CNF_on_bp); // set confirmation feedback flag on
bipCount = 0; // reset bip counter
FLAG |= (1<<BIP_on_bp); // set BIP on flag
VPORTA.OUT ^= (1<<BUZ_bp); // toggle the BUZ pin
last_BIP_time = tickCount; // reset the bip timer
enableRTCInterrupts(); // to track the bip on time
pressCount = 0;
}
else{
disablePITInterrupts();
pressCount = 0; // reset press count
powerDown(&debounced_PINA); // power down payload and go into POWERDOWN - SW_bp pin (tremble switch) will wake up
}
}
if (FLAG & (1 << CNF_on_bp)){ // while confirmation feedback is running
if (bipCount < CNF_countMax){
if (!(FLAG & (1 << BIP_on_bp))){ // if the bip is not currently running
FLAG |= (1<<BIP_on_bp); // set BIP on flag
VPORTA.OUT ^= (1<<BUZ_bp); // toggle the BUZ pin
last_BIP_time = tickCount; // reset the bip timer
enableRTCInterrupts();
//enableRTC();
bipCount ++; // increment the bip counter
}
}
else{
FLAG &= ~(1 << CNF_on_bp); // clear the Confirmation in progress flag
FLAG |= (1 << TX_M); // set flag to transmit a M
}
}
if (INT_FLAG & (1<<PC_Interrupt_bp)){ // a button has been pressed
INT_FLAG &= ~(1<<PC_Interrupt_bp);
//tog(LED_bp);
enableRTCInterrupts();
enablePITInterrupts();
enableRTCandPIT();
last_SW_M_time = tickCount; // restart the debounce counter
last_SW_P_time = tickCount; // restart the debounce counter
FLAG |= (1 << input_bp); // flag that button input is in progress
}
if (INT_FLAG & (1<<RTC_Overflow_bp)){ // a RTC overflow
INT_FLAG &= ~(1<<RTC_Overflow_bp); // clear the flag
tickCount ++; // increment the tick counter for debounce
}
goToSleep(); // go back to STANDBY mode
}
/* Interrupt Service Routines */
ISR(PORTA_PORT_vect){
/* Insert your PORT A interrupt handling code here */
INT_FLAG |=(1<<PC_Interrupt_bp);
/* Clear interrupt flags */
VPORTA_INTFLAGS = (1<<SW_M_bp)|(1<<SW_P_bp);
}
ISR(RTC_PIT_vect){ // puts unit back to sleep if unit is not light a while after tremble switch interrupt
/* Insert your PIT interrupt handling code here */
INT_FLAG |=(1<<PIT_Interrupt_bp);
/* The interrupt flag has to be cleared manually */
RTC.PITINTFLAGS = RTC_PI_bm;
}
ISR(RTC_CNT_vect){
/* Insert your RTC Overflow interrupt handling code */
if (RTC.INTFLAGS & RTC_OVF_bm){
INT_FLAG |= (1<<RTC_Overflow_bp);
}
/* Overflow interrupt flag has to be cleared manually */
RTC.INTFLAGS = RTC_OVF_bm | RTC_CMP_bm;
}
/* Debug routines - not called in production code */
void dummyPayload(void){
VPORTA.OUT |= (1<<PWR_bp);
//_delay_ms(500);
delay(500);
VPORTA.OUT &= ~(1<<PWR_bp);
}
void tog(uint8_t pin){ // used for debug
VPORTA.OUT ^= (1<<pin); // toggle
}
void bip(uint8_t pin){ // used for debug
VPORTA.OUT ^= (1<<pin); // toggle
//_delay_ms(10);
delay(10);
VPORTA.OUT ^= (1<<pin); // toggle
//_delay_ms(40);
delay(40);
}
void flash(uint8_t pin, uint8_t max){ // used for debug
for (uint8_t i = 0; i < max; i++){
bip(pin);
}
}
/* Main functional routines */
int pinChange (unsigned long* last_time, int pinID, char tPIN, char hPIN, char* pins) { // has the pin changed state?
if ((tPIN & (1<<pinID))!= (hPIN & (1<<pinID) )){ // last pin state different
*last_time = tickCount;
}
if ((tickCount - *last_time) > buttonDebounce){ // debounce time has elapsed
if ((tPIN & (1<<pinID))!=(*pins & (1<<pinID))){ // is the current pin state different from the current one
*pins &= ~(1<<pinID); // mask out the bit position
*pins |= (tPIN & (1<<pinID)); // get the bit value at bit location and OR into PINS
return 1; // pin has changed
}
}
return 0; // pin has not changed state
}
int pinHIGH (int pinID, char* pins){
if (*pins & (1<<pinID)){ // pin value is 1.. switch / button is Open Circuit
return 1; // HIGH
}
else{ // pin value is 0 .. switch / button is Closed to ground
return 0; //LOW
}
}
void goToSleep(void){ /* put the unit into Power down sleep mode */
sleep_enable();
sleep_cpu();
sleep_disable();
}
void powerDown(char * debounced_pins){
disableRTCandPIT(); // to prevent unnecessary interrupts during POWER DOWN mode
VPORTA.OUT &= ~(1<<BUZ_bp); // power down BUZ
VPORTA.OUT &= ~(1<<PWR_bp); // power down RF
*debounced_pins = VPORTA_IN; // update the debounced pin state
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // select POWER DOWN sleep mode
goToSleep(); // the button switch is wake up source
enableRTCandPIT(); // start the RTC so it can generate overflow events to trigger ADC and PIT to measure a timeout for this activity
set_sleep_mode(SLEEP_MODE_STANDBY); // select STANDBY sleep mode so we can measure button presses
}
void transmit(int idx){ // routine sends a string message twice
VPORTA_OUT |= (1<<PWR_bp); // PWR on
delay(powerOnDelay); // wait a bit for RF unit to stabilize
for (int i= 0; i <= 1; i++){
const char *msg = myStrings[idx];
driver.send((uint8_t *)msg, strlen(msg));
driver.waitPacketSent();
}
}
/* Helper routines - to make the code more understandable */
void enablePortAInterrupts(uint8_t pin){
uint8_t *port_pin_ctrl = ((uint8_t *)&PORTA + 0x10 + pin); // calculate port PINCTRL address for pin
*port_pin_ctrl = (*port_pin_ctrl & ~PORT_ISC_gm) | PORT_ISC_BOTHEDGES_gc; // enable PC interrupt on SW
}
void disablePortAInterrupts(uint8_t pin){
uint8_t *port_pin_ctrl = ((uint8_t *)&PORTA + 0x10 + pin); // calculate port PINCTRL address for pin
*port_pin_ctrl = (*port_pin_ctrl & ~PORT_ISC_gm) | PORT_ISC_INTDISABLE_gc; // disable PC interrupt on SW
}
void disablePortAPullup(uint8_t pin){
uint8_t *port_pin_ctrl = ((uint8_t *)&PORTA + 0x10 + pin); // calculate port PINCTRL address for pin
*port_pin_ctrl &= ~(1 << PORT_PULLUPEN_bp); // disable pull up
}
void enablePITInterrupts(){
RTC.PITINTFLAGS |= (1 << RTC_PI_bp); // clears the PIT interrupt
RTC.PITINTCTRL = 1 << RTC_PI_bp; // ensable PIT interrupts
}
void disablePITInterrupts(){
RTC.PITINTCTRL = 0 << RTC_PI_bp; // disable PIT interrupts
RTC.PITINTFLAGS |= (1 << RTC_PI_bp); // clears the PIT interrupt
}
void disableRTCInterrupts(){
RTC.INTCTRL = (0 << RTC_CMP_bp) | (0 << RTC_OVF_bp);
}
void enableRTCInterrupts(){
RTC.INTCTRL = (0 << RTC_CMP_bp) | (1 << RTC_OVF_bp);
}
void disableRTCandPIT(){
RTC.CTRLA = RTC_PRESCALER_DIV1_gc | (0 << RTC_RTCEN_bp) | (1 << RTC_RUNSTDBY_bp);
RTC.PITCTRLA = PIT_RTC_PERIOD | (0 << RTC_PITEN_bp);
}
void enableRTCandPIT(){ // make sure RTC and PIT start from the same time origin
disableRTCandPIT();
while (RTC.STATUS > 0){} // required to allow synchronisation
RTC.CTRLA = RTC_PRESCALER_DIV1_gc | (1 << RTC_RTCEN_bp) | (1 << RTC_RUNSTDBY_bp);
while (RTC.PITSTATUS > 0) {} // required to allow synchronisation
RTC.PITINTFLAGS |= (1 << RTC_PI_bp); // clears the PIT interrupt
RTC.PITCTRLA = PIT_RTC_PERIOD | (1 << RTC_PITEN_bp);
}
// Initialization routines
void mcu_init(void) /* MCU initialization */
{
/* On AVR devices all peripherals are enable from power on reset, this
* disables all peripherals to save power. Driver shall enable
* peripheral if used */
/* Set all PORTA pins to low power mode */
for (uint8_t i = 0; i < 8; i++) { // PA0 - PA7
*((uint8_t *)&PORTA + 0x10 + i) |= (1 << PORT_PULLUPEN_bp);
}
/* Set all PORTB pins to low power mode */
for (uint8_t i = 0; i < 4; i++) { // PB0 - PB3 on ATtiny1604
*((uint8_t *)&PORTB + 0x10 + i) |= (1 << PORT_PULLUPEN_bp);
}
}
void pin_init(void){ /* PIN initialization */
// define BUZ as output
VPORTA.DIR |= (1<<BUZ_bp); // set pins as output
VPORTA.OUT &= ~(1<<BUZ_bp); // set pins LOW
disablePortAPullup(BUZ_bp);
disablePortAInterrupts(BUZ_bp);
// define LED as output
VPORTA.DIR |= (1<<LED_bp); // set pins as output
VPORTA.OUT &= ~(1<<LED_bp); // set pins LOW
disablePortAPullup(LED_bp);
disablePortAInterrupts(LED_bp);
// define PWR as output
VPORTA.DIR |= (1<<PWR_bp); // set pins as output
VPORTA.OUT &= ~(1<<PWR_bp); // set pins LOW
disablePortAPullup(PWR_bp);
disablePortAInterrupts(PWR_bp);
// enable interrupts on switches
enablePortAInterrupts(SW_M_bp);
enablePortAInterrupts(SW_P_bp);
}
void RTC_0_init(void){ /* Realtime clock initialization */
/* Wait for all register to be synchronized */
while (RTC.STATUS > 0){}
/* 32KHz Internal Ultra Low Power Oscillator (OSCULP32K) */
//RTC.CLKSEL = RTC_CLKSEL_INT32K_gc;
/* 32KHz divided by 32 */
RTC.CLKSEL = RTC_CLKSEL_INT1K_gc;
/* Period: */
RTC.PER = rtc_period;
//RTC.CMP = rtc_compare;
//RTC.INTFLAGS |= (1<<RTC_OVF_bp); // clear RTC overflow interrupt
/* Compare Match Interrupt disabled, Overflow Interrupt enabled */
RTC.INTCTRL = (0 << RTC_CMP_bp) | (1 << RTC_OVF_bp);
/* Compare Match Interrupt disabled, Overflow Interrupt enabled - */
//RTC.INTCTRL = (0 << RTC_CMP_bp) | (1 << RTC_OVF_bp);
/* Prescaler of 1, disable, run in standby */
RTC.CTRLA = RTC_PRESCALER_DIV1_gc | (0 << RTC_RTCEN_bp) | (1 << RTC_RUNSTDBY_bp);
/* Prescaler of 1, enable, run in standby */
//RTC.CTRLA = RTC_PRESCALER_DIV1_gc | (1 << RTC_RTCEN_bp) | (1 << RTC_RUNSTDBY_bp);
/* Wait for all register to be synchronized */
while (RTC.PITSTATUS > 0) {}
/* RTC Clock Cycles 8192, Enable: disabled */
//RTC.PITCTRLA = RTC_PERIOD_CYC8192_gc | (0 << RTC_PITEN_bp);
/* RTC Clock Cycles 8192, Enable: enabled */
//RTC.PITCTRLA = RTC_PERIOD_CYC256_gc | (1 << RTC_PITEN_bp);
/* RTC Clock Cycles PIT_RTC_PERIOD, Enable: disabled */
RTC.PITCTRLA = PIT_RTC_PERIOD | (0 << RTC_PITEN_bp);
/* Periodic Interrupt: enabled */
RTC.PITINTCTRL = 1 << RTC_PI_bp;
/* Periodic Interrupt: disabled */
//RTC.PITINTCTRL = 0 << RTC_PI_bp;
//RTC.PITINTFLAGS |= (1 << RTC_PI_bp); // clears the PIT interrupt
}
void CPUINT_init(void){ /* Interrupt initialisation */
/* Enable interrupts */
sei();
}
void SLPCTRL_init(void){ /* Sleep controller initialization*/
/* Sleep disabled, Standby Mode */
//SLPCTRL.CTRLA = 0 << SLPCTRL_SEN_bp | SLPCTRL_SMODE_STDBY_gc;
//SLPCTRL.CTRLA = 0 << SLPCTRL_SEN_bp | SLPCTRL_SMODE_PDOWN_gc;
//set_sleep_mode(SLEEP_MODE_PWR_DOWN);
set_sleep_mode(SLEEP_MODE_STANDBY);
}
void BOD_init(void) { /* Brown Out Detection initialization */
/* The following registers have Configuration Change Protection */
//SLEEP in BOD.CTRLA
/* Brown Out Detection disabled in sleep mode */
_PROTECTED_WRITE(BOD.CTRLA, BOD_SLEEP_DIS_gc);
}
void system_init(){ /* system initialization */
mcu_init();
pin_init();
RTC_0_init();
CPUINT_init();
SLPCTRL_init();
BOD_init();
}
Here’s the platformio.ini with the RadioHead library reference
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:ATtiny1604]
platform = atmelmegaavr
board = ATtiny1604
framework = arduino
board_build.f_cpu = 16000000L
upload_speed = 115200
upload_port = COM12
upload_flags =
--tool
uart
--device
ATtiny1604
--uart
$UPLOAD_PORT
--clk
$UPLOAD_SPEED
upload_command = pymcuprog write --erase $UPLOAD_FLAGS --filename $SOURCE
lib_deps = mikem/RadioHead@^1.120
If you want to see how I powered up the ASK radio transmitter - just post a request.