Closed niektb closed 2 years ago
@niektb I can be more helpful if I can see your code. Are you able to post it? Also, what dev board/MCU are you targeting?
Of course I can! Sorry if it's a bit rough round the edges but it is a heavy work-in-progress (currently working to remove a SPI digipot so you might still see some remnants in the code) :)
Right now I'm targeting an Arduino Nano (with the old bootloader)!
#include <HeliOS_Arduino.h>
#include <avr/pgmspace.h>
#include <SPI.h>
/* PIN DEFINITIONS */
const uint8_t pin_footswitch = 2;
const uint8_t pin_rgb_b = 3;
const uint8_t pin_rgb_r = 5;
const uint8_t pin_rgb_g = 6;
const uint8_t pin_delay_mod = 9;
const uint8_t pin_delay_pwm = 10;
const uint8_t pin_tails = 8;
const uint8_t pin_delay_time = A0;
const uint8_t pin_mod_speed = A1;
/* MODE PARAM */
bool bypass = true;
bool tempo_blink = false;
const uint8_t SMODE_BYPASS = 0;
const bool BYPASS_NORM = 0;
const bool BYPASS_TAIL = 1;
const uint8_t SMODE_TEMPO = 1;
const bool TEMPO_POT = 0;
const bool TEMPO_TAP = 1;
const uint8_t SMODE_MOD = 2;
volatile double mod_speed = 0.5;
const uint8_t MODE_CONFIG = 0;
const uint8_t MODE_PLAY = 1;
volatile uint8_t mode = MODE_PLAY;
volatile uint8_t smode = SMODE_TEMPO;
/* MODULATION PARAMETERS */
const uint8_t TRIANGLE = 0;
const uint8_t SINE = 1;
const uint8_t SQUARE = 2;
volatile uint8_t waveform = TRIANGLE;
const PROGMEM uint8_t triangle_wave[1024] = {/*ommitted for readability*/};
const PROGMEM uint8_t sine_wave[1024] = {/*ommitted for readability*/};
const PROGMEM uint8_t square_wave[1024] = {/*ommitted for readability*/};
volatile double wp = 0;
/* DELAY PARAMETERS */
#define MCP_NOP 0b00000000
#define MCP_WRITE 0b00010001
#define MCP_SHTDWN 0b00100001
const uint8_t ssMCPin = 10;
volatile int delay_time = 250;
volatile int delay_time_prev = 0;
unsigned long t_ms = 0;
unsigned long prev_t = 0;
unsigned long first_tap_time;
unsigned long first_release_time;
bool wait_for_release = false;
/* DEBOUNCE PARAMETERS */
const uint8_t debounce_delay = 20;
unsigned long last_debounce_time = 0;
bool fsstate;
bool last_fsstate = HIGH;
bool button_prev = HIGH;
/* HELPER FUNCTIONS */
// SPI write the command and data to the MCP IC connected to the ssPin
void SPIWrite(uint8_t cmd, uint8_t _data, uint8_t ssPin) {
SPI.beginTransaction(SPISettings(14000000, MSBFIRST, SPI_MODE0));
digitalWrite(ssPin, LOW); // SS pin low to select chip
SPI.transfer(cmd); // Send command code
SPI.transfer(_data); // Send associated value
digitalWrite(ssPin, HIGH);// SS pin high to de-select chip
SPI.endTransaction();
}
double map_mod_speed(int aRead) {
return pow(10, (double)aRead/1023) * 0.8 - 0.8 + 0.0125;
}
void wp_inc() {
mod_speed = map_mod_speed(analogRead(pin_mod_speed));
wp = wp + (1 / mod_speed);
if (wp > 1023) {
wp = wp - 1023;
}
if (wp < 0)
wp = 0;
}
const uint8_t * get_wavetable_ptr() {
if (waveform == TRIANGLE) {
return triangle_wave;
} else if (waveform == SINE) {
return sine_wave;
} else { // waveform == SQUARE
return square_wave;
}
}
/* TASKS */
void taskButton(xTaskId id_)
{
// do debounce with ISR
int button = digitalRead(pin_footswitch);
// reset debouncing timer if the switch changed, due to noise or pressing:
if (button != button_prev) {
last_debounce_time = millis();
}
if ((millis() - last_debounce_time) > debounce_delay) {
if (button != fsstate)
fsstate = button;
if (fsstate == LOW && last_fsstate == HIGH)
{ // FALLING EDGE
first_tap_time = millis();
wait_for_release = true;
last_fsstate = fsstate;
}
else if (wait_for_release && ((millis() - first_tap_time) >= 1000))
{
wait_for_release = false;
xTaskNotify(xTaskGetId("TASKMODEMAN"), 4, (char *)"SMOD" );
}
else if (fsstate == HIGH && last_fsstate == LOW) // RISING EDGE
{
if (wait_for_release)
{
first_release_time = millis();
xTaskNotify(xTaskGetId("TASKMODEMAN"), 4, (char *)"STEP" );
wait_for_release = false;
}
last_fsstate = fsstate;
}
else { // nothing changed}
}
button_prev = button;
}
void update_led_status() {
analogWrite(pin_rgb_r, 255 * (smode == SMODE_BYPASS));
analogWrite(pin_rgb_b, 255 * (smode == SMODE_MOD));
if (smode == SMODE_BYPASS) {
analogWrite(pin_rgb_g, 127 * bypass);
} else {}
}
void taskModeMan(xTaskId id_) {
xTaskGetNotifResult res = xTaskGetNotif(id_);
if (res) {
if (strcmp(res->notifyValue, "SMOD") == 0)
{
smode++;
if (smode > SMODE_MOD)
smode = SMODE_BYPASS;
}
else if (strcmp(res->notifyValue, "STEP") == 0)
{
if (smode == SMODE_BYPASS)
{
bypass = !bypass;
if (BYPASS_TAIL)
{
digitalWrite(pin_tails, bypass);
}
else
{
digitalWrite(pin_tails, !bypass);
}
}
else if (smode == SMODE_TEMPO)
{
xTaskGetInfoResult tres = xTaskGetInfo(xTaskGetId("TASKTEMPO"));
if (tres)
{
String str = "";
str += tres->timerInterval;
Serial.println(str);
}
}
else // smode == SMODE_MOD
{}
}
update_led_status();
}
xMemFree(res);
xTaskNotifyClear(id_);
}
// Triggers every ~4ms
void taskMod(xTaskId id_)
{
uint8_t _val = pgm_read_word_near(get_wavetable_ptr() + (int)ceil(wp));
analogWrite(pin_delay_mod, _val);
if (smode == SMODE_MOD)
analogWrite(pin_rgb_b, map(_val, 0, 255, 0, 64));
wp_inc();
}
void taskDelay(xTaskId id_) {
delay_time = analogRead(pin_delay_time);
if (abs(delay_time - delay_time_prev) < 3)
return;
uint8_t res = map(delay_time, 0, 1023, 0, 255);
long delay_time_us = long(map(delay_time, 0, 1023, 53, 626)) * 1000;
xTaskSetTimer(xTaskGetId("TASKTEMPO"), delay_time_us);
analogWrite(pin_delay_pwm, res);
prev_t = t_ms;
delay_time_prev = delay_time;
}
void taskTempo(xTaskId id_) {
if (smode == SMODE_TEMPO)
analogWrite(pin_rgb_g, 127 * tempo_blink);
tempo_blink = !tempo_blink;
}
void taskSerial(xTaskId id_) {
xTaskGetNotifResult res = xTaskGetNotif(id_);
if (res)
Serial.println(res->notifyValue);
xMemFree(res);
xTaskNotifyClear(id_);
}
/* MAIN FUNCTIONS */
void setup() {
Serial.begin(115200);
Serial.println(F("[VDD Debug Stream]"));
TCCR1B = (TCCR1B & 0b11111000) | 0x01; // Set PWM Frequency to 31kHz
pinMode(pin_footswitch, INPUT_PULLUP);
pinMode(pin_delay_time, INPUT);
pinMode(pin_mod_speed, INPUT);
pinMode(pin_delay_pwm, OUTPUT);
// Initialize RGB LED
pinMode(pin_rgb_r, OUTPUT);
pinMode(pin_rgb_g, OUTPUT);
pinMode(pin_rgb_b, OUTPUT);
pinMode(pin_tails, OUTPUT);
update_led_status();
pinMode (ssMCPin, OUTPUT);
digitalWrite(ssMCPin, HIGH);
digitalWrite(pin_tails, HIGH);
xTaskId id = 0;
xHeliOSSetup();
id = xTaskAdd("TASKBUTTON", &taskButton);
xTaskStart(id);
id = xTaskAdd("TASKMODEMAN", &taskModeMan);
xTaskWait(id);
id = xTaskAdd("TASKMOD", &taskMod);
xTaskWait(id);
xTaskSetTimer(id, 4000); // 4ms
id = xTaskAdd("TASKDELAY", &taskDelay);
xTaskWait(id);
xTaskSetTimer(id, 10000); // 10ms
id = xTaskAdd("TASKTEMPO", &taskTempo);
xTaskWait(id);
delay_time = analogRead(pin_delay_time);
long delay_time_us = long(map(delay_time, 0, 1023, 53, 626)) * 1000;
xTaskSetTimer(id, delay_time_us);
id = xTaskAdd("TASKSERIAL", &taskSerial);
xTaskWait(id);
}
void loop() {
xHeliOSLoop();
}
Any clues as to where this might go wrong?
So one quick thing is to make sure you free the mem for tres:
xTaskGetInfoResult tres = xTaskGetInfo(xTaskGetId("TASKTEMPO"));
So taskTempo() starts to execute irregularly right? Have you tried adding one task at a time to see which task may be the culprit? It may be taskSerial() as serial IO takes a lot of clock ticks.
As far as priority goes. Tasks in a waiting state (event driven) always get priority in the scheduler. Tasks in a started state (cooperative) are not always scheduled for execution each cycle. But it appears you only have one cooperative task - taskButton().
Try adding tasks one by one to see if you can find the culprit and let me know what you find. My apologies for the delay getting back to you. I will keep my eyes open for your response.
@niektb one other thing I noticed is you call xTaskSetTimer() in taskDelay(). Calling xTaskSetTimer() resets the timer.
But it seems you have the below code block to prevent unintended resets.
if (abs(delay_time - delay_time_prev) < 3) return;
Make sure this isn’t causing unintended resets.
@MannyPeterson No probs for the delay! Thanks for coming back to me!
So one quick thing is to make sure you free the mem for tres: xTaskGetInfoResult tres = xTaskGetInfo(xTaskGetId("TASKTEMPO"));
Hmmm, I added the serial.println for debugging in that if-statement. I indeed forgot to free the mem, the issue persists with or without :/
So taskTempo() starts to execute irregularly right? Have you tried adding one task at a time to see which task may be the culprit? It may be taskSerial() as serial IO takes a lot of clock ticks.
Yes indeed! taskSerial isn't called atm (as I removed all debugging print statements via that task, to rule that out :)) it definitely seems to help to reduce the amount of tasks, but couldn't find a particular task that breaks everything :)
As far as priority goes. Tasks in a waiting state (event driven) always get priority in the scheduler. Tasks in a started state (cooperative) are not always scheduled for execution each cycle. But it appears you only have one cooperative task - taskButton().
Try adding tasks one by one to see if you can find the culprit and let me know what you find. My apologies for the delay getting back to you. I will keep my eyes open for your response.
But if multiple tasks are waiting, which tasks get priority? If timed tasks are configured to run at the same interval, which task gets the priority? The one that's started first?
@niektb one other thing I noticed is you call xTaskSetTimer() in taskDelay(). Calling xTaskSetTimer() resets the timer.
But it seems you have the below code block to prevent unintended resets.
if (abs(delay_time - delay_time_prev) < 3) return;
Make sure this isn’t causing unintended resets.
Hmm good point, when I tested it, the ADC reading didn't fluctuate any more than '1', but maybe it's become more noisy since I made other changes in the circuit.
Is there a way to update the timer value without resetting it? That would probably fix the issue as well :)
@niektb going to look at adding a function call to update the timer value. Adding to the development backlog.
@niektb my apologies for the time it has taken to get to this - life distractions unfortunately have kept me away. In the next release there will be a function call that allows the user to set the task timer interval without resetting the start time. You will see it in 0.2.8. Closing this for now. Thanks!
void xTaskSetTimerWOReset(TaskId_t, Time_t);
Cool! So what I'm curious about; what is the behaviour if you set the timer to a time that already is expired? :)
Hi all!
I'm trying this RTOS to write the software for a digital guitar effect pedal but I ran across some issues (other than the docs not being up-to-date) with regards to a dynamically timed task...
First, I have a timed task that runs every 10ms which reads a pot value (between 0 and 1023) and calculates a delay tempo from it. This sets the timer of the next task accordingly.
This "TASKTEMPO" is a very simple task that toggles a LED:
If I only have those two tasks, it works fine. However, when I add a couple more tasks, things start to get a little different. The timing seems still alright but often the task completely skips... Meaning that the LED blinks irregularly... Based on the concept of cooperative scheduling I would assume that the priority is high (as the taskTempo is very short) but it doesn't seem to be the case... Can I somehow assign priorities to tasks or is there a different solution to the issue?