mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-19 06:15:16 +03:00
It turns out that two calls to micros() and the calculation of directionChangeDurationUs took 581 cycles, vs 396 cycles without the calls to micros() and deferred calculation of the duration which is only needed in the CLI. This brings the time down from around 7 microseconds to 5.5 microseconds on an F3 at 72Mhz. This makes the difference between 100% invalid telemetry and 4% invalid telemetry on the first motor on the F3. Squashed commits: * Remove the forward declaration for `pwmDshotSetDirectionInput` and make it static. * Remove unneeded forward declaration of `motor_DMA_IRQHandler`. * Remove duplication in DMA IRQ Handler. Doesn't affect resulting code but improves readability. * Use an inline function to read DWT->CYCCNT. * Remove unneeded forward declarations from cli.c now that the correct header is included. * Update DWT unlock method.
263 lines
6.4 KiB
C
263 lines
6.4 KiB
C
/*
|
|
* This file is part of Cleanflight and Betaflight.
|
|
*
|
|
* Cleanflight and Betaflight are free software. You can redistribute
|
|
* this software and/or modify this software 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.
|
|
*
|
|
* Cleanflight and Betaflight are distributed in the hope that they
|
|
* 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 software.
|
|
*
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include "platform.h"
|
|
|
|
#include "build/atomic.h"
|
|
|
|
#include "drivers/light_led.h"
|
|
#include "drivers/nvic.h"
|
|
#include "drivers/sound_beeper.h"
|
|
|
|
#include "system.h"
|
|
|
|
#if defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32H7)
|
|
// See "RM CoreSight Architecture Specification"
|
|
// B2.3.10 "LSR and LAR, Software Lock Status Register and Software Lock Access Register"
|
|
// "E1.2.11 LAR, Lock Access Register"
|
|
|
|
#define DWT_LAR_UNLOCK_VALUE 0xC5ACCE55
|
|
|
|
#endif
|
|
|
|
// cycles per microsecond
|
|
static uint32_t usTicks = 0;
|
|
// current uptime for 1kHz systick timer. will rollover after 49 days. hopefully we won't care.
|
|
static volatile uint32_t sysTickUptime = 0;
|
|
static volatile uint32_t sysTickValStamp = 0;
|
|
// cached value of RCC->CSR
|
|
uint32_t cachedRccCsrValue;
|
|
static uint32_t cpuClockFrequency = 0;
|
|
|
|
void cycleCounterInit(void)
|
|
{
|
|
#if defined(USE_HAL_DRIVER)
|
|
cpuClockFrequency = HAL_RCC_GetSysClockFreq();
|
|
#else
|
|
RCC_ClocksTypeDef clocks;
|
|
RCC_GetClocksFreq(&clocks);
|
|
cpuClockFrequency = clocks.SYSCLK_Frequency;
|
|
#endif
|
|
usTicks = cpuClockFrequency / 1000000;
|
|
|
|
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
|
|
|
#if defined(DWT_LAR_UNLOCK_VALUE)
|
|
#if defined(STM32F7) || defined(STM32H7)
|
|
DWT->LAR = DWT_LAR_UNLOCK_VALUE;
|
|
#elif defined(STM32F3) || defined(STM32F4)
|
|
// Note: DWT_Type does not contain LAR member.
|
|
#define DWT_LAR
|
|
__O uint32_t *DWTLAR = (uint32_t *)(DWT_BASE + 0x0FB0);
|
|
*(DWTLAR) = DWT_LAR_UNLOCK_VALUE;
|
|
#endif
|
|
#endif
|
|
|
|
DWT->CYCCNT = 0;
|
|
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
|
}
|
|
|
|
// SysTick
|
|
|
|
static volatile int sysTickPending = 0;
|
|
|
|
void SysTick_Handler(void)
|
|
{
|
|
ATOMIC_BLOCK(NVIC_PRIO_MAX) {
|
|
sysTickUptime++;
|
|
sysTickValStamp = SysTick->VAL;
|
|
sysTickPending = 0;
|
|
(void)(SysTick->CTRL);
|
|
}
|
|
#ifdef USE_HAL_DRIVER
|
|
// used by the HAL for some timekeeping and timeouts, should always be 1ms
|
|
HAL_IncTick();
|
|
#endif
|
|
}
|
|
|
|
// Return system uptime in microseconds (rollover in 70minutes)
|
|
|
|
uint32_t microsISR(void)
|
|
{
|
|
register uint32_t ms, pending, cycle_cnt;
|
|
|
|
ATOMIC_BLOCK(NVIC_PRIO_MAX) {
|
|
cycle_cnt = SysTick->VAL;
|
|
|
|
if (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) {
|
|
// Update pending.
|
|
// Record it for multiple calls within the same rollover period
|
|
// (Will be cleared when serviced).
|
|
// Note that multiple rollovers are not considered.
|
|
|
|
sysTickPending = 1;
|
|
|
|
// Read VAL again to ensure the value is read after the rollover.
|
|
|
|
cycle_cnt = SysTick->VAL;
|
|
}
|
|
|
|
ms = sysTickUptime;
|
|
pending = sysTickPending;
|
|
}
|
|
|
|
return ((ms + pending) * 1000) + (usTicks * 1000 - cycle_cnt) / usTicks;
|
|
}
|
|
|
|
uint32_t micros(void)
|
|
{
|
|
register uint32_t ms, cycle_cnt;
|
|
|
|
// Call microsISR() in interrupt and elevated (non-zero) BASEPRI context
|
|
|
|
if ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) || (__get_BASEPRI())) {
|
|
return microsISR();
|
|
}
|
|
|
|
do {
|
|
ms = sysTickUptime;
|
|
cycle_cnt = SysTick->VAL;
|
|
} while (ms != sysTickUptime || cycle_cnt > sysTickValStamp);
|
|
|
|
return (ms * 1000) + (usTicks * 1000 - cycle_cnt) / usTicks;
|
|
}
|
|
|
|
inline uint32_t getCycleCounter(void)
|
|
{
|
|
return DWT->CYCCNT;
|
|
}
|
|
|
|
uint32_t clockCyclesToMicros(uint32_t clockCycles)
|
|
{
|
|
return clockCycles / usTicks;
|
|
}
|
|
|
|
// Return system uptime in milliseconds (rollover in 49 days)
|
|
uint32_t millis(void)
|
|
{
|
|
return sysTickUptime;
|
|
}
|
|
|
|
#if 1
|
|
void delayMicroseconds(uint32_t us)
|
|
{
|
|
uint32_t now = micros();
|
|
while (micros() - now < us);
|
|
}
|
|
#else
|
|
void delayMicroseconds(uint32_t us)
|
|
{
|
|
uint32_t elapsed = 0;
|
|
uint32_t lastCount = SysTick->VAL;
|
|
|
|
for (;;) {
|
|
register uint32_t current_count = SysTick->VAL;
|
|
uint32_t elapsed_us;
|
|
|
|
// measure the time elapsed since the last time we checked
|
|
elapsed += current_count - lastCount;
|
|
lastCount = current_count;
|
|
|
|
// convert to microseconds
|
|
elapsed_us = elapsed / usTicks;
|
|
if (elapsed_us >= us)
|
|
break;
|
|
|
|
// reduce the delay by the elapsed time
|
|
us -= elapsed_us;
|
|
|
|
// keep fractional microseconds for the next iteration
|
|
elapsed %= usTicks;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void delay(uint32_t ms)
|
|
{
|
|
while (ms--)
|
|
delayMicroseconds(1000);
|
|
}
|
|
|
|
static void indicate(uint8_t count, uint16_t duration)
|
|
{
|
|
if (count) {
|
|
LED1_ON;
|
|
LED0_OFF;
|
|
|
|
while (count--) {
|
|
LED1_TOGGLE;
|
|
LED0_TOGGLE;
|
|
BEEP_ON;
|
|
delay(duration);
|
|
|
|
LED1_TOGGLE;
|
|
LED0_TOGGLE;
|
|
BEEP_OFF;
|
|
delay(duration);
|
|
}
|
|
}
|
|
}
|
|
|
|
void indicateFailure(failureMode_e mode, int codeRepeatsRemaining)
|
|
{
|
|
while (codeRepeatsRemaining--) {
|
|
indicate(WARNING_FLASH_COUNT, WARNING_FLASH_DURATION_MS);
|
|
|
|
delay(WARNING_PAUSE_DURATION_MS);
|
|
|
|
indicate(mode + 1, WARNING_CODE_DURATION_LONG_MS);
|
|
|
|
delay(1000);
|
|
}
|
|
}
|
|
|
|
void failureMode(failureMode_e mode)
|
|
{
|
|
indicateFailure(mode, 10);
|
|
|
|
#ifdef DEBUG
|
|
systemReset();
|
|
#else
|
|
systemResetToBootloader(BOOTLOADER_REQUEST_ROM);
|
|
#endif
|
|
}
|
|
|
|
void initialiseMemorySections(void)
|
|
{
|
|
#ifdef USE_ITCM_RAM
|
|
/* Load functions into ITCM RAM */
|
|
extern uint8_t tcm_code_start;
|
|
extern uint8_t tcm_code_end;
|
|
extern uint8_t tcm_code;
|
|
memcpy(&tcm_code_start, &tcm_code, (size_t) (&tcm_code_end - &tcm_code_start));
|
|
#endif
|
|
|
|
#ifdef USE_FAST_RAM
|
|
/* Load FAST_RAM variable intializers into DTCM RAM */
|
|
extern uint8_t _sfastram_data;
|
|
extern uint8_t _efastram_data;
|
|
extern uint8_t _sfastram_idata;
|
|
memcpy(&_sfastram_data, &_sfastram_idata, (size_t) (&_efastram_data - &_sfastram_data));
|
|
#endif
|
|
}
|