I am trying to build a project using an ATtiny1604.
I need to use PWM (to control a servo) and TWI (to talk to a transducer) and Serial (for debug oputput and command entry) but the default pin assignments clash.
Looking at data sheet, there are no alternative pin assignments for TWI SCL or SDA on my device so that prevents my using TCA0 PWM signal output on WO0 and WO1.
I am using Serial.swap();
just before the Serial.begin();
statement so the alternate pins for TxD, RxD (PA1, PA2). Thanks to a solution with using alt pins for Serial by https://forum.arduino.cc/u/hmeijdam
This means, I may use WO2 for the PWM output from TCA0.
(I tried using TCB0 but the pre-scaler only gives you a very limited option and the servo needs a PWM signal with a 50Hz frequency.)
I am struggling to achieve a waveform output on W02 / PB2 physical pin 7.
In the code snippet, I initially have LED and PWR toggle real slow in setup() so I can see them, thus proving I have set up these pins successfully as output pins.
I use toggle LED in loop() just to prove the code is running.
Although I see PWM flash in setup(), when I initialise the TCA0 to generate a fast single slope PWM signal. nothing appears at PB2, physical pin 7.
Please could you suggest how I should change my PWM_0_TCA0_init() routine to successfully set up the PWM signal to PB2, physical pin 7?
I'm not too fussed about the actual pin but I need PA1, PA2 for alt serial and PB1, PB0 for TWI
// ATtiny1604_PWM_Help
// Target ATtiny1604
#include <Arduino.h>
//#include <Wire.h> //I2C Arduino Library
/* ATtiny1604 / ARDUINO Pins
* _____
* 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 LED_bp 4 // PA4 pin 2
#define PWM_bp 2 // PB2 pin 7
#define Serial_BAUD 19200
// put function declarations here:
void system_init();
void PWM_0_TCA0_init();
void setup() {
// put your setup code here, to run once:
system_init();
Serial.swap();
Serial.begin(Serial_BAUD);
Serial.println(F("\r\nStart"));
// prove that LED is configured correctly as output
PORTA_OUT ^= (1<<LED_bp);
delay(600);
PORTA_OUT ^= (1<<LED_bp);
delay(600);
// prove that PWM is configured correctly as output
PORTB_OUT ^= (1<<PWM_bp);
delay(600);
PORTB_OUT ^= (1<<PWM_bp);
delay(600);
PWM_0_TCA0_init(); // configure and enable PWM waveform - WO2 / PB2 / PWM_bp remains at 0v
}
void loop() {
PORTA_OUT ^= (1<<LED_bp); // to prove loop is running
}
// put function definitions here:
/* Interrupt Service Routines */
/* Helper routines - to make the code more readable */
// Port A
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
}
// Port B
void disablePortBPullup(uint8_t pin){
uint8_t *port_pin_ctrl = ((uint8_t *)&PORTB + 0x10 + pin); // calculate port PINCTRL address for pin
*port_pin_ctrl &= ~(1 << PORT_PULLUPEN_bp); // disable pull up
}
/* Initialization routines */
void mcu_init() /* 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 PORT A pins to low power mode and input disabled */
for (uint8_t i = 0; i < 8; i++) { // PA0 - PA7
uint8_t *port_pin_ctrl = ((uint8_t *)&PORTA + 0x10 + i);
*port_pin_ctrl |= (1 << PORT_PULLUPEN_bp); // define pull up
*port_pin_ctrl = (*port_pin_ctrl & ~PORT_ISC_gm) | PORT_ISC_INTDISABLE_gc; // disable input sensing
}
/* Set all PORT B pins to low power mode and input disabled */
for (uint8_t i = 0; i < 4; i++) { // PB0 - PB3 on ATtiny1604
uint8_t *port_pin_ctrl = ((uint8_t *)&PORTB + 0x10 + i);
*port_pin_ctrl |= (1 << PORT_PULLUPEN_bp); // define pull up
*port_pin_ctrl = (*port_pin_ctrl & ~PORT_ISC_gm) | PORT_ISC_INTDISABLE_gc; // disable input sensing
}
}
void pin_init(){ // PIN initialization
/* See <https://github.com/SpenceKonde/megaTinyCore/blob/master/megaavr/extras/Ref_DirectPortManipulation.md> */
// PORT A
PORTA_DIRSET = (1<<LED_bp); // set output pins
disablePortAPullup(LED_bp); // no need for a pull up on output pin
PORTB_OUT &= ~(1<<LED_bp); // initially LOW
// PORT B
PORTB_DIRSET = (1<<PWM_bp); // set as output
disablePortBPullup(1<<PWM_bp); // no need for a pull up on output pin
PORTA_OUT &= ~(1<<PWM_bp); // initially LOW
}
void CPUINT_init(){ // Interrupt initialisation
/* Enable interrupts */
sei();
}
void PWM_0_TCB0_init(){
TCB0.CCMPL = 0xff; /* PWM Period: 0xff */
TCB0.CCMPH = 0x64; /* PWM Compare: 0x64 */
// TCB0.CNT = 0x0; /* Count: 0x0 */
TCB0.CTRLB = 0 << TCB_ASYNC_bp /* Asynchronous Enable: disabled */
| 1 << TCB_CCMPEN_bp /* Pin Output Enable: enabled */
| 1 << TCB_CCMPINIT_bp /* Pin Initial State: disabled */
| TCB_CNTMODE_PWM8_gc; /* 8-bit PWM */
// TCB0.DBGCTRL = 0 << TCB_DBGRUN_bp; /* Debug Run: disabled */
// TCB0.EVCTRL = 0 << TCB_CAPTEI_bp /* Event Input Enable: disabled */
// | 0 << TCB_EDGE_bp /* Event Edge: disabled */
// | 0 << TCB_FILTER_bp; /* Input Capture Noise Cancellation Filter: disabled */
TCB0.INTCTRL = 1 << TCB_CAPT_bp /* Setting: enabled */;
TCB0.CTRLA = TCB_CLKSEL_CLKDIV2_gc /* CLK_PER (No Prescaling) */
| 1 << TCB_ENABLE_bp /* Enable: enabled */
| 0 << TCB_RUNSTDBY_bp /* Run Standby: enabled */
| 0 << TCB_SYNCUPD_bp; /* Synchronize Update: disabled */
}
void PWM_0_TCA0_init(){
//TCA0.SINGLE.CMP0 = 400; /* Setting: 0x0 */
// TCA0.SINGLE.CMP1 = 0x0; /* Setting: 0x0 */
TCA0.SINGLE.CMP2 = 0x2ff; // I think this defines the mark space ratio
// TCA0.SINGLE.CNT = 0x0; /* Count: 0x0 */
TCA0.SINGLE.CTRLB = 0 << TCA_SINGLE_ALUPD_bp /* Auto Lock Update: disabled */
| 0 << TCA_SINGLE_CMP0EN_bp /* Setting: disabled */
| 0 << TCA_SINGLE_CMP1EN_bp /* Setting: disabled */
| 1 << TCA_SINGLE_CMP2EN_bp /* Setting: enabled for WO2*/
| TCA_SINGLE_WGMODE_SINGLESLOPE_gc; /* */
TCA0.SINGLE.CTRLC = 0 << TCA_SINGLE_CMP0OV_bp /* Setting: disabled */
| 0 << TCA_SINGLE_CMP1OV_bp /* Setting: disabled */
| 1 << TCA_SINGLE_CMP2OV_bp; /* Setting: enabled */
// TCA0.SINGLE.DBGCTRL = 0 << TCA_SINGLE_DBGRUN_bp; /* Debug Run: disabled */
// TCA0.SINGLE.EVCTRL = 0 << TCA_SINGLE_CNTEI_bp /* Count on Event Input: disabled */
// | TCA_SINGLE_EVACT_POSEDGE_gc /* Count on positive edge event */;
// TCA0.SINGLE.INTCTRL = 0 << TCA_SINGLE_CMP0_bp /* Compare 0 Interrupt: disabled */
// | 0 << TCA_SINGLE_CMP1_bp /* Compare 1 Interrupt: disabled */
// | 0 << TCA_SINGLE_CMP2_bp /* Compare 2 Interrupt: disabled */
// | 0 << TCA_SINGLE_OVF_bp; /* Overflow Interrupt Enable: disabled */
TCA0.SINGLE.PER = 0xffff; // This helps define the frequency taken with the prescaler TCA_SINGLE_CLKSEL_DIV256_gc
TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV256_gc /* System Clock */
| 1 << TCA_SINGLE_ENABLE_bp /* Module Enable: enabled */;
}
void system_init(){ /* system initialization */
mcu_init();
pin_init();
CPUINT_init();
//PWM_0_TCB0_init(); // don't think I can use this as setting the frequency to 50Hz would be a challenge
//PWM_0_TCA0_init(); // now called at the end of set up after the test pin flashes
}