Register / Login | Desktop view |
#include "Arduino_menu.h"
#include "definitions.h"
#include "Clock_Setup.h"
#include "PWM_Signals.h"
#include "Dual_PWM_HW.h"
#include <IoAbstraction.h>
#include <IoAbstractionWire.h>
#include <TaskManagerIO.h>
#include <SPI.h>
#include <Wire.h>
//Pin numbers on evb
#define PWM_OUT1 9
#define PWM_OUT2 10
#define INTERRUPT_PIN_8574 11
#define GFX_CS 12
#define GFX_DC 13
//Pin Numbers on PCF8574
// 5,6,7 are the tcMenu encoder
#define ENABLE_DISABLE_BUTTON 1
#define ROTARY2_SW 2
#define ROTARY2_DT 3
#define ROTARY2_CLK 4
#define MAX_ROTARY_MODES 2 // mode definitions right below
#define FREQUENCY_ADJUST_MODE 0
#define DUTY_CYCLE_ADJUST_MODE 1
#define PHASE_ADJUST_MODE 2
uint16_t rotaryMode = 0;
volatile uint32_t virtualFrequency = DEFAULT_FREQUENCY;
volatile uint32_t virtualDutyCycle = DEFAULT_DUTYCYCLE*1000;
volatile int virtualPhase = DEFAULT_PHASE;
Clock_Setup clk;
PWM_Signals signals;
HardwareRotaryEncoder* RotaryEncoder2;
//0x20 address is means A0-A2 on chip are low
IoAbstractionRef ioExpander = ioFrom8574(0x20, INTERRUPT_PIN_8574);
Adafruit_ST7735 gfx = Adafruit_ST7735(GFX_CS, GFX_DC, -1);
AdaColorGfxMenuConfig colorConfig;
void CALLBACK_FUNCTION onRotary2Changed(int newVal){
switch(rotaryMode){
case FREQUENCY_ADJUST_MODE:
virtualFrequency = newVal;
signals.setSignalFrequency(virtualFrequency,PWM_SIGNAL_1);
signals.setSignalDutyCycle(virtualDutyCycle/1000,PWM_SIGNAL_1);
signals.setSignalFrequency(virtualFrequency,PWM_SIGNAL_2);
signals.setSignalDutyCycle(virtualDutyCycle/1000,PWM_SIGNAL_2);
break;
case DUTY_CYCLE_ADJUST_MODE:
virtualDutyCycle = newVal;
signals.setSignalDutyCycle(virtualDutyCycle/1000,PWM_SIGNAL_1);
signals.setSignalDutyCycle(virtualDutyCycle/1000,PWM_SIGNAL_2);
break;
case PHASE_ADJUST_MODE:
virtualPhase = newVal;
signals.setSignalPhase(virtualPhase,virtualDutyCycle/1000);
break;
}
}
//void CALLBACK_FUNCTION onRotary2Changed(int newVal){}
void CALLBACK_FUNCTION enableDisableButtonPressed(pinid_t pin, bool heldDown){
if(heldDown){} //If holding down the enable/disable button should do something it goes here
//validate that pressing the enable/disable button doesn't screw things up if it's pressed while menu item for enable output is OFF/FALSE
if (menuEnableOutput.getBoolean()){
//toggle timer 0
TCC0->CTRLA.reg ^= (TCC_CTRLA_ENABLE); while (TCC0->SYNCBUSY.bit.ENABLE) {}
//toggle timer 1
TCC1->CTRLA.reg ^= (TCC_CTRLA_ENABLE); while (TCC1->SYNCBUSY.bit.ENABLE) {}
}
}
void CALLBACK_FUNCTION onRotary2SwitchPressed(pinid_t pin, bool heldDown){
if(heldDown){}//If holding down Rotary2 should do something it goes here
// if were already in the last rotaryMode adjustment mode then go back to 0
if ((rotaryMode+1) > MAX_ROTARY_MODES){
rotaryMode=0;
}
//Otherwise increment to the next fine tune adjustment mode
rotaryMode++;
switch (rotaryMode){
case FREQUENCY_ADJUST_MODE:
switches.changeEncoderPrecision(1,MAX_FREQUENCY,virtualFrequency);
break;
case DUTY_CYCLE_ADJUST_MODE:
switches.changeEncoderPrecision(1,MAX_DUTYCYCLE*1000,virtualDutyCycle);
break;
case PHASE_ADJUST_MODE:
switches.changeEncoderPrecision(1,MAX_PHASE,virtualPhase);
break;
}
}
void setup() {
Wire.begin();
initDualPWM(); //sets up output pins and clocks
switches.initialiseInterrupt(ioFrom8574(0x20, INTERRUPT_PIN_8574),true);
//HardwareRotaryEncoder* RotaryEncoder2 = new HardwareRotaryEncoder(ROTARY2_CLK, ROTARY2_DT, onRotary2Changed);
RotaryEncoder2 = new HardwareRotaryEncoder(ROTARY2_CLK, ROTARY2_DT, onRotary2Changed);
switches.setEncoder(1, RotaryEncoder2); //puts this 2nd encoder in "Slot 1"... "Slot 0" is for the menu encoder
/*
Enable/Disable button
PRESS disables output
HOLD disables output and resets all the timer registers*/
// switches.addSwitch(ENABLE_DISABLE_BUTTON,enableDisableButtonPressed,NO_REPEAT,true);
switches.addSwitch(ENABLE_DISABLE_BUTTON,enableDisableButtonPressed,NO_REPEAT,true);
/*
Rotary 2 push button changes between adjustment modes on output screens
PRESS - adjust pulse width / Duty cycle, adjust phase
HOLD - gives back the screen - output is still enabled */
// switches.addSwitch(ROTARY2_SW, onRotary2SwitchPressed,NO_REPEAT,true);
switches.addSwitch(ROTARY2_SW, onRotary2SwitchPressed,NO_REPEAT,true);
//I think this makes the callback only return direction 1=CW, -1=CCW
//switches.changeEncoderPrecision(1,0,0);
taskManager.addInterrupt(ioExpander,ROTARY2_CLK,CHANGE);
taskManager.addInterrupt(ioExpander,ROTARY2_SW, FALLING);
taskManager.addInterrupt(ioExpander, ENABLE_DISABLE_BUTTON,FALLING);
//taskManager.addInterrupt(switches.getIoAbstraction(),ROTARY2_CLK,CHANGE);
//taskManager.addInterrupt(switches.getIoAbstraction(),ROTARY2_SW, FALLING);
//taskManager.addInterrupt(switches.getIoAbstraction(), ENABLE_DISABLE_BUTTON,FALLING);
prepareAdaMonoGfxConfigLoRes(&colorConfig);
gfx.initR(INITR_144GREENTAB); //Adafruit 1.44"
gfx.setRotation(1);
setupMenu();
}
void initDualPWM(){
pinMode(PWM_OUT1,OUTPUT); //D9 is PA7 Pin 12 Function E TCC1
pinMode(PWM_OUT2,OUTPUT); //D10 is PA18 Pin 27 Function F TCC0
//pinMode(ENABLE_DISABLE_BUTTON,INPUT_PULLUP);
//There should be no output until the user sets up the output
digitalWrite(PWM_OUT1, 0);
digitalWrite(PWM_OUT2, 0);
// Enable the peripheral multiplexer for the pins.
PORT->Group[0].PINCFG[18].reg |= PORT_PINCFG_PMUXEN;
PORT->Group[0].PINCFG[7].reg |= PORT_PINCFG_PMUXEN;
// Set PA18's function to function F. Function F is TCC0/WO[2] for PA18.
// Because it's an even numbered pin the PMUX is E (even) and the PMUX
// index is pin number / 2, so 9.
PORT->Group[0].PMUX[9].reg = PORT_PMUX_PMUXE_F;
// Set PA07's function to function E. Function E is TCC1/WO[1] for PA07.
// Because this is an odd numbered pin the PMUX is O (odd) and the PMUX
// index is pin number - 1 / 2, so 3.
PORT->Group[0].PMUX[3].reg = PORT_PMUX_PMUXO_E;
clk.Clock_Init(); //setup system clocks
//PUT THIS SOMEWHERE WHERE IT MAKES SENSE!
//signals.PWM_Signals_Init();
}
void loop() {
taskManager.runLoop();
}
void CALLBACK_FUNCTION enablePWMOutNow(int id) {
signals.PWM_Signals_Init();
}
void CALLBACK_FUNCTION resetRegistersNow(int id) {
// TODO - your menu change code
}
void CALLBACK_FUNCTION globalResetNow(int id) {
// TODO - your menu change code
}
void CALLBACK_FUNCTION setPWMFrequency(int id) {
// TODO - your menu change code
}
void CALLBACK_FUNCTION changedPWMDutyCycle(int id) {
// TODO - your menu change code
}
void CALLBACK_FUNCTION changedPWMPhase(int id) {
// TODO - your menu change code
}
void CALLBACK_FUNCTION enableOutputFlag(int id) {
// TODO - your menu change code
if (menuEnableOutput.getBoolean()==false) // if it's changed to "NO" then it was "YES" so turn off output
enableDisableButtonPressed(id,false);
}
void CALLBACK_FUNCTION beginFrequencySweep(int id) {
// TODO - your menu change code
}
void CALLBACK_FUNCTION beginDutyCycleSweep(int id) {
// TODO - your menu change code
}
void CALLBACK_FUNCTION beginPhaseSweep(int id) {
// TODO - your menu change code
}
void MenuManager::initForEncoder(MenuRenderer* renderer, MenuItem* root, pinid_t encoderPinA, pinid_t encoderPinB, pinid_t encoderButton) {
this->renderer = renderer;
this->currentRoot = this->rootMenu = root;
switches.addSwitch(encoderButton, nullptr);
switches.onRelease(encoderButton, [](pinid_t /*key*/, bool held) { menuMgr.onMenuSelect(held); });
setupRotaryEncoderWithInterrupt(encoderPinA, encoderPinB, [](int value) {menuMgr.valueChanged(value); });
renderer->initialise();
}
switches.onRelease(encoderButton, [](pinid_t /*key*/, bool held) { menuMgr.onMenuSelect(held); });
setupRotaryEncoderWithInterrupt(encoderPinA, encoderPinB, [](int value) {menuMgr.valueChanged(value); });
Arduino MKR SAMD21 board
MKR Ethernet Sheild
ST7735 display
i2c AT24 EEPROM
PCF8574 Device on 0x20
0, 2, 3 -> Rotary encoder 2
5, 6, 7 -> Rotary encoder 1
1 -> LED
Next, as previously discussed, it is using two different IOexpander references to refer to the same 8574 device, and there could be other errors that I don't immediately see, but I'd start with those two.
Line 111 -switches.initialiseInterrupt(ioFrom8574(0x20, INTERRUPT_PIN_8574),true);
If switches doesn't need to be told to explicitly treat signals coming from this pin as interrupts and that they represent signals from an encoder over i2c then it must be these lines that designate interrupts using TaskManagerIO
taskManager.addInterrupt(ioExpander,ROTARY2_CLK,CHANGE); taskManager.addInterrupt(ioExpander,ROTARY2_SW, FALLING); taskManager.addInterrupt(ioExpander, ENABLE_DISABLE_BUTTON,FALLING);
Hopefully this is the way to think about it.
Further, you're adding switches before setupMenu() is called that does the switches initialization. Only ever call into switches after setupMenu has run. I'll make that clearer in the documentation.
Yes as previously discussed
//43 IoAbstractionRef ioExpander = ioFrom8574(0x20, INTERRUPT_PIN_8574)
//111 switches.initialiseInterrupt(ioFrom8574(0x20, INTERRUPT_PIN_8574),true)
Line 111 has no business being there and this important to remove. It's equally important to me that I now think I understand why
Ah ha. Well... I'll be. I wouldn't have ever guessed this was important.
Documentation wise I think there are things that could be done that would have prevented someone admittedly inexperienced from having trouble. That isn't meant to be a criticism. No on one the planet knows these libraries better than you and that might make you too close to it to write documentation for plebs like me to understand. Each library is documented well on its own and there are many examples of how to use one with another. I just happened to want to use almost all of them on my first attempt and was cobbling together sections of code from different pages and in doing so initialising some things more than once, incorrectly, and in the wrong order. I'll keep and eye out for changes to documentations about what order things need to take place in just in case I can learn something else.
@davecc I can recognise that you've gone beyond what you think is appropriate for an issue being presented on this forum and the effort isn't unnoticed. I never feel entitled to free dev support so that makes me grateful for it. I'm one of those people whose feature set includes pervasive ambiguation when concepts are abstracted and specific use cases aren't explicit. I also seem to only learn from my own mistakes and tend to step in every mistake possible taking on projects above my skill level because my ambition outweighs my experience and wallet.
I just paid off an an invoice for dev work/hardware for November and December is a tough month for obvious reasons. I'm trying to get something that functions well enough so that I can send you complete source for evaluation of commercial support for bluetooth control because there's no chance I'm going to get that working. I'm looking forward to working with you in 2021. I'm going to read your new examples and hopefully you won't be hearing from me until next year.