123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- /*
- * This file is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Code by Andrew Tridgell and Siddharth Bharat Purohit
- */
- #include <AP_HAL/AP_HAL.h>
- #include "AP_HAL_ChibiOS.h"
- #include "Scheduler.h"
- #include "Util.h"
- #include <AP_HAL_ChibiOS/UARTDriver.h>
- #include <AP_HAL_ChibiOS/AnalogIn.h>
- #include <AP_HAL_ChibiOS/Storage.h>
- #include <AP_HAL_ChibiOS/RCOutput.h>
- #include <AP_HAL_ChibiOS/RCInput.h>
- #include <AP_HAL_ChibiOS/CAN.h>
- #include <AP_InternalError/AP_InternalError.h>
- #if CH_CFG_USE_DYNAMIC == TRUE
- #include <AP_Logger/AP_Logger.h>
- #include <AP_Scheduler/AP_Scheduler.h>
- #include <AP_BoardConfig/AP_BoardConfig.h>
- #include "hwdef/common/stm32_util.h"
- #include "hwdef/common/watchdog.h"
- #include "shared_dma.h"
- #include "sdcard.h"
- #if HAL_WITH_IO_MCU
- #include <AP_IOMCU/AP_IOMCU.h>
- extern AP_IOMCU iomcu;
- #endif
- using namespace ChibiOS;
- extern const AP_HAL::HAL& hal;
- #ifndef HAL_NO_TIMER_THREAD
- THD_WORKING_AREA(_timer_thread_wa, TIMER_THD_WA_SIZE);
- #endif
- #ifndef HAL_NO_RCIN_THREAD
- THD_WORKING_AREA(_rcin_thread_wa, RCIN_THD_WA_SIZE);
- #endif
- #ifndef HAL_USE_EMPTY_IO
- THD_WORKING_AREA(_io_thread_wa, IO_THD_WA_SIZE);
- #endif
- #ifndef HAL_USE_EMPTY_STORAGE
- THD_WORKING_AREA(_storage_thread_wa, STORAGE_THD_WA_SIZE);
- #endif
- #ifndef HAL_NO_MONITOR_THREAD
- THD_WORKING_AREA(_monitor_thread_wa, MONITOR_THD_WA_SIZE);
- #endif
- Scheduler::Scheduler()
- {
- }
- void Scheduler::init()
- {
- chBSemObjectInit(&_timer_semaphore, false);
- chBSemObjectInit(&_io_semaphore, false);
- #ifndef HAL_NO_MONITOR_THREAD
- // setup the monitor thread - this is used to detect software lockups
- _monitor_thread_ctx = chThdCreateStatic(_monitor_thread_wa,
- sizeof(_monitor_thread_wa),
- APM_MONITOR_PRIORITY, /* Initial priority. */
- _monitor_thread, /* Thread function. */
- this); /* Thread parameter. */
- #endif
- #ifndef HAL_NO_TIMER_THREAD
- // setup the timer thread - this will call tasks at 1kHz
- _timer_thread_ctx = chThdCreateStatic(_timer_thread_wa,
- sizeof(_timer_thread_wa),
- APM_TIMER_PRIORITY, /* Initial priority. */
- _timer_thread, /* Thread function. */
- this); /* Thread parameter. */
- #endif
- #ifndef HAL_NO_RCIN_THREAD
- // setup the RCIN thread - this will call tasks at 1kHz
- _rcin_thread_ctx = chThdCreateStatic(_rcin_thread_wa,
- sizeof(_rcin_thread_wa),
- APM_RCIN_PRIORITY, /* Initial priority. */
- _rcin_thread, /* Thread function. */
- this); /* Thread parameter. */
- #endif
- #ifndef HAL_USE_EMPTY_IO
- // the IO thread runs at lower priority
- _io_thread_ctx = chThdCreateStatic(_io_thread_wa,
- sizeof(_io_thread_wa),
- APM_IO_PRIORITY, /* Initial priority. */
- _io_thread, /* Thread function. */
- this); /* Thread parameter. */
- #endif
- #ifndef HAL_USE_EMPTY_STORAGE
- // the storage thread runs at just above IO priority
- _storage_thread_ctx = chThdCreateStatic(_storage_thread_wa,
- sizeof(_storage_thread_wa),
- APM_STORAGE_PRIORITY, /* Initial priority. */
- _storage_thread, /* Thread function. */
- this); /* Thread parameter. */
- #endif
- }
- void Scheduler::delay_microseconds(uint16_t usec)
- {
- if (usec == 0) { //chibios faults with 0us sleep
- return;
- }
- uint32_t ticks;
- ticks = chTimeUS2I(usec);
- if (ticks == 0) {
- // calling with ticks == 0 causes a hard fault on ChibiOS
- ticks = 1;
- }
- chThdSleep(ticks); //Suspends Thread for desired microseconds
- }
- /*
- wrapper around sem_post that boosts main thread priority
- */
- static void set_high_priority()
- {
- #if APM_MAIN_PRIORITY_BOOST != APM_MAIN_PRIORITY
- hal_chibios_set_priority(APM_MAIN_PRIORITY_BOOST);
- #endif
- }
- /*
- return the main thread to normal priority
- */
- void Scheduler::boost_end(void)
- {
- #if APM_MAIN_PRIORITY_BOOST != APM_MAIN_PRIORITY
- if (in_main_thread() && _priority_boosted) {
- _priority_boosted = false;
- hal_chibios_set_priority(APM_MAIN_PRIORITY);
- }
- #endif
- }
- /*
- a variant of delay_microseconds that boosts priority to
- APM_MAIN_PRIORITY_BOOST for APM_MAIN_PRIORITY_BOOST_USEC
- microseconds when the time completes. This significantly improves
- the regularity of timing of the main loop
- */
- void Scheduler::delay_microseconds_boost(uint16_t usec)
- {
- if (!_priority_boosted && in_main_thread()) {
- set_high_priority();
- _priority_boosted = true;
- _called_boost = true;
- }
- delay_microseconds(usec); //Suspends Thread for desired microseconds
- }
- /*
- return true if delay_microseconds_boost() has been called since last check
- */
- bool Scheduler::check_called_boost(void)
- {
- if (!_called_boost) {
- return false;
- }
- _called_boost = false;
- return true;
- }
- void Scheduler::delay(uint16_t ms)
- {
- uint64_t start = AP_HAL::micros64();
- while ((AP_HAL::micros64() - start)/1000 < ms) {
- delay_microseconds(1000);
- if (_min_delay_cb_ms <= ms) {
- if (in_main_thread()) {
- call_delay_cb();
- }
- }
- }
- }
- void Scheduler::register_timer_process(AP_HAL::MemberProc proc)
- {
- chBSemWait(&_timer_semaphore);
- for (uint8_t i = 0; i < _num_timer_procs; i++) {
- if (_timer_proc[i] == proc) {
- chBSemSignal(&_timer_semaphore);
- return;
- }
- }
- if (_num_timer_procs < CHIBIOS_SCHEDULER_MAX_TIMER_PROCS) {
- _timer_proc[_num_timer_procs] = proc;
- _num_timer_procs++;
- } else {
- hal.console->printf("Out of timer processes\n");
- }
- chBSemSignal(&_timer_semaphore);
- }
- void Scheduler::register_io_process(AP_HAL::MemberProc proc)
- {
- chBSemWait(&_io_semaphore);
- for (uint8_t i = 0; i < _num_io_procs; i++) {
- if (_io_proc[i] == proc) {
- chBSemSignal(&_io_semaphore);
- return;
- }
- }
-
- if (_num_io_procs < CHIBIOS_SCHEDULER_MAX_TIMER_PROCS) {
- _io_proc[_num_io_procs] = proc;
- _num_io_procs++;
- } else {
- hal.console->printf("Out of IO processes\n");
- }
- chBSemSignal(&_io_semaphore);
- }
- void Scheduler::register_timer_failsafe(AP_HAL::Proc failsafe, uint32_t period_us)
- {
- _failsafe = failsafe;
- }
- void Scheduler::reboot(bool hold_in_bootloader)
- {
- // disarm motors to ensure they are off during a bootloader upload
- hal.rcout->force_safety_on();
- #if HAL_WITH_IO_MCU
- if (AP_BoardConfig::io_enabled()) {
- iomcu.shutdown();
- }
- #endif
- #ifndef HAL_NO_LOGGING
- //stop logging
- if (AP_Logger::get_singleton()) {
- AP::logger().StopLogging();
- }
- // stop sdcard driver, if active
- sdcard_stop();
- #endif
- #if !defined(NO_FASTBOOT)
- // setup RTC for fast reboot
- set_fast_reboot(hold_in_bootloader?RTC_BOOT_HOLD:RTC_BOOT_FAST);
- #endif
- // disable all interrupt sources
- port_disable();
-
- // reboot
- NVIC_SystemReset();
- }
- void Scheduler::_run_timers()
- {
- if (_in_timer_proc) {
- return;
- }
- _in_timer_proc = true;
- int num_procs = 0;
- chBSemWait(&_timer_semaphore);
- num_procs = _num_timer_procs;
- chBSemSignal(&_timer_semaphore);
- // now call the timer based drivers
- for (int i = 0; i < num_procs; i++) {
- if (_timer_proc[i]) {
- _timer_proc[i]();
- }
- }
- // and the failsafe, if one is setup
- if (_failsafe != nullptr) {
- _failsafe();
- }
- #if HAL_USE_ADC == TRUE && !defined(HAL_DISABLE_ADC_DRIVER)
- // process analog input
- ((AnalogIn *)hal.analogin)->_timer_tick();
- #endif
- _in_timer_proc = false;
- }
- void Scheduler::_timer_thread(void *arg)
- {
- Scheduler *sched = (Scheduler *)arg;
- chRegSetThreadName("apm_timer");
- while (!sched->_hal_initialized) {
- sched->delay_microseconds(1000);
- }
- while (true) {
- sched->delay_microseconds(1000);
- // run registered timers
- sched->_run_timers();
- // process any pending RC output requests
- hal.rcout->timer_tick();
- if (sched->expect_delay_start != 0) {
- uint32_t now = AP_HAL::millis();
- if (now - sched->expect_delay_start <= sched->expect_delay_length) {
- sched->watchdog_pat();
- }
- }
- }
- }
- #ifndef HAL_NO_MONITOR_THREAD
- void Scheduler::_monitor_thread(void *arg)
- {
- Scheduler *sched = (Scheduler *)arg;
- chRegSetThreadName("apm_monitor");
- while (!sched->_initialized) {
- sched->delay(100);
- }
- bool using_watchdog = AP_BoardConfig::watchdog_enabled();
- while (true) {
- sched->delay(100);
- if (using_watchdog) {
- stm32_watchdog_save((uint32_t *)&hal.util->persistent_data, (sizeof(hal.util->persistent_data)+3)/4);
- }
- uint32_t now = AP_HAL::millis();
- uint32_t loop_delay = now - sched->last_watchdog_pat_ms;
- if (loop_delay >= 200) {
- // the main loop has been stuck for at least
- // 200ms. Starting logging the main loop state
- const AP_HAL::Util::PersistentData &pd = hal.util->persistent_data;
- AP::logger().Write("MON", "TimeUS,LDelay,Task,IErr,IErrCnt,MavMsg,MavCmd,SemLine,SPICnt,I2CCnt", "QIbIIHHHII",
- AP_HAL::micros64(),
- loop_delay,
- pd.scheduler_task,
- pd.internal_errors,
- pd.internal_error_count,
- pd.last_mavlink_msgid,
- pd.last_mavlink_cmd,
- pd.semaphore_line,
- pd.spi_count,
- pd.i2c_count);
- }
- if (loop_delay >= 500) {
- // at 500ms we declare an internal error
- AP::internalerror().error(AP_InternalError::error_t::main_loop_stuck);
- }
- }
- }
- #endif // HAL_NO_MONITOR_THREAD
- void Scheduler::_rcin_thread(void *arg)
- {
- Scheduler *sched = (Scheduler *)arg;
- chRegSetThreadName("apm_rcin");
- while (!sched->_hal_initialized) {
- sched->delay_microseconds(20000);
- }
- while (true) {
- sched->delay_microseconds(1000);
- ((RCInput *)hal.rcin)->_timer_tick();
- }
- }
- void Scheduler::_run_io(void)
- {
- if (_in_io_proc) {
- return;
- }
- _in_io_proc = true;
- int num_procs = 0;
- chBSemWait(&_io_semaphore);
- num_procs = _num_io_procs;
- chBSemSignal(&_io_semaphore);
- // now call the IO based drivers
- for (int i = 0; i < num_procs; i++) {
- if (_io_proc[i]) {
- _io_proc[i]();
- }
- }
- _in_io_proc = false;
- }
- void Scheduler::_io_thread(void* arg)
- {
- Scheduler *sched = (Scheduler *)arg;
- chRegSetThreadName("apm_io");
- while (!sched->_hal_initialized) {
- sched->delay_microseconds(1000);
- }
- uint32_t last_sd_start_ms = AP_HAL::millis();
- while (true) {
- sched->delay_microseconds(1000);
- // run registered IO processes
- sched->_run_io();
- if (!hal.util->get_soft_armed()) {
- // if sdcard hasn't mounted then retry it every 3s in the IO
- // thread when disarmed
- uint32_t now = AP_HAL::millis();
- if (now - last_sd_start_ms > 3000) {
- last_sd_start_ms = now;
- sdcard_retry();
- }
- }
- }
- }
- void Scheduler::_storage_thread(void* arg)
- {
- Scheduler *sched = (Scheduler *)arg;
- chRegSetThreadName("apm_storage");
- while (!sched->_hal_initialized) {
- sched->delay_microseconds(10000);
- }
- while (true) {
- sched->delay_microseconds(10000);
- // process any pending storage writes
- hal.storage->_timer_tick();
- }
- }
- void Scheduler::system_initialized()
- {
- if (_initialized) {
- AP_HAL::panic("PANIC: scheduler::system_initialized called"
- "more than once");
- }
- _initialized = true;
- }
- /*
- disable interrupts and return a context that can be used to
- restore the interrupt state. This can be used to protect
- critical regions
- */
- void *Scheduler::disable_interrupts_save(void)
- {
- return (void *)(uintptr_t)chSysGetStatusAndLockX();
- }
- /*
- restore interrupt state from disable_interrupts_save()
- */
- void Scheduler::restore_interrupts(void *state)
- {
- chSysRestoreStatusX((syssts_t)(uintptr_t)state);
- }
- /*
- trampoline for thread create
- */
- void Scheduler::thread_create_trampoline(void *ctx)
- {
- AP_HAL::MemberProc *t = (AP_HAL::MemberProc *)ctx;
- (*t)();
- free(t);
- }
- /*
- create a new thread
- */
- bool Scheduler::thread_create(AP_HAL::MemberProc proc, const char *name, uint32_t stack_size, priority_base base, int8_t priority)
- {
- // take a copy of the MemberProc, it is freed after thread exits
- AP_HAL::MemberProc *tproc = (AP_HAL::MemberProc *)malloc(sizeof(proc));
- if (!tproc) {
- return false;
- }
- *tproc = proc;
- uint8_t thread_priority = APM_IO_PRIORITY;
- static const struct {
- priority_base base;
- uint8_t p;
- } priority_map[] = {
- { PRIORITY_BOOST, APM_MAIN_PRIORITY_BOOST},
- { PRIORITY_MAIN, APM_MAIN_PRIORITY},
- { PRIORITY_SPI, APM_SPI_PRIORITY},
- { PRIORITY_I2C, APM_I2C_PRIORITY},
- { PRIORITY_CAN, APM_CAN_PRIORITY},
- { PRIORITY_TIMER, APM_TIMER_PRIORITY},
- { PRIORITY_RCIN, APM_RCIN_PRIORITY},
- { PRIORITY_IO, APM_IO_PRIORITY},
- { PRIORITY_UART, APM_UART_PRIORITY},
- { PRIORITY_STORAGE, APM_STORAGE_PRIORITY},
- { PRIORITY_SCRIPTING, APM_SCRIPTING_PRIORITY},
- };
- for (uint8_t i=0; i<ARRAY_SIZE(priority_map); i++) {
- if (priority_map[i].base == base) {
- thread_priority = constrain_int16(priority_map[i].p + priority, LOWPRIO, HIGHPRIO);
- break;
- }
- }
- thread_t *thread_ctx = thread_create_alloc(THD_WORKING_AREA_SIZE(stack_size),
- name,
- thread_priority,
- thread_create_trampoline,
- tproc);
- if (thread_ctx == nullptr) {
- free(tproc);
- return false;
- }
- return true;
- }
- /*
- inform the scheduler that we are calling an operation from the
- main thread that may take an extended amount of time. This can
- be used to prevent watchdog reset during expected long delays
- A value of zero cancels the previous expected delay
- */
- void Scheduler::expect_delay_ms(uint32_t ms)
- {
- if (!in_main_thread()) {
- // only for main thread
- return;
- }
- if (ms == 0) {
- if (expect_delay_nesting > 0) {
- expect_delay_nesting--;
- }
- if (expect_delay_nesting == 0) {
- expect_delay_start = 0;
- }
- } else {
- uint32_t now = AP_HAL::millis();
- if (expect_delay_start != 0) {
- // we already have a delay running, possibly extend it
- uint32_t done = now - expect_delay_start;
- if (expect_delay_length > done) {
- ms = MAX(ms, expect_delay_length - done);
- }
- }
- expect_delay_start = now;
- expect_delay_length = ms;
- expect_delay_nesting++;
- // also put our priority below timer thread if we are boosted
- boost_end();
- }
- }
- // pat the watchdog
- void Scheduler::watchdog_pat(void)
- {
- stm32_watchdog_pat();
- last_watchdog_pat_ms = AP_HAL::millis();
- }
- #endif // CH_CFG_USE_DYNAMIC
|