You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
331 lines
9.2 KiB
331 lines
9.2 KiB
/****************************************************************************** |
|
* The MIT License |
|
* |
|
(c) 2017 night_ghost@ykoctpa.ru |
|
|
|
based on: |
|
|
|
* Copyright (c) 2010 Perry Hung. |
|
* |
|
* Permission is hereby granted, free of charge, to any person |
|
* obtaining a copy of this software and associated documentation |
|
* files (the "Software"), to deal in the Software without |
|
* restriction, including without limitation the rights to use, copy, |
|
* modify, merge, publish, distribute, sublicense, and/or sell copies |
|
* of the Software, and to permit persons to whom the Software is |
|
* furnished to do so, subject to the following conditions: |
|
* |
|
* The above copyright notice and this permission notice shall be |
|
* included in all copies or substantial portions of the Software. |
|
* |
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
* SOFTWARE. |
|
*****************************************************************************/ |
|
|
|
/** |
|
* @brief Generic board initialization routines. |
|
* |
|
*/ |
|
|
|
#pragma GCC optimize ("O2") |
|
|
|
#include "boards.h" |
|
#include <usb.h> |
|
|
|
static void setupNVIC(void); |
|
static void enableFPU(void); |
|
static void setupCCM(void); |
|
|
|
|
|
void setupADC(void); |
|
void setupTimers(void); |
|
void usb_init(void); |
|
|
|
|
|
void usb_init(void){ |
|
|
|
|
|
usb_attr_t usb_attr; |
|
usb_open(); |
|
|
|
usb_default_attr(&usb_attr); |
|
usb_attr.preempt_prio = USB_INT_PRIORITY; |
|
usb_attr.sub_prio = 0; |
|
usb_attr.use_present_pin = 1; |
|
usb_attr.present_port = PIN_MAP[BOARD_USB_SENSE].gpio_device; |
|
usb_attr.present_pin = PIN_MAP[BOARD_USB_SENSE].gpio_bit; |
|
|
|
usb_configure(&usb_attr); |
|
|
|
} |
|
|
|
static INLINE void enableFPU(void){ |
|
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) |
|
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); // set CP10 and CP11 Full Access |
|
|
|
/* |
|
FPU_FPCCR_ASPEN_Msk |
|
FPU_FPCCR_LSPEN_Msk |
|
*/ |
|
|
|
#endif |
|
} |
|
|
|
|
|
|
|
|
|
static INLINE void setupCCM(){ |
|
extern unsigned _sccm,_eccm; // defined by link script |
|
/* enabled by startup code |
|
RCC->AHB1ENR |= RCC_AHB1ENR_CCMDATARAMEN; |
|
asm volatile("dsb \n"); |
|
*/ |
|
|
|
|
|
// volatile unsigned *src = &_siccm; // CCM initializers in flash |
|
volatile unsigned *dest = &_sccm; // start of CCM |
|
|
|
#if 0 // no support for initialized data in CCM |
|
|
|
while (dest < &_eccm) { |
|
*dest++ = *src++; |
|
} |
|
#endif |
|
while (dest < &_eccm) { |
|
*dest++ = 0; |
|
} |
|
|
|
// only for stack debugging |
|
#if 0 |
|
uint32_t sp; |
|
|
|
// Get stack pointer |
|
asm volatile ("mov %0, sp\n\t" : "=rm" (sp) ); |
|
|
|
#if 0 // memset is much faster but uses too much stack for own needs |
|
memset((void *)dest,0x55, (sp-(uint32_t)dest) -128); |
|
#else |
|
while ((uint32_t)dest < (sp-8)) { |
|
*dest++ = 0x55555555; // fill stack to check it's usage |
|
} |
|
#endif |
|
#endif |
|
} |
|
|
|
|
|
|
|
static INLINE void setupNVIC() |
|
{ |
|
/* 4 bit preemption, 0 bit subpriority */ |
|
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4); |
|
|
|
exti_init(); |
|
} |
|
|
|
|
|
/* |
|
[..] To enable access to the RTC Domain and RTC registers, proceed as follows: |
|
(+) Enable the Power Controller (PWR) APB1 interface clock using the |
|
RCC_APB1PeriphClockCmd() function. |
|
(+) Enable access to RTC domain using the PWR_BackupAccessCmd() function. |
|
(+) Select the RTC clock source using the RCC_RTCCLKConfig() function. |
|
(+) Enable RTC Clock using the RCC_RTCCLKCmd() function. |
|
*/ |
|
|
|
void board_set_rtc_register(uint32_t sig, uint16_t reg) |
|
{ |
|
PWR->CR |= PWR_CR_DBP; |
|
|
|
RTC_WriteBackupRegister(reg, sig); |
|
|
|
PWR->CR &= ~PWR_CR_DBP; |
|
} |
|
|
|
|
|
uint32_t board_get_rtc_register(uint16_t reg) |
|
{ |
|
// enable the backup registers. |
|
PWR->CR |= PWR_CR_DBP; |
|
|
|
uint32_t ret = RTC_ReadBackupRegister(reg); |
|
|
|
PWR->CR &= ~PWR_CR_DBP; |
|
return ret; |
|
} |
|
|
|
|
|
// 1st executing function |
|
|
|
void inline init(void) { |
|
|
|
// now we can use stack |
|
|
|
// turn on and enable RTC |
|
RCC->APB1ENR |= RCC_APB1ENR_PWREN; |
|
RCC->AHB1ENR |= RCC_AHB1ENR_BKPSRAMEN; |
|
PWR_BackupAccessCmd(ENABLE); |
|
|
|
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); |
|
RCC_RTCCLKCmd(ENABLE); |
|
|
|
// enable the backup registers. |
|
RCC->BDCR |= RCC_BDCR_RTCEN; |
|
|
|
RTC_WriteProtectionCmd(DISABLE); |
|
for(volatile int i=0; i<50; i++); // small delay |
|
// RTC is ready |
|
if(board_get_rtc_register(RTC_SIGNATURE_REG) == DFU_RTC_SIGNATURE) { |
|
board_set_rtc_register(0, RTC_SIGNATURE_REG); |
|
for(volatile int i=0; i<50; i++); // small delay |
|
uint32_t reg=board_get_rtc_register(RTC_SIGNATURE_REG); // read again |
|
if(reg==0) { |
|
goDFU(); // just after reset - so all hardware is in boot state |
|
} |
|
} |
|
|
|
|
|
bool overclock_failed = false; |
|
uint8_t overclock=0; |
|
|
|
uint32_t g = board_get_rtc_register(RTC_OV_GUARD_REG); |
|
|
|
if(g == OV_GUARD_FAIL_SIGNATURE) { |
|
overclock_failed = true; // never reset it to 0 if failed once |
|
} else if(g == OV_GUARD_SIGNATURE) { // overclock fails |
|
overclock_failed = true; // never reset it to 0 if failed once |
|
board_set_rtc_register(OV_GUARD_FAIL_SIGNATURE, RTC_OVERCLOCK_REG); // |
|
} else { |
|
uint32_t sig=board_get_rtc_register(RTC_OVERCLOCK_REG); |
|
if((sig & ~OVERCLOCK_SIG_MASK ) == OVERCLOCK_SIGNATURE) { |
|
board_set_rtc_register(0, RTC_OVERCLOCK_REG); // |
|
overclock = (uint8_t)sig & OVERCLOCK_SIG_MASK; |
|
|
|
if(overclock) { |
|
board_set_rtc_register(OV_GUARD_SIGNATURE, RTC_OV_GUARD_REG); // set guard in case overclock fails |
|
} else { |
|
board_set_rtc_register(0, RTC_OV_GUARD_REG); // clear guard |
|
} |
|
} |
|
} |
|
|
|
|
|
systemInit(overclock); // calls SetSysClock |
|
SystemCoreClockUpdate(); // update SystemCoreClock variable to current frequency |
|
|
|
enableFPU(); |
|
|
|
setupNVIC(); |
|
systick_init(SYSTICK_RELOAD_VAL); |
|
|
|
stopwatch_init(); // will use stopwatch_delay_us() and stopwatch_get_ticks() |
|
|
|
#ifdef DEBUG_BUILD |
|
//*/// enable clock in sleep for debugging |
|
DBGMCU->CR |= DBGMCU_STANDBY | DBGMCU_STOP | DBGMCU_SLEEP; |
|
DBGMCU->APB1FZ |= DBGMCU_TIM4_STOP | DBGMCU_TIM5_STOP | DBGMCU_TIM7_STOP; // stop internal timers |
|
DBGMCU->APB2FZ |= DBGMCU_TIM10_STOP | DBGMCU_TIM11_STOP; |
|
//*/// |
|
#endif |
|
|
|
boardInit(); // board-specific part of init |
|
/* |
|
only CPU init here, all another moved to modules .init() functions |
|
*/ |
|
interrupts(); |
|
|
|
if(!overclock_failed) { |
|
// comes here - all ok, we can clear guard |
|
board_set_rtc_register(0, RTC_OV_GUARD_REG); // |
|
} |
|
|
|
} |
|
|
|
// called with stack in MSP |
|
void pre_init(){ // before any stack usage @NG |
|
setupCCM(); // needs because stack in CCM |
|
|
|
init(); |
|
} |
|
|
|
// частота неправильная и штатными функциями задержки мы не можем пользоваться |
|
void emerg_delay(uint32_t n){ |
|
volatile uint32_t i; |
|
|
|
while(n){ |
|
for (i=4000; i!=0; i--) { // 16MHz, command each tick - ~4MHz or 0.25uS * 4000 = 1ms |
|
asm volatile("nop \n"); |
|
} |
|
n--; |
|
} |
|
} |
|
|
|
|
|
void NMI_Handler() { |
|
|
|
//Очищаем флаг прерывания CSS |
|
RCC->CIR |= RCC_CIR_CSSC; |
|
//Ждем некоторое время после сбоя, если он кратковременный |
|
//Возможно удастся перезапустить |
|
emerg_delay(100); // clock is wrong so all micros() etc lies! |
|
|
|
|
|
//Пытаемся запустить HSE |
|
RCC_HSEConfig(RCC_HSE_ON); |
|
emerg_delay(1); //Задержка на запуск кварца |
|
|
|
|
|
if (RCC_WaitForHSEStartUp() == SUCCESS){ |
|
//Если запустился - проводим установку заново |
|
SetSysClock(0); // without overclocking |
|
|
|
} else { |
|
|
|
// кварц не запустился, переключаемся на HSI и выставляем полную частоту |
|
|
|
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */ |
|
#define PLL_M 8 |
|
#define PLL_N 168 |
|
|
|
/* SYSCLK = PLL_VCO / PLL_P */ |
|
#define PLL_P 2 |
|
|
|
/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */ |
|
#define PLL_Q 7 |
|
|
|
/* Enable high performance mode, System frequency up to 168 MHz */ |
|
RCC->APB1ENR |= RCC_APB1ENR_PWREN; |
|
PWR->CR |= PWR_CR_PMODE; |
|
|
|
/* HCLK = SYSCLK / 1*/ |
|
RCC->CFGR |= RCC_CFGR_HPRE_DIV1; |
|
|
|
/* PCLK2 = HCLK / 2*/ |
|
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; |
|
|
|
/* PCLK1 = HCLK / 4*/ |
|
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; |
|
|
|
/* Configure the main PLL */ |
|
RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) | |
|
(RCC_PLLCFGR_PLLSRC_HSI) | (PLL_Q << 24); |
|
|
|
/* Enable the main PLL */ |
|
RCC->CR |= RCC_CR_PLLON; |
|
|
|
/* Wait till the main PLL is ready */ |
|
while((RCC->CR & RCC_CR_PLLRDY) == 0) { } // TODO: do something on failure? |
|
|
|
/* Select the main PLL as system clock source */ |
|
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); |
|
RCC->CFGR |= RCC_CFGR_SW_PLL; |
|
|
|
SystemCoreClock=168000000; |
|
} |
|
} |
|
|
|
|