tsandmann / freertos-teensy

FreeRTOS port with C++ std::thread support for ARM boards Teensy 3.5, 3.6, 4.0 and 4.1 (cortex-m4f and cortex-m7f)
92 stars 15 forks source link

Serial UART and SPI don't work together #9

Closed tyalie closed 2 years ago

tyalie commented 2 years ago

As far as I can debug when I use both the Serial UART devices like Serial7 and SPI together, the program will not be able to send stuff over UART when using freertos.

You can find a minimal example here: https://github.com/arandomliz/teensy-freertos-serial-test

tsandmann commented 2 years ago

That's an interesting bug...

First, you can't use (interrupt driven) peripherals before calling vTaskStartScheduler(), because the system initialization isn't complete at this point (that's why the call to Serial7.flush() never finishes). Using the usb serial (Serial) is fine, because it is setup by a startup hook to allow some early debug printing. I think I have to add some documentation about that...

To handle it, you can move the initialization code in a setup task, that is called first, e.g.:

static void setup_task(void *)
{
  pinMode(STO_KNEE, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);

  Serial.begin(9600);

  while (!Serial)
  {
    delay(50);
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  }

#ifdef USE_SPI
  test_spi();
#endif
#ifdef USE_UART
  test_serial();
#endif

  Serial.println("Done all");

#if __has_include("arduino_freertos.h")
  xTaskCreate(led_task, "led_task", 128, nullptr, 2, nullptr);
  vTaskDelete(nullptr);
#endif
}

void setup()
{
#if __has_include("arduino_freertos.h")
  xTaskCreate(setup_task, "setup_task", 1024, nullptr, configMAX_PRIORITIES - 1, nullptr);
  vTaskStartScheduler();
#else
  setup_task(nullptr);
#endif
}

However, there seems to be a second problem: using LED_BUILTIN in digitalWrite() doesn't work when using SPI in the same program. If I change LED_BUILTIN to another pin (not used by SPI), your example works for me. I don't have an explanation for that, except that LED_BUILTIN is shared with SPI SCK. But this problem also exists for me in your without_freertos environment, if I add some code that uses the LED comparable to the FreeRTOS version:

static void setup_task(void *)
{
  pinMode(STO_KNEE, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);

  Serial.begin(9600);

  while (!Serial)
  {
    delay(50);
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  }

#ifdef USE_SPI
  test_spi();
#endif
#ifdef USE_UART
  test_serial();
#endif

  Serial.println("Done all");

#if __has_include("arduino_freertos.h")
  xTaskCreate(led_task, "led_task", 128, nullptr, 2, nullptr);
  vTaskDelete(nullptr);
#else
  while (true)
  {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    if (digitalRead(LED_BUILTIN))
      Serial.println("Hey");
    delay(250);
  }
#endif
}

void setup()
{
#if __has_include("arduino_freertos.h")
  xTaskCreate(setup_task, "setup_task", 1024, nullptr, configMAX_PRIORITIES - 1, nullptr);
  vTaskStartScheduler();
#else
  setup_task(nullptr);
#endif
}
tyalie commented 2 years ago

Thank you so much for your help. I'll try using the setup task. It makes much more sense in many aspects to call all initialization after xTaskStartScheduler. I actually thought that there wouldn't be a solution as freeRTOS also uses interrupts and those are naturally limited in the system. But I think a mention in the docs, wouldn't be that bad 😅

Regarding the BUILTIN_LED: I noticed that too yesterday. The next iteration will put the SCK pin somewhere else than pin 13.

tsandmann commented 2 years ago

Since I don't like that limitation either, I'll see if it can be addressed in a future version. But the setup task workaround should work for most cases.