Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • avn/swd/miosix-kernel
  • emilio.corigliano/miosix-kernel
2 results
Select Git revision
Show changes
Showing
with 2080 additions and 157 deletions
...@@ -99,20 +99,20 @@ void __attribute__((noinline)) HardFault_impl() ...@@ -99,20 +99,20 @@ void __attribute__((noinline)) HardFault_impl()
#ifdef WITH_ERRLOG #ifdef WITH_ERRLOG
IRQerrorLog("\r\n***Unexpected HardFault @ "); IRQerrorLog("\r\n***Unexpected HardFault @ ");
printUnsignedInt(getProgramCounter()); printUnsignedInt(getProgramCounter());
#ifndef _ARCH_CORTEXM0 #if !defined(_ARCH_CORTEXM0_STM32F0) && !defined(_ARCH_CORTEXM0_STM32G0) && !defined(_ARCH_CORTEXM0_STM32L0)
unsigned int hfsr=SCB->HFSR; unsigned int hfsr=SCB->HFSR;
if(hfsr & 0x40000000) //SCB_HFSR_FORCED if(hfsr & 0x40000000) //SCB_HFSR_FORCED
IRQerrorLog("Fault escalation occurred\r\n"); IRQerrorLog("Fault escalation occurred\r\n");
if(hfsr & 0x00000002) //SCB_HFSR_VECTTBL if(hfsr & 0x00000002) //SCB_HFSR_VECTTBL
IRQerrorLog("A BusFault occurred during a vector table read\r\n"); IRQerrorLog("A BusFault occurred during a vector table read\r\n");
#endif //_ARCH_CORTEXM0 #endif // !_ARCH_CORTEXM0_STM32F0 && !_ARCH_CORTEXM0_STM32G0 && !_ARCH_CORTEXM0_STM32L0
#endif //WITH_ERRLOG #endif //WITH_ERRLOG
miosix_private::IRQsystemReboot(); miosix_private::IRQsystemReboot();
} }
// Cortex M0/M0+ architecture does not have the interrupts handled by code // Cortex M0/M0+ architecture does not have the interrupts handled by code
// below this point // below this point
#ifndef _ARCH_CORTEXM0 #if !defined(_ARCH_CORTEXM0_STM32F0) && !defined(_ARCH_CORTEXM0_STM32G0) && !defined(_ARCH_CORTEXM0_STM32L0)
void __attribute__((naked)) MemManage_Handler() void __attribute__((naked)) MemManage_Handler()
{ {
...@@ -262,7 +262,7 @@ void DebugMon_Handler() ...@@ -262,7 +262,7 @@ void DebugMon_Handler()
miosix_private::IRQsystemReboot(); miosix_private::IRQsystemReboot();
} }
#endif //_ARCH_CORTEXM0 #endif // !_ARCH_CORTEXM0_STM32F0 && !_ARCH_CORTEXM0_STM32G0 && !_ARCH_CORTEXM0_STM32L0
void PendSV_Handler() void PendSV_Handler()
{ {
......
/***************************************************************************
* Copyright (C) 2021 by Terraneo Federico *
* *
* This program 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 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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. *
* *
* As a special exception, if other files instantiate templates or use *
* macros or inline functions from this file, or you compile this file *
* and link it with other works to produce a work based on this file, *
* this file does not by itself cause the resulting work to be covered *
* by the GNU General Public License. However the source code for this *
* file must still be made available in accordance with the GNU General *
* Public License. This exception does not invalidate any other reasons *
* why a work based on this file might be covered by the GNU General *
* Public License. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#include "kernel/kernel.h"
#include "interfaces/os_timer.h"
#include "interfaces/arch_registers.h"
namespace miosix {
class STM32Timer2 : public TimerAdapter<STM32Timer2, 32>
{
public:
static inline unsigned int IRQgetTimerCounter() { return TIM2->CNT; }
static inline void IRQsetTimerCounter(unsigned int v) { TIM2->CNT=v; }
static inline unsigned int IRQgetTimerMatchReg() { return TIM2->CCR1; }
static inline void IRQsetTimerMatchReg(unsigned int v) { TIM2->CCR1=v; }
static inline bool IRQgetOverflowFlag() { return TIM2->SR & TIM_SR_UIF; }
static inline void IRQclearOverflowFlag() { TIM2->SR = ~TIM_SR_UIF; }
static inline bool IRQgetMatchFlag() { return TIM2->SR & TIM_SR_CC1IF; }
static inline void IRQclearMatchFlag() { TIM2->SR = ~TIM_SR_CC1IF; }
static inline void IRQforcePendingIrq() { NVIC_SetPendingIRQ(TIM2_IRQn); }
static inline void IRQstopTimer() { TIM2->CR1 &= ~TIM_CR1_CEN; }
static inline void IRQstartTimer() { TIM2->CR1 |= TIM_CR1_CEN; }
static unsigned int IRQTimerFrequency()
{
// The global variable SystemCoreClock from ARM's CMSIS allows to know
// the CPU frequency.
unsigned int result=SystemCoreClock;
// The timer frequency may however be a submultiple of the CPU frequency,
// due to the bus at whch the periheral is connected being slower. The
// RCC->CFGR register tells us how slower the APB1 bus is running.
// This formula takes into account that if the APB1 clock is divided by a
// factor of two or greater, the timer is clocked at twice the bus
// interface. After this, the freq variable contains the frequency in Hz
// at which the timer prescaler is clocked.
#if _ARCH_CORTEXM0_STM32F0
if(RCC->CFGR & RCC_CFGR_PPRE_2) result/=1<<((RCC->CFGR>>8) & 0x3);
#else
if(RCC->CFGR & RCC_CFGR_PPRE1_2) result/=1<<((RCC->CFGR>>8) & 0x3);
#endif
return result;
}
static void IRQinitTimer()
{
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
RCC_SYNC();
DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_TIM2_STOP; //Stop while debugging
// Setup TIM2 base configuration
// Mode: Up-counter
// Interrupts: counter overflow, Compare/Capture on channel 1
TIM2->CR1=TIM_CR1_URS;
TIM2->DIER=TIM_DIER_UIE | TIM_DIER_CC1IE;
NVIC_SetPriority(TIM2_IRQn,3); //High priority for TIM2 (Max=0, min=15)
NVIC_EnableIRQ(TIM2_IRQn);
// Configure channel 1 as:
// Output channel (CC1S=0)
// No preload(OC1PE=0), hence TIM2_CCR1 can be written at anytime
// No effect on the output signal on match (OC1M = 0)
TIM2->CCMR1 = 0;
TIM2->CCR1 = 0;
// TIM2 Operation Frequency Configuration: Max Freq. and longest period
TIM2->PSC = 0;
TIM2->ARR = 0xFFFFFFFF;
TIM2->EGR = TIM_EGR_UG; //To enforce the timer to apply PSC
}
};
static STM32Timer2 timer;
DEFAULT_OS_TIMER_INTERFACE_IMPLMENTATION(timer);
} //namespace miosix
void __attribute__((naked)) TIM2_IRQHandler()
{
saveContext();
asm volatile ("bl _Z11osTimerImplv");
restoreContext();
}
void __attribute__((used)) osTimerImpl()
{
miosix::timer.IRQhandler();
}
/***************************************************************************
* Copyright (C) 2021 by Terraneo Federico *
* *
* This program 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 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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. *
* *
* As a special exception, if other files instantiate templates or use *
* macros or inline functions from this file, or you compile this file *
* and link it with other works to produce a work based on this file, *
* this file does not by itself cause the resulting work to be covered *
* by the GNU General Public License. However the source code for this *
* file must still be made available in accordance with the GNU General *
* Public License. This exception does not invalidate any other reasons *
* why a work based on this file might be covered by the GNU General *
* Public License. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#include "kernel/kernel.h"
#include "interfaces/os_timer.h"
#include "interfaces/arch_registers.h"
#include "kernel/logging.h"
namespace miosix {
class STM32Timer4 : public TimerAdapter<STM32Timer4, 16>
{
public:
static inline unsigned int IRQgetTimerCounter() { return TIM4->CNT; }
static inline void IRQsetTimerCounter(unsigned int v) { TIM4->CNT=v; }
static inline unsigned int IRQgetTimerMatchReg() { return TIM4->CCR1; }
static inline void IRQsetTimerMatchReg(unsigned int v) { TIM4->CCR1=v; }
static inline bool IRQgetOverflowFlag() { return TIM4->SR & TIM_SR_UIF; }
static inline void IRQclearOverflowFlag() { TIM4->SR = ~TIM_SR_UIF; }
static inline bool IRQgetMatchFlag() { return TIM4->SR & TIM_SR_CC1IF; }
static inline void IRQclearMatchFlag() { TIM4->SR = ~TIM_SR_CC1IF; }
static inline void IRQforcePendingIrq() { NVIC_SetPendingIRQ(TIM4_IRQn); }
static inline void IRQstopTimer() { TIM4->CR1 &= ~TIM_CR1_CEN; }
static inline void IRQstartTimer() { TIM4->CR1 |= TIM_CR1_CEN; }
/*
* These microcontrollers unfortunately do not have a 32bit timer.
* 16bit timers overflow too frequently if clocked at the maximum speed,
* and this is bad both because it nullifies the gains of a tickless kernel
* and because if interrupts are disabled for an entire timer period the
* OS loses the knowledge of time. For this reason, we set the timer clock
* to a lower value using the prescaler.
*/
static const int timerFrequency=1000000; //1MHz
static unsigned int IRQTimerFrequency()
{
return timerFrequency;
}
static void IRQinitTimer()
{
RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;
RCC_SYNC();
#ifndef _ARCH_CORTEXM3_STM32L1
DBGMCU->CR |= DBGMCU_CR_DBG_TIM4_STOP; //Stop while debugging
#else
DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_TIM4_STOP; //Stop while debugging
#endif
// Setup TIM4 base configuration
// Mode: Up-counter
// Interrupts: counter overflow, Compare/Capture on channel 1
TIM4->CR1=TIM_CR1_URS;
TIM4->DIER=TIM_DIER_UIE | TIM_DIER_CC1IE;
NVIC_SetPriority(TIM4_IRQn,3); //High priority for TIM4 (Max=0, min=15)
NVIC_EnableIRQ(TIM4_IRQn);
// Configure channel 1 as:
// Output channel (CC1S=0)
// No preload(OC1PE=0), hence TIM4_CCR1 can be written at anytime
// No effect on the output signal on match (OC1M = 0)
TIM4->CCMR1 = 0;
TIM4->CCR1 = 0;
// TIM4 Operation Frequency Configuration: Max Freq. and longest period
// The global variable SystemCoreClock from ARM's CMSIS allows to know
// the CPU frequency.
unsigned int timerInputFreq=SystemCoreClock;
// The timer frequency may however be a submultiple of the CPU frequency,
// due to the bus at whch the periheral is connected being slower. The
// RCC->CFGR register tells us how slower the APB1 bus is running.
// This formula takes into account that if the APB1 clock is divided by a
// factor of two or greater, the timer is clocked at twice the bus
// interface. After this, the freq variable contains the frequency in Hz
// at which the timer prescaler is clocked.
if(RCC->CFGR & RCC_CFGR_PPRE1_2) timerInputFreq/=1<<((RCC->CFGR>>8) & 0x3);
//Handle the case when the prescribed timer frequency is not achievable.
//For now, we just enter an infinite loop so if someone selects an
//impossible frequency it won't go unnoticed during testing
if(timerInputFreq % timerFrequency)
{
IRQbootlog("Frequency error\r\n");
for(;;) ;
}
TIM4->PSC = (timerInputFreq/timerFrequency)-1;
TIM4->ARR = 0xFFFF;
TIM4->EGR = TIM_EGR_UG; //To enforce the timer to apply PSC
}
};
static STM32Timer4 timer;
DEFAULT_OS_TIMER_INTERFACE_IMPLMENTATION(timer);
} //namespace miosix
void __attribute__((naked)) TIM4_IRQHandler()
{
saveContext();
asm volatile ("bl _Z11osTimerImplv");
restoreContext();
}
void __attribute__((used)) osTimerImpl()
{
miosix::timer.IRQhandler();
}
/***************************************************************************
* Copyright (C) 2021 by Terraneo Federico *
* *
* This program 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 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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. *
* *
* As a special exception, if other files instantiate templates or use *
* macros or inline functions from this file, or you compile this file *
* and link it with other works to produce a work based on this file, *
* this file does not by itself cause the resulting work to be covered *
* by the GNU General Public License. However the source code for this *
* file must still be made available in accordance with the GNU General *
* Public License. This exception does not invalidate any other reasons *
* why a work based on this file might be covered by the GNU General *
* Public License. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#include "kernel/kernel.h"
#include "interfaces/os_timer.h"
#include "interfaces/arch_registers.h"
namespace miosix {
class STM32Timer5 : public TimerAdapter<STM32Timer5, 32>
{
public:
static inline unsigned int IRQgetTimerCounter() { return TIM5->CNT; }
static inline void IRQsetTimerCounter(unsigned int v) { TIM5->CNT=v; }
static inline unsigned int IRQgetTimerMatchReg() { return TIM5->CCR1; }
static inline void IRQsetTimerMatchReg(unsigned int v) { TIM5->CCR1=v; }
static inline bool IRQgetOverflowFlag() { return TIM5->SR & TIM_SR_UIF; }
static inline void IRQclearOverflowFlag() { TIM5->SR = ~TIM_SR_UIF; }
static inline bool IRQgetMatchFlag() { return TIM5->SR & TIM_SR_CC1IF; }
static inline void IRQclearMatchFlag() { TIM5->SR = ~TIM_SR_CC1IF; }
static inline void IRQforcePendingIrq() { NVIC_SetPendingIRQ(TIM5_IRQn); }
static inline void IRQstopTimer() { TIM5->CR1 &= ~TIM_CR1_CEN; }
static inline void IRQstartTimer() { TIM5->CR1 |= TIM_CR1_CEN; }
static unsigned int IRQTimerFrequency()
{
// The global variable SystemCoreClock from ARM's CMSIS allows to know
// the CPU frequency.
unsigned int result=SystemCoreClock;
// The timer frequency may however be a submultiple of the CPU frequency,
// due to the bus at whch the periheral is connected being slower. The
// RCC->CFGR register tells us how slower the APB1 bus is running.
// This formula takes into account that if the APB1 clock is divided by a
// factor of two or greater, the timer is clocked at twice the bus
// interface. After this, the freq variable contains the frequency in Hz
// at which the timer prescaler is clocked.
#if defined(_ARCH_CORTEXM7_STM32H7)
if(RCC->D2CFGR & RCC_D2CFGR_D2PPRE1_2) result/=1<<((RCC->D2CFGR>>4) & 0x3);
#else
if(RCC->CFGR & RCC_CFGR_PPRE1_2) result/=1<<((RCC->CFGR>>10) & 0x3);
#endif
return result;
}
static void IRQinitTimer()
{
#if defined(_ARCH_CORTEXM7_STM32H7)
RCC->APB1LENR |= RCC_APB1LENR_TIM5EN;
RCC_SYNC();
DBGMCU->APB1LFZ1 |= DBGMCU_APB1LFZ1_DBG_TIM5; //Stop while debugging
#elif defined(_ARCH_CORTEXM4_STM32L4)
RCC->APB1ENR1 |= RCC_APB1ENR1_TIM5EN;
RCC_SYNC();
DBGMCU->APB1FZR1 |= DBGMCU_APB1FZR1_DBG_TIM5_STOP; //Stop while debugging
#else
RCC->APB1ENR |= RCC_APB1ENR_TIM5EN;
RCC_SYNC();
DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_TIM5_STOP; //Stop while debugging
#endif
// Setup TIM5 base configuration
// Mode: Up-counter
// Interrupts: counter overflow, Compare/Capture on channel 1
TIM5->CR1=TIM_CR1_URS;
TIM5->DIER=TIM_DIER_UIE | TIM_DIER_CC1IE;
NVIC_SetPriority(TIM5_IRQn,3); //High priority for TIM5 (Max=0, min=15)
NVIC_EnableIRQ(TIM5_IRQn);
// Configure channel 1 as:
// Output channel (CC1S=0)
// No preload(OC1PE=0), hence TIM5_CCR1 can be written at anytime
// No effect on the output signal on match (OC1M = 0)
TIM5->CCMR1 = 0;
TIM5->CCR1 = 0;
// TIM5 Operation Frequency Configuration: Max Freq. and longest period
TIM5->PSC = 0;
TIM5->ARR = 0xFFFFFFFF;
TIM5->EGR = TIM_EGR_UG; //To enforce the timer to apply PSC
}
};
static STM32Timer5 timer;
DEFAULT_OS_TIMER_INTERFACE_IMPLMENTATION(timer);
} //namespace miosix
void __attribute__((naked)) TIM5_IRQHandler()
{
saveContext();
asm volatile ("bl _Z11osTimerImplv");
restoreContext();
}
void __attribute__((used)) osTimerImpl()
{
miosix::timer.IRQhandler();
}
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/> * * along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/ ***************************************************************************/
#include "sd_stm32f2_f4.h" #include "sd_stm32f2_f4_f7.h"
#include "interfaces/bsp.h" #include "interfaces/bsp.h"
#include "interfaces/arch_registers.h" #include "interfaces/arch_registers.h"
#include "core/cache_cortexMx.h" #include "core/cache_cortexMx.h"
...@@ -52,9 +52,17 @@ ...@@ -52,9 +52,17 @@
*/ */
#if defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7) #if defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)
#if SD_SDMMC==1
#define SDIO SDMMC1 #define SDIO SDMMC1
#define RCC_APB2ENR_SDIOEN RCC_APB2ENR_SDMMC1EN #define RCC_APB2ENR_SDIOEN RCC_APB2ENR_SDMMC1EN
#define SDIO_IRQn SDMMC1_IRQn #define SDIO_IRQn SDMMC1_IRQn
#elif SD_SDMMC==2
#define SDIO SDMMC2
#define RCC_APB2ENR_SDIOEN RCC_APB2ENR_SDMMC2EN
#define SDIO_IRQn SDMMC2_IRQn
#else
#error SD_SDMMC undefined or not in range
#endif
#define SDIO_STA_STBITERR 0 //This bit has been removed #define SDIO_STA_STBITERR 0 //This bit has been removed
#define SDIO_STA_RXOVERR SDMMC_STA_RXOVERR #define SDIO_STA_RXOVERR SDMMC_STA_RXOVERR
...@@ -76,6 +84,7 @@ ...@@ -76,6 +84,7 @@
#define SDIO_CLKCR_CLKEN SDMMC_CLKCR_CLKEN #define SDIO_CLKCR_CLKEN SDMMC_CLKCR_CLKEN
#define SDIO_CLKCR_PWRSAV SDMMC_CLKCR_PWRSAV #define SDIO_CLKCR_PWRSAV SDMMC_CLKCR_PWRSAV
#define SDIO_CLKCR_PWRSAV SDMMC_CLKCR_PWRSAV #define SDIO_CLKCR_PWRSAV SDMMC_CLKCR_PWRSAV
#define SDIO_CLKCR_WIDBUS_0 SDMMC_CLKCR_WIDBUS_0
#define SDIO_MASK_STBITERRIE 0 //This bit has been removed #define SDIO_MASK_STBITERRIE 0 //This bit has been removed
#define SDIO_MASK_RXOVERRIE SDMMC_MASK_RXOVERRIE #define SDIO_MASK_RXOVERRIE SDMMC_MASK_RXOVERRIE
...@@ -91,16 +100,32 @@ ...@@ -91,16 +100,32 @@
#define SDIO_POWER_PWRCTRL_1 SDMMC_POWER_PWRCTRL_1 #define SDIO_POWER_PWRCTRL_1 SDMMC_POWER_PWRCTRL_1
#define SDIO_POWER_PWRCTRL_0 SDMMC_POWER_PWRCTRL_0 #define SDIO_POWER_PWRCTRL_0 SDMMC_POWER_PWRCTRL_0
constexpr int ICR_FLAGS_CLR=0x5ff;
#else //defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)
constexpr int ICR_FLAGS_CLR=0x7ff;
#endif //defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7) #endif //defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)
#if (defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)) && SD_SDMMC==2
#define DMA_Stream DMA2_Stream0
#else
#define DMA_Stream DMA2_Stream3
#endif
/** /**
* \internal * \internal
* DMA2 Stream3 interrupt handler * DMA2 Stream interrupt handler
*/ */
#if (defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)) && SD_SDMMC==2
void __attribute__((naked)) DMA2_Stream0_IRQHandler()
#else
void __attribute__((naked)) DMA2_Stream3_IRQHandler() void __attribute__((naked)) DMA2_Stream3_IRQHandler()
#endif
{ {
saveContext(); saveContext();
asm volatile("bl _ZN6miosix18DMA2stream3irqImplEv"); asm volatile("bl _ZN6miosix12SDDMAirqImplEv");
restoreContext(); restoreContext();
} }
...@@ -108,14 +133,16 @@ void __attribute__((naked)) DMA2_Stream3_IRQHandler() ...@@ -108,14 +133,16 @@ void __attribute__((naked)) DMA2_Stream3_IRQHandler()
* \internal * \internal
* SDIO interrupt handler * SDIO interrupt handler
*/ */
#if defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7) #if (defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)) && SD_SDMMC==1
void __attribute__((naked)) SDMMC1_IRQHandler() void __attribute__((naked)) SDMMC1_IRQHandler()
#elif (defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)) && SD_SDMMC==2
void __attribute__((naked)) SDMMC2_IRQHandler()
#else //stm32f2 and stm32f4 #else //stm32f2 and stm32f4
void __attribute__((naked)) SDIO_IRQHandler() void __attribute__((naked)) SDIO_IRQHandler()
#endif #endif
{ {
saveContext(); saveContext();
asm volatile("bl _ZN6miosix11SDIOirqImplEv"); asm volatile("bl _ZN6miosix9SDirqImplEv");
restoreContext(); restoreContext();
} }
...@@ -130,16 +157,26 @@ static unsigned int sdioFlags; ///< \internal SDIO status flags ...@@ -130,16 +157,26 @@ static unsigned int sdioFlags; ///< \internal SDIO status flags
* \internal * \internal
* DMA2 Stream3 interrupt handler actual implementation * DMA2 Stream3 interrupt handler actual implementation
*/ */
void __attribute__((used)) DMA2stream3irqImpl() void __attribute__((used)) SDDMAirqImpl()
{ {
dmaFlags=DMA2->LISR; dmaFlags=DMA2->LISR;
#if (defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)) && SD_SDMMC==2
if(dmaFlags & (DMA_LISR_TEIF0 | DMA_LISR_DMEIF0 | DMA_LISR_FEIF0))
transferError=true;
DMA2->LIFCR = DMA_LIFCR_CTCIF0
| DMA_LIFCR_CTEIF0
| DMA_LIFCR_CDMEIF0
| DMA_LIFCR_CFEIF0;
#else
if(dmaFlags & (DMA_LISR_TEIF3 | DMA_LISR_DMEIF3 | DMA_LISR_FEIF3)) if(dmaFlags & (DMA_LISR_TEIF3 | DMA_LISR_DMEIF3 | DMA_LISR_FEIF3))
transferError=true; transferError=true;
DMA2->LIFCR=DMA_LIFCR_CTCIF3 | DMA2->LIFCR = DMA_LIFCR_CTCIF3
DMA_LIFCR_CTEIF3 | | DMA_LIFCR_CTEIF3
DMA_LIFCR_CDMEIF3 | | DMA_LIFCR_CDMEIF3
DMA_LIFCR_CFEIF3; | DMA_LIFCR_CFEIF3;
#endif
if(!waiting) return; if(!waiting) return;
waiting->IRQwakeup(); waiting->IRQwakeup();
...@@ -152,14 +189,14 @@ void __attribute__((used)) DMA2stream3irqImpl() ...@@ -152,14 +189,14 @@ void __attribute__((used)) DMA2stream3irqImpl()
* \internal * \internal
* DMA2 Stream3 interrupt handler actual implementation * DMA2 Stream3 interrupt handler actual implementation
*/ */
void __attribute__((used)) SDIOirqImpl() void __attribute__((used)) SDirqImpl()
{ {
sdioFlags=SDIO->STA; sdioFlags=SDIO->STA;
if(sdioFlags & (SDIO_STA_STBITERR | SDIO_STA_RXOVERR | if(sdioFlags & (SDIO_STA_STBITERR | SDIO_STA_RXOVERR |
SDIO_STA_TXUNDERR | SDIO_STA_DTIMEOUT | SDIO_STA_DCRCFAIL)) SDIO_STA_TXUNDERR | SDIO_STA_DTIMEOUT | SDIO_STA_DCRCFAIL))
transferError=true; transferError=true;
SDIO->ICR=0x7ff;//Clear flags SDIO->ICR=ICR_FLAGS_CLR; //Clear flags
if(!waiting) return; if(!waiting) return;
waiting->IRQwakeup(); waiting->IRQwakeup();
...@@ -193,12 +230,22 @@ enum CardType ...@@ -193,12 +230,22 @@ enum CardType
static CardType cardType=Invalid; static CardType cardType=Invalid;
//SD card GPIOs //SD card GPIOs
#if (defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)) && SD_SDMMC==2
typedef Gpio<GPIOG_BASE,9> sdD0;
typedef Gpio<GPIOG_BASE,10> sdD1;
typedef Gpio<GPIOB_BASE,3> sdD2;
typedef Gpio<GPIOB_BASE,4> sdD3;
typedef Gpio<GPIOD_BASE,6> sdCLK;
typedef Gpio<GPIOD_BASE,7> sdCMD;
#else
typedef Gpio<GPIOC_BASE,8> sdD0; typedef Gpio<GPIOC_BASE,8> sdD0;
typedef Gpio<GPIOC_BASE,9> sdD1; typedef Gpio<GPIOC_BASE,9> sdD1;
typedef Gpio<GPIOC_BASE,10> sdD2; typedef Gpio<GPIOC_BASE,10> sdD2;
typedef Gpio<GPIOC_BASE,11> sdD3; typedef Gpio<GPIOC_BASE,11> sdD3;
typedef Gpio<GPIOC_BASE,12> sdCLK; typedef Gpio<GPIOC_BASE,12> sdCLK;
typedef Gpio<GPIOD_BASE,2> sdCMD; typedef Gpio<GPIOD_BASE,2> sdCMD;
#endif
// //
// Class BufferConverter // Class BufferConverter
...@@ -631,12 +678,12 @@ CmdResult Command::send(CommandType cmd, unsigned int arg) ...@@ -631,12 +678,12 @@ CmdResult Command::send(CommandType cmd, unsigned int arg)
{ {
if(SDIO->STA & SDIO_STA_CMDSENT) if(SDIO->STA & SDIO_STA_CMDSENT)
{ {
SDIO->ICR=0x7ff;//Clear flags SDIO->ICR=ICR_FLAGS_CLR;//Clear flags
return CmdResult(cc,CmdResult::Ok); return CmdResult(cc,CmdResult::Ok);
} }
delayUs(1); delayUs(1);
} }
SDIO->ICR=0x7ff;//Clear flags SDIO->ICR=ICR_FLAGS_CLR;//Clear flags
return CmdResult(cc,CmdResult::Timeout); return CmdResult(cc,CmdResult::Timeout);
} }
...@@ -646,7 +693,7 @@ CmdResult Command::send(CommandType cmd, unsigned int arg) ...@@ -646,7 +693,7 @@ CmdResult Command::send(CommandType cmd, unsigned int arg)
unsigned int status=SDIO->STA; unsigned int status=SDIO->STA;
if(status & SDIO_STA_CMDREND) if(status & SDIO_STA_CMDREND)
{ {
SDIO->ICR=0x7ff;//Clear flags SDIO->ICR=ICR_FLAGS_CLR;//Clear flags
if(SDIO->RESPCMD==cc) return CmdResult(cc,CmdResult::Ok); if(SDIO->RESPCMD==cc) return CmdResult(cc,CmdResult::Ok);
else return CmdResult(cc,CmdResult::RespNotMatch); else return CmdResult(cc,CmdResult::RespNotMatch);
} }
...@@ -754,7 +801,7 @@ private: ...@@ -754,7 +801,7 @@ private:
#endif //SD_ONE_BIT_DATABUS #endif //SD_ONE_BIT_DATABUS
///\internal Maximum number of calls to IRQreduceClockSpeed() allowed ///\internal Maximum number of calls to IRQreduceClockSpeed() allowed
static const unsigned char MAX_ALLOWED_REDUCTIONS=1; static const unsigned char MAX_ALLOWED_REDUCTIONS=5;
///\internal value returned by getRetryCount() while *not* calibrating clock. ///\internal value returned by getRetryCount() while *not* calibrating clock.
static const unsigned char MAX_RETRY=10; static const unsigned char MAX_RETRY=10;
...@@ -768,6 +815,10 @@ private: ...@@ -768,6 +815,10 @@ private:
void ClockController::calibrateClockSpeed(SDIODriver *sdio) void ClockController::calibrateClockSpeed(SDIODriver *sdio)
{ {
#ifdef SD_DIVIDER
// The frequency will be divided by a factor of SD_DIVIDER + 2
setClockSpeed(SD_DIVIDER);
#else
//During calibration we call readBlock() which will call reduceClockSpeed() //During calibration we call readBlock() which will call reduceClockSpeed()
//so not to invalidate calibration clock reduction must not be available //so not to invalidate calibration clock reduction must not be available
clockReductionAvailable=0; clockReductionAvailable=0;
...@@ -796,6 +847,7 @@ void ClockController::calibrateClockSpeed(SDIODriver *sdio) ...@@ -796,6 +847,7 @@ void ClockController::calibrateClockSpeed(SDIODriver *sdio)
setClockSpeed(minFreq); setClockSpeed(minFreq);
DBG("Optimal CLKCR=%d\n",minFreq); DBG("Optimal CLKCR=%d\n",minFreq);
} }
#endif
//Make clock reduction available //Make clock reduction available
clockReductionAvailable=MAX_ALLOWED_REDUCTIONS; clockReductionAvailable=MAX_ALLOWED_REDUCTIONS;
...@@ -884,11 +936,18 @@ static void displayBlockTransferError() ...@@ -884,11 +936,18 @@ static void displayBlockTransferError()
static unsigned int dmaTransferCommonSetup(const unsigned char *buffer) static unsigned int dmaTransferCommonSetup(const unsigned char *buffer)
{ {
//Clear both SDIO and DMA interrupt flags //Clear both SDIO and DMA interrupt flags
SDIO->ICR=0x7ff; SDIO->ICR=ICR_FLAGS_CLR;
DMA2->LIFCR=DMA_LIFCR_CTCIF3 | #if (defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)) && SD_SDMMC==2
DMA_LIFCR_CTEIF3 | DMA2->LIFCR = DMA_LIFCR_CTCIF0
DMA_LIFCR_CDMEIF3 | | DMA_LIFCR_CTEIF0
DMA_LIFCR_CFEIF3; | DMA_LIFCR_CDMEIF0
| DMA_LIFCR_CFEIF0;
#else
DMA2->LIFCR = DMA_LIFCR_CTCIF3
| DMA_LIFCR_CTEIF3
| DMA_LIFCR_CDMEIF3
| DMA_LIFCR_CFEIF3;
#endif
transferError=false; transferError=false;
dmaFlags=sdioFlags=0; dmaFlags=sdioFlags=0;
...@@ -937,24 +996,28 @@ static bool multipleBlockRead(unsigned char *buffer, unsigned int nblk, ...@@ -937,24 +996,28 @@ static bool multipleBlockRead(unsigned char *buffer, unsigned int nblk,
SDIO_MASK_TXUNDERRIE | //Interrupt on tx underrun SDIO_MASK_TXUNDERRIE | //Interrupt on tx underrun
SDIO_MASK_DCRCFAILIE | //Interrupt on data CRC fail SDIO_MASK_DCRCFAILIE | //Interrupt on data CRC fail
SDIO_MASK_DTIMEOUTIE; //Interrupt on data timeout SDIO_MASK_DTIMEOUTIE; //Interrupt on data timeout
DMA2_Stream3->PAR=reinterpret_cast<unsigned int>(&SDIO->FIFO); DMA_Stream->PAR=reinterpret_cast<unsigned int>(&SDIO->FIFO);
DMA2_Stream3->M0AR=reinterpret_cast<unsigned int>(buffer); DMA_Stream->M0AR=reinterpret_cast<unsigned int>(buffer);
//Note: DMA2_Stream3->NDTR is don't care in peripheral flow control mode //Note: DMA_Stream->NDTR is don't care in peripheral flow control mode
DMA2_Stream3->FCR=DMA_SxFCR_FEIE | //Interrupt on fifo error DMA_Stream->FCR = DMA_SxFCR_FEIE //Interrupt on fifo error
DMA_SxFCR_DMDIS | //Fifo enabled | DMA_SxFCR_DMDIS //Fifo enabled
DMA_SxFCR_FTH_0; //Take action if fifo half full | DMA_SxFCR_FTH_0; //Take action if fifo half full
DMA2_Stream3->CR=DMA_SxCR_CHSEL_2 | //Channel 4 (SDIO) #if (defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)) && SD_SDMMC==2
DMA_SxCR_PBURST_0 | //4-beat bursts read from SDIO DMA_Stream->CR = (11 << DMA_SxCR_CHSEL_Pos) //Channel 4 (SDIO)
DMA_SxCR_PL_0 | //Medium priority DMA stream #else
memoryTransferSize | //RAM data size depends on alignment DMA_Stream->CR = DMA_SxCR_CHSEL_2 //Channel 4 (SDIO)
DMA_SxCR_PSIZE_1 | //Read 32bit at a time from SDIO #endif
DMA_SxCR_MINC | //Increment RAM pointer | DMA_SxCR_PBURST_0 //4-beat bursts read from SDIO
0 | //Peripheral to memory direction | DMA_SxCR_PL_0 //Medium priority DMA stream
DMA_SxCR_PFCTRL | //Peripheral is flow controller | memoryTransferSize //RAM data size depends on alignment
DMA_SxCR_TCIE | //Interrupt on transfer complete | DMA_SxCR_PSIZE_1 //Read 32bit at a time from SDIO
DMA_SxCR_TEIE | //Interrupt on transfer error | DMA_SxCR_MINC //Increment RAM pointer
DMA_SxCR_DMEIE | //Interrupt on direct mode error | 0 //Peripheral to memory direction
DMA_SxCR_EN; //Start the DMA | DMA_SxCR_PFCTRL //Peripheral is flow controller
| DMA_SxCR_TCIE //Interrupt on transfer complete
| DMA_SxCR_TEIE //Interrupt on transfer error
| DMA_SxCR_DMEIE //Interrupt on direct mode error
| DMA_SxCR_EN; //Start the DMA
SDIO->DLEN=nblk*512; SDIO->DLEN=nblk*512;
if(waiting==0) if(waiting==0)
...@@ -977,8 +1040,8 @@ static bool multipleBlockRead(unsigned char *buffer, unsigned int nblk, ...@@ -977,8 +1040,8 @@ static bool multipleBlockRead(unsigned char *buffer, unsigned int nblk,
} }
} }
} else transferError=true; } else transferError=true;
DMA2_Stream3->CR=0; DMA_Stream->CR=0;
while(DMA2_Stream3->CR & DMA_SxCR_EN) ; //DMA may take time to stop while(DMA_Stream->CR & DMA_SxCR_EN) ; //DMA may take time to stop
SDIO->DCTRL=0; //Disable data path state machine SDIO->DCTRL=0; //Disable data path state machine
SDIO->MASK=0; SDIO->MASK=0;
...@@ -1040,26 +1103,30 @@ static bool multipleBlockWrite(const unsigned char *buffer, unsigned int nblk, ...@@ -1040,26 +1103,30 @@ static bool multipleBlockWrite(const unsigned char *buffer, unsigned int nblk,
SDIO_MASK_TXUNDERRIE | //Interrupt on tx underrun SDIO_MASK_TXUNDERRIE | //Interrupt on tx underrun
SDIO_MASK_DCRCFAILIE | //Interrupt on data CRC fail SDIO_MASK_DCRCFAILIE | //Interrupt on data CRC fail
SDIO_MASK_DTIMEOUTIE; //Interrupt on data timeout SDIO_MASK_DTIMEOUTIE; //Interrupt on data timeout
DMA2_Stream3->PAR=reinterpret_cast<unsigned int>(&SDIO->FIFO); DMA_Stream->PAR=reinterpret_cast<unsigned int>(&SDIO->FIFO);
DMA2_Stream3->M0AR=reinterpret_cast<unsigned int>(buffer); DMA_Stream->M0AR=reinterpret_cast<unsigned int>(buffer);
//Note: DMA2_Stream3->NDTR is don't care in peripheral flow control mode //Note: DMA_Stream->NDTR is don't care in peripheral flow control mode
//Quirk: not enabling DMA_SxFCR_FEIE because the SDIO seems to generate //Quirk: not enabling DMA_SxFCR_FEIE because the SDIO seems to generate
//a spurious fifo error. The code was tested and the transfer completes //a spurious fifo error. The code was tested and the transfer completes
//successfully even in the presence of this fifo error //successfully even in the presence of this fifo error
DMA2_Stream3->FCR=DMA_SxFCR_DMDIS | //Fifo enabled DMA_Stream->FCR = DMA_SxFCR_DMDIS //Fifo enabled
DMA_SxFCR_FTH_1 | //Take action if fifo full | DMA_SxFCR_FTH_1 //Take action if fifo full
DMA_SxFCR_FTH_0; | DMA_SxFCR_FTH_0;
DMA2_Stream3->CR=DMA_SxCR_CHSEL_2 | //Channel 4 (SDIO) #if (defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)) && SD_SDMMC==2
DMA_SxCR_PBURST_0 | //4-beat bursts write to SDIO DMA_Stream->CR = (11 << DMA_SxCR_CHSEL_Pos) // Channel 4 (SDIO)
DMA_SxCR_PL_0 | //Medium priority DMA stream #else
memoryTransferSize | //RAM data size depends on alignment DMA_Stream->CR = DMA_SxCR_CHSEL_2 // Channel 4 (SDIO)
DMA_SxCR_PSIZE_1 | //Write 32bit at a time to SDIO #endif
DMA_SxCR_MINC | //Increment RAM pointer | DMA_SxCR_PBURST_0 //4-beat bursts write to SDIO
DMA_SxCR_DIR_0 | //Memory to peripheral direction | DMA_SxCR_PL_0 //Medium priority DMA stream
DMA_SxCR_PFCTRL | //Peripheral is flow controller | memoryTransferSize //RAM data size depends on alignment
DMA_SxCR_TEIE | //Interrupt on transfer error | DMA_SxCR_PSIZE_1 //Write 32bit at a time to SDIO
DMA_SxCR_DMEIE | //Interrupt on direct mode error | DMA_SxCR_MINC //Increment RAM pointer
DMA_SxCR_EN; //Start the DMA | DMA_SxCR_DIR_0 //Memory to peripheral direction
| DMA_SxCR_PFCTRL //Peripheral is flow controller
| DMA_SxCR_TEIE //Interrupt on transfer error
| DMA_SxCR_DMEIE //Interrupt on direct mode error
| DMA_SxCR_EN; //Start the DMA
SDIO->DLEN=nblk*512; SDIO->DLEN=nblk*512;
if(waiting==0) if(waiting==0)
...@@ -1082,8 +1149,8 @@ static bool multipleBlockWrite(const unsigned char *buffer, unsigned int nblk, ...@@ -1082,8 +1149,8 @@ static bool multipleBlockWrite(const unsigned char *buffer, unsigned int nblk,
} }
} }
} else transferError=true; } else transferError=true;
DMA2_Stream3->CR=0; DMA_Stream->CR=0;
while(DMA2_Stream3->CR & DMA_SxCR_EN) ; //DMA may take time to stop while(DMA_Stream->CR & DMA_SxCR_EN) ; //DMA may take time to stop
SDIO->DCTRL=0; //Disable data path state machine SDIO->DCTRL=0; //Disable data path state machine
SDIO->MASK=0; SDIO->MASK=0;
...@@ -1161,6 +1228,22 @@ static void initSDIOPeripheral() ...@@ -1161,6 +1228,22 @@ static void initSDIOPeripheral()
RCC_SYNC(); RCC_SYNC();
RCC->APB2ENR |= RCC_APB2ENR_SDIOEN; RCC->APB2ENR |= RCC_APB2ENR_SDIOEN;
RCC_SYNC(); RCC_SYNC();
#if (defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)) && SD_SDMMC==2
sdD0::mode(Mode::ALTERNATE);
sdD0::alternateFunction(11);
#ifndef SD_ONE_BIT_DATABUS
sdD1::mode(Mode::ALTERNATE);
sdD1::alternateFunction(11);
sdD2::mode(Mode::ALTERNATE);
sdD2::alternateFunction(10);
sdD3::mode(Mode::ALTERNATE);
sdD3::alternateFunction(10);
#endif // SD_ONE_BIT_DATABUS
sdCLK::mode(Mode::ALTERNATE);
sdCLK::alternateFunction(11);
sdCMD::mode(Mode::ALTERNATE);
sdCMD::alternateFunction(11);
#else
sdD0::mode(Mode::ALTERNATE); sdD0::mode(Mode::ALTERNATE);
sdD0::alternateFunction(12); sdD0::alternateFunction(12);
#ifndef SD_ONE_BIT_DATABUS #ifndef SD_ONE_BIT_DATABUS
...@@ -1175,9 +1258,16 @@ static void initSDIOPeripheral() ...@@ -1175,9 +1258,16 @@ static void initSDIOPeripheral()
sdCLK::alternateFunction(12); sdCLK::alternateFunction(12);
sdCMD::mode(Mode::ALTERNATE); sdCMD::mode(Mode::ALTERNATE);
sdCMD::alternateFunction(12); sdCMD::alternateFunction(12);
#endif
} }
#if (defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)) && SD_SDMMC==2
NVIC_SetPriority(DMA2_Stream0_IRQn,15);//Low priority for DMA
NVIC_EnableIRQ(DMA2_Stream0_IRQn);
#else
NVIC_SetPriority(DMA2_Stream3_IRQn,15);//Low priority for DMA NVIC_SetPriority(DMA2_Stream3_IRQn,15);//Low priority for DMA
NVIC_EnableIRQ(DMA2_Stream3_IRQn); NVIC_EnableIRQ(DMA2_Stream3_IRQn);
#endif
NVIC_SetPriority(SDIO_IRQn,15);//Low priority for SDIO NVIC_SetPriority(SDIO_IRQn,15);//Low priority for SDIO
NVIC_EnableIRQ(SDIO_IRQn); NVIC_EnableIRQ(SDIO_IRQn);
...@@ -1186,7 +1276,11 @@ static void initSDIOPeripheral() ...@@ -1186,7 +1276,11 @@ static void initSDIOPeripheral()
SDIO->CLKCR=0; SDIO->CLKCR=0;
SDIO->CMD=0; SDIO->CMD=0;
SDIO->DCTRL=0; SDIO->DCTRL=0;
#if defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)
SDIO->ICR=0x4005ff;
#else
SDIO->ICR=0xc007ff; SDIO->ICR=0xc007ff;
#endif
SDIO->POWER=SDIO_POWER_PWRCTRL_1 | SDIO_POWER_PWRCTRL_0; //Power on state SDIO->POWER=SDIO_POWER_PWRCTRL_1 | SDIO_POWER_PWRCTRL_0; //Power on state
//This delay is particularly important: when setting the POWER register a //This delay is particularly important: when setting the POWER register a
//glitch on the CMD pin happens. This glitch has a fast fall time and a slow //glitch on the CMD pin happens. This glitch has a fast fall time and a slow
......
#include "sd_stm32l4.h"
#include "interfaces/bsp.h"
#include "interfaces/arch_registers.h"
#include "core/cache_cortexMx.h"
#include "kernel/scheduler/scheduler.h"
#include "interfaces/delays.h"
#include "kernel/kernel.h"
#include "board_settings.h" //For sdVoltage and SD_ONE_BIT_DATABUS definitions
#include <cstdio>
#include <cstring>
#include <errno.h>
//Note: enabling debugging might cause deadlock when using sleep() or reboot()
//The bug won't be fixed because debugging is only useful for driver development
///\internal Debug macro, for normal conditions
//#define DBG iprintf
#define DBG(x,...) do {} while(0)
///\internal Debug macro, for errors only
//#define DBGERR iprintf
#define DBGERR(x,...) do {} while(0)
void __attribute__((naked)) SDMMC1_IRQHandler()
{
saveContext();
asm volatile("bl _ZN6miosix12SDMMCirqImplEv");
restoreContext();
}
namespace miosix {
static volatile bool transferError; ///< \internal DMA or SDIO transfer error
static Thread *waiting; ///< \internal Thread waiting for transfer
static unsigned int dmaFlags; ///< \internal DMA status flags
static unsigned int sdioFlags; ///< \internal SDIO status flags
/**
* \internal
* DMA2 Stream3 interrupt handler actual implementation
*/
void __attribute__((used)) SDMMCirqImpl()
{
sdioFlags=SDMMC1->STA;
if(sdioFlags & (SDMMC_STA_RXOVERR |
SDMMC_STA_TXUNDERR | SDMMC_STA_DTIMEOUT | SDMMC_STA_DCRCFAIL | SDMMC_STA_DABORT | SDMMC_STA_IDMATE))
transferError=true;
//Changed: Old value was 0x7ff
SDMMC1->ICR=0x1fe00fff;//Clear flags
if(!waiting) return;
waiting->IRQwakeup();
if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
Scheduler::IRQfindNextThread();
waiting=0;
}
/*
* Operating voltage of device. It is sent to the SD card to check if it can
* work at this voltage. Range *must* be within 28..36
* Example 33=3.3v
*/
//static const unsigned char sdVoltage=33; //Is defined in board_settings.h
static const unsigned int sdVoltageMask=1<<(sdVoltage-13); //See OCR reg in SD spec
/**
* \internal
* Possible state of the cardType variable.
*/
enum CardType
{
Invalid=0, ///<\internal Invalid card type
MMC=1<<0, ///<\internal if(cardType==MMC) card is an MMC
SDv1=1<<1, ///<\internal if(cardType==SDv1) card is an SDv1
SDv2=1<<2, ///<\internal if(cardType==SDv2) card is an SDv2
SDHC=1<<3 ///<\internal if(cardType==SDHC) card is an SDHC
};
///\internal Type of card.
static CardType cardType=Invalid;
//SD card GPIOs
typedef Gpio<GPIOC_BASE,8> sdD0;
typedef Gpio<GPIOC_BASE,9> sdD1;
typedef Gpio<GPIOC_BASE,10> sdD2;
typedef Gpio<GPIOC_BASE,11> sdD3;
typedef Gpio<GPIOC_BASE,12> sdCLK;
typedef Gpio<GPIOD_BASE,2> sdCMD;
//
// Class CmdResult
//
/**
* \internal
* Contains the result of an SD/MMC command
*/
class CmdResult
{
public:
/**
* \internal
* Possible outcomes of sending a command
*/
enum Error
{
Ok=0, /// No errors
Timeout, /// Timeout while waiting command reply
CRCFail, /// CRC check failed in command reply
RespNotMatch,/// Response index does not match command index
ACMDFail /// Sending CMD55 failed
};
/**
* \internal
* Default constructor
*/
CmdResult(): cmd(0), error(Ok), response(0) {}
/**
* \internal
* Constructor, set the response data
* \param cmd command index of command that was sent
* \param result result of command
*/
CmdResult(unsigned char cmd, Error error): cmd(cmd), error(error),
response(SDMMC1->RESP1) {}
/**
* \internal
* \return the 32 bit of the response.
* May not be valid if getError()!=Ok or the command does not send a
* response, such as CMD0
*/
unsigned int getResponse() { return response; }
/**
* \internal
* \return command index
*/
unsigned char getCmdIndex() { return cmd; }
/**
* \internal
* \return the error flags of the response
*/
Error getError() { return error; }
/**
* \internal
* Checks if errors occurred while sending the command.
* \return true if no errors, false otherwise
*/
bool validateError();
/**
* \internal
* interprets this->getResponse() as an R1 response, and checks if there are
* errors, or everything is ok
* \return true on success, false on failure
*/
bool validateR1Response();
/**
* \internal
* Same as validateR1Response, but can be called with interrupts disabled.
* \return true on success, false on failure
*/
bool IRQvalidateR1Response();
/**
* \internal
* interprets this->getResponse() as an R6 response, and checks if there are
* errors, or everything is ok
* \return true on success, false on failure
*/
bool validateR6Response();
/**
* \internal
* \return the card state from an R1 or R6 resonse
*/
unsigned char getState();
private:
unsigned char cmd; ///<\internal Command index that was sent
Error error; ///<\internal possible error that occurred
unsigned int response; ///<\internal 32bit response
};
bool CmdResult::validateError()
{
switch(error)
{
case Ok:
return true;
case Timeout:
DBGERR("CMD%d: Timeout\n",cmd);
break;
case CRCFail:
DBGERR("CMD%d: CRC Fail\n",cmd);
break;
case RespNotMatch:
DBGERR("CMD%d: Response does not match\n",cmd);
break;
case ACMDFail:
DBGERR("CMD%d: ACMD Fail\n",cmd);
break;
}
return false;
}
bool CmdResult::validateR1Response()
{
if(error!=Ok) return validateError();
//Note: this number is obtained with all the flags of R1 which are errors
//(flagged as E in the SD specification), plus CARD_IS_LOCKED because
//locked card are not supported by this software driver
if((response & 0xfff98008)==0) return true;
DBGERR("CMD%d: R1 response error(s):\n",cmd);
if(response & (1<<31)) DBGERR("Out of range\n");
if(response & (1<<30)) DBGERR("ADDR error\n");
if(response & (1<<29)) DBGERR("BLOCKLEN error\n");
if(response & (1<<28)) DBGERR("ERASE SEQ error\n");
if(response & (1<<27)) DBGERR("ERASE param\n");
if(response & (1<<26)) DBGERR("WP violation\n");
if(response & (1<<25)) DBGERR("card locked\n");
if(response & (1<<24)) DBGERR("LOCK_UNLOCK failed\n");
if(response & (1<<23)) DBGERR("command CRC failed\n");
if(response & (1<<22)) DBGERR("illegal command\n");
if(response & (1<<21)) DBGERR("ECC fail\n");
if(response & (1<<20)) DBGERR("card controller error\n");
if(response & (1<<19)) DBGERR("unknown error\n");
if(response & (1<<16)) DBGERR("CSD overwrite\n");
if(response & (1<<15)) DBGERR("WP ERASE skip\n");
if(response & (1<<3)) DBGERR("AKE_SEQ error\n");
return false;
}
bool CmdResult::IRQvalidateR1Response()
{
if(error!=Ok) return false;
if(response & 0xfff98008) return false;
return true;
}
bool CmdResult::validateR6Response()
{
if(error!=Ok) return validateError();
if((response & 0xe008)==0) return true;
DBGERR("CMD%d: R6 response error(s):\n",cmd);
if(response & (1<<15)) DBGERR("command CRC failed\n");
if(response & (1<<14)) DBGERR("illegal command\n");
if(response & (1<<13)) DBGERR("unknown error\n");
if(response & (1<<3)) DBGERR("AKE_SEQ error\n");
return false;
}
unsigned char CmdResult::getState()
{
unsigned char result=(response>>9) & 0xf;
DBG("CMD%d: State: ",cmd);
switch(result)
{
case 0: DBG("Idle\n"); break;
case 1: DBG("Ready\n"); break;
case 2: DBG("Ident\n"); break;
case 3: DBG("Stby\n"); break;
case 4: DBG("Tran\n"); break;
case 5: DBG("Data\n"); break;
case 6: DBG("Rcv\n"); break;
case 7: DBG("Prg\n"); break;
case 8: DBG("Dis\n"); break;
case 9: DBG("Btst\n"); break;
default: DBG("Unknown\n"); break;
}
return result;
}
//
// Class Command
//
/**
* \internal
* This class allows sending commands to an SD or MMC
*/
class Command
{
public:
/**
* \internal
* SD/MMC commands
* - bit #7 is @ 1 if a command is an ACMDxx. send() will send the
* sequence CMD55, CMDxx
* - bit from #0 to #5 indicate command index (CMD0..CMD63)
* - bit #6 is don't care
*/
enum CommandType
{
CMD0=0, //GO_IDLE_STATE
CMD2=2, //ALL_SEND_CID
CMD3=3, //SEND_RELATIVE_ADDR
ACMD6=0x80 | 6, //SET_BUS_WIDTH
CMD7=7, //SELECT_DESELECT_CARD
ACMD41=0x80 | 41, //SEND_OP_COND (SD)
CMD8=8, //SEND_IF_COND
CMD9=9, //SEND_CSD
CMD12=12, //STOP_TRANSMISSION
CMD13=13, //SEND_STATUS
CMD16=16, //SET_BLOCKLEN
CMD17=17, //READ_SINGLE_BLOCK
CMD18=18, //READ_MULTIPLE_BLOCK
ACMD23=0x80 | 23, //SET_WR_BLK_ERASE_COUNT (SD)
CMD24=24, //WRITE_BLOCK
CMD25=25, //WRITE_MULTIPLE_BLOCK
CMD55=55 //APP_CMD
};
/**
* \internal
* Send a command.
* \param cmd command index (CMD0..CMD63) or ACMDxx command
* \param arg the 32 bit argument to the command
* \return a CmdResult object
*/
static CmdResult send(CommandType cmd, unsigned int arg);
/**
* \internal
* Set the relative card address, obtained during initialization.
* \param r the card's rca
*/
static void setRca(unsigned short r) { rca=r; }
/**
* \internal
* \return the card's rca, as set by setRca
*/
static unsigned int getRca() { return static_cast<unsigned int>(rca); }
private:
static unsigned short rca;///<\internal Card's relative address
};
CmdResult Command::send(CommandType cmd, unsigned int arg)
{
unsigned char cc=static_cast<unsigned char>(cmd);
//Handle ACMDxx as CMD55, CMDxx
if(cc & 0x80)
{
DBG("ACMD%d\n",cc & 0x3f);
CmdResult r=send(CMD55,(static_cast<unsigned int>(rca))<<16);
if(r.validateR1Response()==false)
return CmdResult(cc & 0x3f,CmdResult::ACMDFail);
//Bit 5 @ 1 = next command will be interpreted as ACMD
if((r.getResponse() & (1<<5))==0)
return CmdResult(cc & 0x3f,CmdResult::ACMDFail);
} else DBG("CMD%d\n",cc & 0x3f);
//Send command
cc &= 0x3f;
unsigned int command= SDMMC_CMD_CPSMEN | static_cast<unsigned int>(cc);
if(cc!=CMD0) command |= SDMMC_CMD_WAITRESP_0; //CMD0 has no response
if(cc==CMD2) command |= SDMMC_CMD_WAITRESP_1; //CMD2 has long response
if(cc==CMD9) command |= SDMMC_CMD_WAITRESP_1; //CMD9 has long response
SDMMC1->ARG = arg;
SDMMC1->CMD = command;
//CMD0 has no response, so wait until it is sent
if(cc==CMD0)
{
for(int i=0;i<500;i++)
{
if(SDMMC1->STA & SDMMC_STA_CMDSENT)
{
SDMMC1->ICR=0x1fe00fff;//Clear flags
return CmdResult(cc,CmdResult::Ok);
}
delayUs(1);
}
SDMMC1->ICR = 0x1fe00fff;//Clear flags
return CmdResult(cc,CmdResult::Timeout);
}
//Command is not CMD0, so wait a reply
for(int i=0;i<500;i++)
{
unsigned int status=SDMMC1->STA;
if(status & SDMMC_STA_CMDREND)
{
SDMMC1->ICR=0x1fe00fff;//Clear flags
if(SDMMC1->RESPCMD==cc) return CmdResult(cc,CmdResult::Ok);
else return CmdResult(cc,CmdResult::RespNotMatch);
}
if(status & SDMMC_STA_CCRCFAIL)
{
SDMMC1->ICR=SDMMC_ICR_CCRCFAILC;
return CmdResult(cc,CmdResult::CRCFail);
}
if(status & SDMMC_STA_CTIMEOUT) break;
delayUs(1);
}
SDMMC1->ICR=SDMMC_ICR_CTIMEOUTC;
return CmdResult(cc,CmdResult::Timeout);
}
unsigned short Command::rca=0;
//
// Class ClockController
//
/**
* \internal
* This class controls the clock speed of the SDIO peripheral. It originated
* from a previous version of this driver, where the SDIO was used in polled
* mode instead of DMA mode, but has been retained to improve the robustness
* of the driver.
*/
class ClockController
{
public:
/**
* \internal. Set a low clock speed of 400KHz or less, used for
* detecting SD/MMC cards. This function as a side effect enables 1bit bus
* width, and disables clock powersave, since it is not allowed by SD spec.
*/
static void setLowSpeedClock()
{
clockReductionAvailable=0;
// No hardware flow control, SDIO_CK generated on rising edge, 1bit bus
// width, no clock bypass, no powersave.
// Set low clock speed 400KHz
SDMMC1->CLKCR=CLOCK_400KHz;
SDMMC1->DTIMER=240000; //Timeout 600ms expressed in SD_CK cycles
}
/**
* \internal
* Automatically select the data speed. This routine selects the highest
* sustainable data transfer speed. This is done by binary search until
* the highest clock speed that causes no errors is found.
* This function as a side effect enables 4bit bus width, and clock
* powersave.
*/
static void calibrateClockSpeed(SDIODriver *sdio);
/**
* \internal
* Since clock speed is set dynamically by binary search at runtime, a
* corner case might be that of a clock speed which results in unreliable
* data transfer, that sometimes succeeds, and sometimes fail.
* For maximum robustness, this function is provided to reduce the clock
* speed slightly in case a data transfer should fail after clock
* calibration. To avoid inadvertently considering other kind of issues as
* clock issues, this function can be called only MAX_ALLOWED_REDUCTIONS
* times after clock calibration, subsequent calls will fail. This will
* avoid other issues causing an ever decreasing clock speed.
* \return true on success, false on failure
*/
static bool reduceClockSpeed();
/**
* \internal
* Read and write operation do retry during normal use for robustness, but
* during clock claibration they must not retry for speed reasons. This
* member function returns 1 during clock claibration and MAX_RETRY during
* normal use.
*/
static unsigned char getRetryCount() { return retries; }
private:
/**
* Set SDIO clock speed
* \param clkdiv speed is SDIOCLK/(clkdiv+2)
*/
static void setClockSpeed(unsigned int clkdiv);
static const unsigned int SDIOCLK=48000000; //On stm32f2 SDIOCLK is always 48MHz
static const unsigned int CLOCK_400KHz=60; //48MHz/(2*60)=400KHz
#ifdef OVERRIDE_SD_CLOCK_DIVIDER_MAX
//Some boards using SDRAM cause SDIO TX Underrun occasionally
static const unsigned int CLOCK_MAX=OVERRIDE_SD_CLOCK_DIVIDER_MAX;
#else //OVERRIDE_SD_CLOCK_DIVIDER_MAX
static const unsigned int CLOCK_MAX=0; //48MHz/(0+2) =24MHz
#endif //OVERRIDE_SD_CLOCK_DIVIDER_MAX
#ifdef SD_ONE_BIT_DATABUS
///\internal Clock enabled, bus width 1bit, clock powersave enabled.
static const unsigned int CLKCR_FLAGS= SDMMC_CLKCR_PWRSAV;
#else //SD_ONE_BIT_DATABUS
///\internal Clock enabled, bus width 4bit, clock powersave enabled.
static const unsigned int CLKCR_FLAGS= //SDIO_CLKCR_CLKEN |
SDMMC_CLKCR_WIDBUS_0 | SDMMC_CLKCR_PWRSAV;
#endif //SD_ONE_BIT_DATABUS
///\internal Maximum number of calls to IRQreduceClockSpeed() allowed
static const unsigned char MAX_ALLOWED_REDUCTIONS=1;
///\internal value returned by getRetryCount() while *not* calibrating clock.
static const unsigned char MAX_RETRY=10;
///\internal Used to allow only one call to reduceClockSpeed()
static unsigned char clockReductionAvailable;
///\internal value returned by getRetryCount()
static unsigned char retries;
};
void ClockController::calibrateClockSpeed(SDIODriver *sdio)
{
//During calibration we call readBlock() which will call reduceClockSpeed()
//so not to invalidate calibration clock reduction must not be available
clockReductionAvailable=0;
retries=1;
DBG("Automatic speed calibration\n");
unsigned int buffer[512/sizeof(unsigned int)];
unsigned int minFreq=CLOCK_400KHz;
unsigned int maxFreq=CLOCK_MAX;
unsigned int selected;
while(minFreq-maxFreq>1)
{
selected=(minFreq+maxFreq)/2;
DBG("Trying CLKCR=%d\n",selected);
setClockSpeed(selected);
if(sdio->readBlock(reinterpret_cast<unsigned char*>(buffer),512,0)==512)
minFreq=selected;
else maxFreq=selected;
}
//Last round of algorithm
setClockSpeed(maxFreq);
if(sdio->readBlock(reinterpret_cast<unsigned char*>(buffer),512,0)==512)
{
DBG("Optimal CLKCR=%d\n",maxFreq);
} else {
setClockSpeed(minFreq);
DBG("Optimal CLKCR=%d\n",minFreq);
}
//Make clock reduction available
clockReductionAvailable=MAX_ALLOWED_REDUCTIONS;
retries=MAX_RETRY;
}
bool ClockController::reduceClockSpeed()
{
DBGERR("clock speed reduction requested\n");
//Ensure this function can be called only a few times
if(clockReductionAvailable==0) return false;
clockReductionAvailable--;
unsigned int currentClkcr=SDMMC1->CLKCR & 0x3ff;
if(currentClkcr==CLOCK_400KHz) return false; //No lower than this value
//If the value of clockcr is low, increasing it by one is enough since
//frequency changes a lot, otherwise increase by 2.
if(currentClkcr < 6) currentClkcr++; // was < 10 with the f4
else currentClkcr+=2;
setClockSpeed(currentClkcr);
return true;
}
void ClockController::setClockSpeed(unsigned int clkdiv)
{
SDMMC1->CLKCR = clkdiv | CLKCR_FLAGS;
//Timeout 600ms expressed in SD_CK cycles
SDMMC1-> DTIMER = (6*SDIOCLK)/((clkdiv == 0 ? 1 : 2 * clkdiv)*10);
}
unsigned char ClockController::clockReductionAvailable = false;
unsigned char ClockController::retries = ClockController::MAX_RETRY;
//
// Data send/receive functions
//
/**
* \internal
* Wait until the card is ready for data transfer.
* Can be called independently of the card being selected.
* \return true on success, false on failure
*/
static bool waitForCardReady()
{
const int timeout=1500; //Timeout 1.5 second
const int sleepTime=2;
for(int i=0;i<timeout/sleepTime;i++)
{
CmdResult cr=Command::send(Command::CMD13,Command::getRca()<<16);
if(cr.validateR1Response()==false) return false;
//Bit 8 in R1 response means ready for data.
if(cr.getResponse() & (1<<8)) return true;
Thread::sleep(sleepTime);
}
DBGERR("Timeout waiting card ready\n");
return false;
}
/**
* \internal
* Prints the errors that may occur during a DMA transfer
*/
static void displayBlockTransferError()
{
DBGERR("Block transfer error\n");
if(dmaFlags & DMA_ISR_TEIF4) DBGERR("* DMA Transfer error\n");
if(sdioFlags & SDMMC_STA_RXOVERR) DBGERR("* SDIO RX Overrun\n");
if(sdioFlags & SDMMC_STA_TXUNDERR) DBGERR("* SDIO TX Underrun error\n");
if(sdioFlags & SDMMC_STA_DCRCFAIL) DBGERR("* SDIO Data CRC fail\n");
if(sdioFlags & SDMMC_STA_DTIMEOUT) DBGERR("* SDIO Data timeout\n");
}
/**
* \internal
* Contains initial common code between multipleBlockRead and multipleBlockWrite
* to clear interrupt and error flags, set the waiting thread.
*/
static void dmaTransferCommonSetup(const unsigned char *buffer)
{
//Clear both SDIO and DMA interrupt flags
SDMMC1->ICR=0x1fe00fff; //Clear interrupts
SDMMC1->IDMACTRL = SDMMC_IDMA_IDMAEN & ~(SDMMC_IDMA_IDMABMODE); //Enable dma without double buffer
SDMMC1->IDMABASE0 = reinterpret_cast<unsigned int>(buffer); //Set buffer base address (where to read/write)
transferError=false;
dmaFlags=sdioFlags=0;
waiting=Thread::getCurrentThread();
}
/**
* \internal
* Read a given number of contiguous 512 byte blocks from an SD/MMC card.
* Card must be selected prior to calling this function.
* \param buffer, a buffer whose size is 512*nblk bytes
* \param nblk number of blocks to read.
* \param lba logical block address of the first block to read.
*/
static bool multipleBlockRead(unsigned char *buffer, unsigned int nblk,
unsigned int lba)
{
if(nblk==0) return true;
while(nblk>32767)
{
if(multipleBlockRead(buffer,32767,lba)==false) return false;
buffer+=32767*512;
nblk-=32767;
lba+=32767;
}
if(waitForCardReady()==false) return false;
if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC
dmaTransferCommonSetup(buffer);
//Data transfer is considered complete once the DMA transfer complete
//interrupt occurs, that happens when the last data was written in the
//buffer. Both SDIO and DMA error interrupts are active to catch errors
SDMMC1->MASK= SDMMC_MASK_DATAENDIE |
SDMMC_MASK_RXOVERRIE | //Interrupt on rx underrun
SDMMC_MASK_TXUNDERRIE | //Interrupt on tx underrun
SDMMC_MASK_DCRCFAILIE | //Interrupt on data CRC fail
SDMMC_MASK_DTIMEOUTIE | //Interrupt on data timeout
SDMMC_MASK_IDMABTCIE | //Interrupt on IDMA events
SDMMC_MASK_DABORTIE; //Interrupt on aborted
SDMMC1->DLEN=nblk*512;
if(waiting==0)
{
DBGERR("Premature wakeup\n");
transferError=true;
}
CmdResult cr=Command::send(nblk>1 ? Command::CMD18 : Command::CMD17,lba);
if(cr.validateR1Response())
{
//Block size 512 bytes, block data xfer, from card to controller
//DTMode set to 00 - Block Data Transfer (Not shown here)
SDMMC1->DCTRL=(9<<4) | SDMMC_DCTRL_DTDIR | SDMMC_DCTRL_DTEN;
DBG("READ STARTED! WAITING FOR INTERRUPT...\n");
FastInterruptDisableLock dLock;
while(waiting)
{
Thread::IRQwait();
{
FastInterruptEnableLock eLock(dLock);
Thread::yield();
}
}
} else {
transferError=true;
DBG("TRANSFER ERROR\n");
}
SDMMC1->DCTRL=0; //Disable data path state machine
SDMMC1->MASK=0;
DBGERR("TRANSFER ERROR: %d\n", transferError);
// CMD12 is sent to end CMD18 (multiple block read), or to abort an
// unfinished read in case of errors
if(nblk>1 || transferError) cr=Command::send(Command::CMD12,0);
if(transferError || cr.validateR1Response()==false)
{
displayBlockTransferError();
ClockController::reduceClockSpeed();
return false;
}
//Read ok, deal with cache coherence
markBufferAfterDmaRead(buffer,nblk*512);
return true;
}
/**
* \internal
* Write a given number of contiguous 512 byte blocks to an SD/MMC card.
* Card must be selected prior to calling this function.
* \param buffer, a buffer whose size is 512*nblk bytes
* \param nblk number of blocks to write.
* \param lba logical block address of the first block to write.
*/
static bool multipleBlockWrite(const unsigned char *buffer, unsigned int nblk,
unsigned int lba)
{
if(nblk==0) return true;
while(nblk>32767)
{
if(multipleBlockWrite(buffer,32767,lba)==false) return false;
buffer+=32767*512;
nblk-=32767;
lba+=32767;
}
//Deal with cache coherence
markBufferBeforeDmaWrite(buffer,nblk*512);
if(waitForCardReady()==false) return false;
if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC
if(nblk>1)
{
CmdResult cr=Command::send(Command::ACMD23,nblk);
if(cr.validateR1Response()==false) return false;
}
dmaTransferCommonSetup(buffer);
//Data transfer is considered complete once the SDIO transfer complete
//interrupt occurs, that happens when the last data was written to the SDIO
//Both SDIO and DMA error interrupts are active to catch errors
SDMMC1->MASK=SDMMC_MASK_DATAENDIE | //Interrupt on data end
SDMMC_MASK_RXOVERRIE | //Interrupt on rx underrun
SDMMC_MASK_TXUNDERRIE | //Interrupt on tx underrun
SDMMC_MASK_DCRCFAILIE | //Interrupt on data CRC fail
SDMMC_MASK_DTIMEOUTIE | //Interrupt on data timeout
SDMMC_MASK_IDMABTCIE | //Interrupt on IDMA events
SDMMC_MASK_DABORTIE;
SDMMC1->DLEN=nblk*512;
if(waiting==0)
{
DBGERR("Premature wakeup\n");
transferError=true;
}
CmdResult cr=Command::send(nblk>1 ? Command::CMD25 : Command::CMD24,lba);
if(cr.validateR1Response())
{
//Block size 512 bytes, block data xfer, from card to controller
SDMMC1->DCTRL= ((9<<4) | SDMMC_DCTRL_DTEN) & ~(SDMMC_DCTRL_DTDIR);
FastInterruptDisableLock dLock;
while(waiting)
{
Thread::IRQwait();
{
FastInterruptEnableLock eLock(dLock);
Thread::yield();
}
}
} else transferError=true;
// CMD12 is sent to end CMD25 (multiple block write), or to abort an
// unfinished write in case of errors
if(nblk>1 || transferError) cr=Command::send(Command::CMD12,0);
if(transferError || cr.validateR1Response()==false)
{
displayBlockTransferError();
ClockController::reduceClockSpeed();
return false;
}
return true;
}
//
// Class CardSelector
//
/**
* \internal
* Simple RAII class for selecting an SD/MMC card an automatically deselect it
* at the end of the scope.
*/
class CardSelector
{
public:
/**
* \internal
* Constructor. Selects the card.
* The result of the select operation is available through its succeded()
* member function
*/
explicit CardSelector()
{
success=Command::send(
Command::CMD7,Command::getRca()<<16).validateR1Response();
}
/**
* \internal
* \return true if the card was selected, false on error
*/
bool succeded() { return success; }
/**
* \internal
* Destructor, ensures that the card is deselected
*/
~CardSelector()
{
Command::send(Command::CMD7,0); //Deselect card. This will timeout
}
private:
bool success;
};
//
// Initialization helper functions
//
/**
* \internal
* Initialzes the SDIO peripheral in the STM32
*/
static void initSDIOPeripheral()
{
{
//Doing read-modify-write on RCC->APBENR2 and gpios, better be safe
FastInterruptDisableLock lock;
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN
| RCC_AHB2ENR_GPIODEN
| RCC_AHB2ENR_SDMMC1EN;
//RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
RCC->CCIPR |= RCC_CCIPR_CLK48SEL_1;
//RCC_SYNC();
//RCC_SYNC();
sdD0::mode(Mode::ALTERNATE);
sdD0::alternateFunction(12);
#ifndef SD_ONE_BIT_DATABUS
sdD1::mode(Mode::ALTERNATE);
sdD1::alternateFunction(12);
sdD2::mode(Mode::ALTERNATE);
sdD2::alternateFunction(12);
sdD3::mode(Mode::ALTERNATE);
sdD3::alternateFunction(12);
#endif //SD_ONE_BIT_DATABUS
sdCLK::mode(Mode::ALTERNATE);
sdCLK::alternateFunction(12);
sdCMD::mode(Mode::ALTERNATE);
sdCMD::alternateFunction(12);
}
NVIC_SetPriority(SDMMC1_IRQn,15);//Low priority for SDIO
NVIC_EnableIRQ(SDMMC1_IRQn);
SDMMC1->POWER=0; //Power off state
delayUs(1);
SDMMC1->CLKCR=0;
SDMMC1->CMD=0;
SDMMC1->DCTRL=0;
SDMMC1->ICR=0x1fe007ff; //Interrupt //0xc007ff
SDMMC1->POWER=SDMMC_POWER_PWRCTRL_1 | SDMMC_POWER_PWRCTRL_0; //Power on state
DBG("\nIDMACTRL: 0x%x\n", SDMMC1->IDMACTRL);
//This delay is particularly important: when setting the POWER register a
//glitch on the CMD pin happens. This glitch has a fast fall time and a slow
//rise time resembling an RC charge with a ~6us rise time. If the clock is
//started too soon, the card sees a clock pulse while CMD is low, and
//interprets it as a start bit. No, setting POWER to powerup does not
//eliminate the glitch.
delayUs(10);
ClockController::setLowSpeedClock();
}
/**
* \internal
* Detect if the card is an SDHC, SDv2, SDv1, MMC
* \return Type of card: (1<<0)=MMC (1<<1)=SDv1 (1<<2)=SDv2 (1<<2)|(1<<3)=SDHC
* or Invalid if card detect failed.
*/
static CardType detectCardType()
{
const int INIT_TIMEOUT=200; //200*10ms= 2 seconds
CmdResult r=Command::send(Command::CMD8,0x1aa);
if(r.validateError())
{
//We have an SDv2 card connected
if(r.getResponse()!=0x1aa)
{
DBGERR("CMD8 validation: voltage range fail\n");
return Invalid;
}
for(int i=0;i<INIT_TIMEOUT;i++)
{
//Bit 30 @ 1 = tell the card we like SDHCs
r=Command::send(Command::ACMD41,(1<<30) | sdVoltageMask);
//ACMD41 sends R3 as response, whose CRC is wrong.
if(r.getError()!=CmdResult::Ok && r.getError()!=CmdResult::CRCFail)
{
r.validateError();
return Invalid;
}
if((r.getResponse() & (1<<31))==0) //Busy bit
{
Thread::sleep(10);
continue;
}
if((r.getResponse() & sdVoltageMask)==0)
{
DBGERR("ACMD41 validation: voltage range fail\n");
return Invalid;
}
DBG("ACMD41 validation: looped %d times\n",i);
if(r.getResponse() & (1<<30))
{
DBG("SDHC\n");
return SDHC;
} else {
DBG("SDv2\n");
return SDv2;
}
}
DBGERR("ACMD41 validation: looped until timeout\n");
return Invalid;
} else {
//We have an SDv1 or MMC
r=Command::send(Command::ACMD41,sdVoltageMask);
//ACMD41 sends R3 as response, whose CRC is wrong.
if(r.getError()!=CmdResult::Ok && r.getError()!=CmdResult::CRCFail)
{
//MMC card
DBG("MMC card\n");
return MMC;
} else {
//SDv1 card
for(int i=0;i<INIT_TIMEOUT;i++)
{
//ACMD41 sends R3 as response, whose CRC is wrong.
if(r.getError()!=CmdResult::Ok &&
r.getError()!=CmdResult::CRCFail)
{
r.validateError();
return Invalid;
}
if((r.getResponse() & (1<<31))==0) //Busy bit
{
Thread::sleep(10);
//Send again command
r=Command::send(Command::ACMD41,sdVoltageMask);
continue;
}
if((r.getResponse() & sdVoltageMask)==0)
{
DBGERR("ACMD41 validation: voltage range fail\n");
return Invalid;
}
DBG("ACMD41 validation: looped %d times\nSDv1\n",i);
return SDv1;
}
DBGERR("ACMD41 validation: looped until timeout\n");
return Invalid;
}
}
}
intrusive_ref_ptr<SDIODriver> SDIODriver::instance()
{
static FastMutex m;
static intrusive_ref_ptr<SDIODriver> instance;
Lock<FastMutex> l(m);
if(!instance) instance=new SDIODriver();
return instance;
}
ssize_t SDIODriver::readBlock(void* buffer, size_t size, off_t where)
{
if(where % 512 || size % 512) return -EFAULT;
unsigned int lba=where/512;
unsigned int nSectors=size/512;
Lock<FastMutex> l(mutex);
DBG("SDIODriver::readBlock(): nSectors=%d\n",nSectors);
for(int i=0;i < ClockController::getRetryCount();i++)
{
CardSelector selector;
if(selector.succeded()==false) continue;
bool error=false;
if(multipleBlockRead(reinterpret_cast<unsigned char*>(buffer),
nSectors,lba)==false) error=true;
if(error==false)
{
if(i>0) DBGERR("Read: required %d retries\n",i);
return size;
}
}
return -EBADF;
}
ssize_t SDIODriver::writeBlock(const void* buffer, size_t size, off_t where)
{
if(where % 512 || size % 512) return -EFAULT;
unsigned int lba=where/512;
unsigned int nSectors=size/512;
Lock<FastMutex> l(mutex);
DBG("SDIODriver::writeBlock(): nSectors=%d\n",nSectors);
for(int i=0;i<ClockController::getRetryCount();i++)
{
CardSelector selector;
if(selector.succeded()==false) continue;
bool error=false;
if(multipleBlockWrite(reinterpret_cast<const unsigned char*>(buffer),
nSectors,lba)==false) error=true;
if(error==false)
{
if(i>0) DBGERR("Write: required %d retries\n",i);
return size;
}
}
return -EBADF;
}
int SDIODriver::ioctl(int cmd, void* arg)
{
DBG("SDIODriver::ioctl()\n");
if(cmd!=IOCTL_SYNC) return -ENOTTY;
Lock<FastMutex> l(mutex);
//Note: no need to select card, since status can be queried even with card
//not selected.
return waitForCardReady() ? 0 : -EFAULT;
}
SDIODriver::SDIODriver() : Device(Device::BLOCK)
{
initSDIOPeripheral();
// This is more important than it seems, since CMD55 requires the card's RCA
// as argument. During initalization, after CMD0 the card has an RCA of zero
// so without this line ACMD41 will fail and the card won't be initialized.
Command::setRca(0);
//Send card reset command
CmdResult r=Command::send(Command::CMD0,0);
if(r.validateError()==false) return;
cardType=detectCardType();
if(cardType==Invalid) return; //Card detect failed
if(cardType==MMC) return; //MMC cards currently unsupported
// Now give an RCA to the card. In theory we should loop and enumerate all
// the cards but this driver supports only one card.
r=Command::send(Command::CMD2,0);
//CMD2 sends R2 response, whose CMDINDEX field is wrong
if(r.getError()!=CmdResult::Ok && r.getError()!=CmdResult::RespNotMatch)
{
r.validateError();
return;
}
r=Command::send(Command::CMD3,0);
if(r.validateR6Response()==false) return;
Command::setRca(r.getResponse()>>16);
DBG("Got RCA=%u\n",Command::getRca());
if(Command::getRca()==0)
{
//RCA=0 can't be accepted, since it is used to deselect cards
DBGERR("RCA=0 is invalid\n");
return;
}
//Lastly, try selecting the card and configure the latest bits
{
CardSelector selector;
if(selector.succeded()==false) return;
r=Command::send(Command::CMD13,Command::getRca()<<16);//Get status
if(r.validateR1Response()==false) return;
if(r.getState()!=4) //4=Tran state
{
DBGERR("CMD7 was not able to select card\n");
return;
}
#ifndef SD_ONE_BIT_DATABUS
r=Command::send(Command::ACMD6,2); //Set 4 bit bus width
if(r.validateR1Response()==false) return;
#endif //SD_ONE_BIT_DATABUS
if(cardType!=SDHC)
{
r=Command::send(Command::CMD16,512); //Set 512Byte block length
if(r.validateR1Response()==false) return;
}
}
// Now that card is initialized, perform self calibration of maximum
// possible read/write speed. This as a side effect enables 4bit bus width.
ClockController::calibrateClockSpeed(this);
DBG("SDIO init: Success\n");
}
}
/***************************************************************************
* Copyright (C) 2014 by Terraneo Federico *
* *
* This program 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 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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. *
* *
* As a special exception, if other files instantiate templates or use *
* macros or inline functions from this file, or you compile this file *
* and link it with other works to produce a work based on this file, *
* this file does not by itself cause the resulting work to be covered *
* by the GNU General Public License. However the source code for this *
* file must still be made available in accordance with the GNU General *
* Public License. This exception does not invalidate any other reasons *
* why a work based on this file might be covered by the GNU General *
* Public License. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#ifndef SD_STM32L4_H
#define SD_STM32L4_H
#include "kernel/sync.h"
#include "filesystem/devfs/devfs.h"
#include "filesystem/ioctl.h"
namespace miosix {
/**
* Driver for the SDIO peripheral in STM32F2 and F4 microcontrollers
*/
class SDIODriver : public Device
{
public:
/**
* \return an instance to this class, singleton
*/
static intrusive_ref_ptr<SDIODriver> instance();
virtual ssize_t readBlock(void *buffer, size_t size, off_t where);
virtual ssize_t writeBlock(const void *buffer, size_t size, off_t where);
virtual int ioctl(int cmd, void *arg);
private:
/**
* Constructor
*/
SDIODriver();
FastMutex mutex;
};
} //namespace miosix
#endif //SD_STM32L4_H
\ No newline at end of file
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
#ifdef _ARCH_ARM7_LPC2000 #ifdef _ARCH_ARM7_LPC2000
#include "serial_lpc2000.h" #include "serial_lpc2000.h"
#elif defined(_ARCH_CORTEXM0_STM32) || defined(_ARCH_CORTEXM3_STM32) \ #elif defined(_ARCH_CORTEXM0_STM32F0) || defined(_ARCH_CORTEXM3_STM32F1) \
|| defined(_ARCH_CORTEXM4_STM32F4) || defined(_ARCH_CORTEXM3_STM32F2) \ || defined(_ARCH_CORTEXM4_STM32F4) || defined(_ARCH_CORTEXM3_STM32F2) \
|| defined(_ARCH_CORTEXM3_STM32L1) || defined(_ARCH_CORTEXM7_STM32F7) \ || defined(_ARCH_CORTEXM3_STM32L1) || defined(_ARCH_CORTEXM7_STM32F7) \
|| defined(_ARCH_CORTEXM7_STM32H7) || defined(_ARCH_CORTEXM4_STM32F3) \ || defined(_ARCH_CORTEXM7_STM32H7) || defined(_ARCH_CORTEXM4_STM32F3) \
......
...@@ -83,7 +83,7 @@ ATSAMSerial::ATSAMSerial(int id, int baudrate) ...@@ -83,7 +83,7 @@ ATSAMSerial::ATSAMSerial(int id, int baudrate)
port=USART2; port=USART2;
//TODO: USART2 hardcoded //TODO: USART2 hardcoded
PM->PM_UNLOCK=0xaa<<24 | PM_PBAMASK_OFFSET; PM->PM_UNLOCK = PM_UNLOCK_KEY(0xaa) | PM_UNLOCK_ADDR(PM_PBAMASK_OFFSET);
PM->PM_PBAMASK |= PM_PBAMASK_USART2; PM->PM_PBAMASK |= PM_PBAMASK_USART2;
NVIC_SetPriority(USART2_IRQn,15);//Lowest priority for serial NVIC_SetPriority(USART2_IRQn,15);//Lowest priority for serial
NVIC_EnableIRQ(USART2_IRQn); NVIC_EnableIRQ(USART2_IRQn);
...@@ -233,7 +233,7 @@ ATSAMSerial::~ATSAMSerial() ...@@ -233,7 +233,7 @@ ATSAMSerial::~ATSAMSerial()
//TODO: USART2 hardcoded //TODO: USART2 hardcoded
NVIC_DisableIRQ(USART2_IRQn); NVIC_DisableIRQ(USART2_IRQn);
NVIC_ClearPendingIRQ(USART2_IRQn); NVIC_ClearPendingIRQ(USART2_IRQn);
PM->PM_UNLOCK=0xaa<<24 | PM_PBAMASK_OFFSET; PM->PM_UNLOCK = PM_UNLOCK_KEY(0xaa) | PM_UNLOCK_ADDR(PM_PBAMASK_OFFSET);
PM->PM_PBAMASK &= ~PM_PBAMASK_USART2; PM->PM_PBAMASK &= ~PM_PBAMASK_USART2;
} }
......
...@@ -30,7 +30,6 @@ ...@@ -30,7 +30,6 @@
#include "filesystem/console/console_device.h" #include "filesystem/console/console_device.h"
#include "kernel/sync.h" #include "kernel/sync.h"
#include "kernel/queue.h" #include "kernel/queue.h"
#include "board_settings.h"
namespace miosix { namespace miosix {
......
...@@ -31,7 +31,6 @@ ...@@ -31,7 +31,6 @@
#include "filesystem/console/console_device.h" #include "filesystem/console/console_device.h"
#include "kernel/sync.h" #include "kernel/sync.h"
#include "kernel/queue.h" #include "kernel/queue.h"
#include "board_settings.h"
namespace miosix { namespace miosix {
......
...@@ -87,7 +87,8 @@ void __attribute__ ((interrupt("IRQ"),naked)) usart1irq() ...@@ -87,7 +87,8 @@ void __attribute__ ((interrupt("IRQ"),naked)) usart1irq()
// 20ms of full data rate. In the 8N1 format one char is made of 10 bits. // 20ms of full data rate. In the 8N1 format one char is made of 10 bits.
// So (baudrate/10)*0.02=baudrate/500 // So (baudrate/10)*0.02=baudrate/500
LPC2000Serial::LPC2000Serial(int id, int baudrate) : Device(Device::TTY), LPC2000Serial::LPC2000Serial(int id, int baudrate) : Device(Device::TTY),
rxQueue(hwRxQueueLen+baudrate/500), rxWaiting(0), idle(true) txQueue(swTxQueue), rxQueue(hwRxQueueLen+baudrate/500),
txWaiting(nullptr), rxWaiting(nullptr), idle(true)
{ {
InterruptDisableLock dLock; InterruptDisableLock dLock;
if(id<0 || id>1 || ports[id]!=0) errorHandler(UNEXPECTED); if(id<0 || id>1 || ports[id]!=0) errorHandler(UNEXPECTED);
...@@ -177,13 +178,13 @@ ssize_t LPC2000Serial::writeBlock(const void *buffer, size_t size, off_t where) ...@@ -177,13 +178,13 @@ ssize_t LPC2000Serial::writeBlock(const void *buffer, size_t size, off_t where)
if(len==0) break; if(len==0) break;
} }
} else { } else {
if(txQueue.IRQput(*buf)==true) if(txQueue.tryPut(*buf))
{ {
buf++; buf++;
len--; len--;
} else { } else {
FastInterruptEnableLock eLock(dLock); txWaiting=Thread::IRQgetCurrentThread();
txQueue.waitUntilNotFull(); while(txWaiting) Thread::IRQenableIrqAndWait(dLock);
} }
} }
} }
...@@ -256,8 +257,14 @@ void LPC2000Serial::IRQhandleInterrupt() ...@@ -256,8 +257,14 @@ void LPC2000Serial::IRQhandleInterrupt()
case 0x2: //THRE case 0x2: //THRE
for(int i=0;i<hwTxQueueLen;i++) for(int i=0;i<hwTxQueueLen;i++)
{ {
//If software queue empty, stop if(txWaiting)
if(txQueue.IRQget(c,hppw)==false) break; {
txWaiting->IRQwakeup();
if(txWaiting->IRQgetPriority()>
Thread::IRQgetCurrentThread()->IRQgetPriority()) hppw=true;
txWaiting=nullptr;
}
if(txQueue.tryGet(c)==false) break; //If software queue empty, stop
serial->THR=c; serial->THR=c;
} }
break; break;
...@@ -267,7 +274,7 @@ void LPC2000Serial::IRQhandleInterrupt() ...@@ -267,7 +274,7 @@ void LPC2000Serial::IRQhandleInterrupt()
rxWaiting->IRQwakeup(); rxWaiting->IRQwakeup();
if(rxWaiting->IRQgetPriority()> if(rxWaiting->IRQgetPriority()>
Thread::IRQgetCurrentThread()->IRQgetPriority()) hppw=true; Thread::IRQgetCurrentThread()->IRQgetPriority()) hppw=true;
rxWaiting=0; rxWaiting=nullptr;
} }
if(hppw) Scheduler::IRQfindNextThread(); if(hppw) Scheduler::IRQfindNextThread();
} }
......
...@@ -171,8 +171,9 @@ private: ...@@ -171,8 +171,9 @@ private:
FastMutex txMutex;///< Mutex used to guard the tx queue FastMutex txMutex;///< Mutex used to guard the tx queue
FastMutex rxMutex;///< Mutex used to guard the rx queue FastMutex rxMutex;///< Mutex used to guard the rx queue
Queue<char,swTxQueue> txQueue;///< Tx software queue DynUnsyncQueue<char> txQueue;///< Rx software queue
DynUnsyncQueue<char> rxQueue;///< Rx software queue DynUnsyncQueue<char> rxQueue;///< Rx software queue
Thread *txWaiting; ///< Thread waiting on rx queue
Thread *rxWaiting; ///< Thread waiting on rx queue Thread *rxWaiting; ///< Thread waiting on rx queue
bool idle; ///< Receiver idle bool idle; ///< Receiver idle
......
...@@ -73,7 +73,7 @@ void __attribute__((noinline)) usart1irqImpl() ...@@ -73,7 +73,7 @@ void __attribute__((noinline)) usart1irqImpl()
/** /**
* \internal interrupt routine for usart1 * \internal interrupt routine for usart1
*/ */
void __attribute__((naked)) USART1_IRQHandler() void __attribute__((naked, weak)) USART1_IRQHandler()
{ {
saveContext(); saveContext();
asm volatile("bl _Z13usart1irqImplv"); asm volatile("bl _Z13usart1irqImplv");
...@@ -93,7 +93,7 @@ void __attribute__((noinline)) usart2irqImpl() ...@@ -93,7 +93,7 @@ void __attribute__((noinline)) usart2irqImpl()
/** /**
* \internal interrupt routine for usart2 * \internal interrupt routine for usart2
*/ */
void __attribute__((naked)) USART2_IRQHandler() void __attribute__((naked, weak)) USART2_IRQHandler()
{ {
saveContext(); saveContext();
asm volatile("bl _Z13usart2irqImplv"); asm volatile("bl _Z13usart2irqImplv");
...@@ -113,14 +113,14 @@ void __attribute__((noinline)) usart3irqImpl() ...@@ -113,14 +113,14 @@ void __attribute__((noinline)) usart3irqImpl()
* \internal interrupt routine for usart3 * \internal interrupt routine for usart3
*/ */
#if !defined(STM32F072xB) #if !defined(STM32F072xB)
void __attribute__((naked)) USART3_IRQHandler() void __attribute__((naked, weak)) USART3_IRQHandler()
{ {
saveContext(); saveContext();
asm volatile("bl _Z13usart3irqImplv"); asm volatile("bl _Z13usart3irqImplv");
restoreContext(); restoreContext();
} }
#else //!defined(STM32F072xB) #else //!defined(STM32F072xB)
void __attribute__((naked)) USART3_4_IRQHandler() void __attribute__((naked, weak)) USART3_4_IRQHandler()
{ {
saveContext(); saveContext();
asm volatile("bl _Z13usart3irqImplv"); asm volatile("bl _Z13usart3irqImplv");
...@@ -137,7 +137,7 @@ void __attribute__((naked)) USART3_4_IRQHandler() ...@@ -137,7 +137,7 @@ void __attribute__((naked)) USART3_4_IRQHandler()
*/ */
void __attribute__((noinline)) usart1txDmaImpl() void __attribute__((noinline)) usart1txDmaImpl()
{ {
#if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \ #if defined(_ARCH_CORTEXM3_STM32F1) || defined (_ARCH_CORTEXM4_STM32F3) \
|| defined(_ARCH_CORTEXM4_STM32L4) || defined(_ARCH_CORTEXM4_STM32L4)
DMA1->IFCR=DMA_IFCR_CGIF4; DMA1->IFCR=DMA_IFCR_CGIF4;
DMA1_Channel4->CCR=0; //Disable DMA DMA1_Channel4->CCR=0; //Disable DMA
...@@ -158,7 +158,7 @@ void __attribute__((noinline)) usart1rxDmaImpl() ...@@ -158,7 +158,7 @@ void __attribute__((noinline)) usart1rxDmaImpl()
if(ports[0]) ports[0]->IRQhandleDMArx(); if(ports[0]) ports[0]->IRQhandleDMArx();
} }
#if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \ #if defined(_ARCH_CORTEXM3_STM32F1) || defined (_ARCH_CORTEXM4_STM32F3) \
|| defined(_ARCH_CORTEXM4_STM32L4) || defined(_ARCH_CORTEXM4_STM32L4)
/** /**
* \internal DMA1 Channel 4 IRQ (configured as USART1 TX) * \internal DMA1 Channel 4 IRQ (configured as USART1 TX)
...@@ -211,7 +211,7 @@ void __attribute__((naked)) DMA2_Stream5_IRQHandler() ...@@ -211,7 +211,7 @@ void __attribute__((naked)) DMA2_Stream5_IRQHandler()
*/ */
void __attribute__((noinline)) usart2txDmaImpl() void __attribute__((noinline)) usart2txDmaImpl()
{ {
#if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \ #if defined(_ARCH_CORTEXM3_STM32F1) || defined (_ARCH_CORTEXM4_STM32F3) \
|| defined(_ARCH_CORTEXM4_STM32L4) || defined(_ARCH_CORTEXM4_STM32L4)
DMA1->IFCR=DMA_IFCR_CGIF7; DMA1->IFCR=DMA_IFCR_CGIF7;
DMA1_Channel7->CCR=0; //Disable DMA DMA1_Channel7->CCR=0; //Disable DMA
...@@ -232,7 +232,7 @@ void __attribute__((noinline)) usart2rxDmaImpl() ...@@ -232,7 +232,7 @@ void __attribute__((noinline)) usart2rxDmaImpl()
if(ports[1]) ports[1]->IRQhandleDMArx(); if(ports[1]) ports[1]->IRQhandleDMArx();
} }
#if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \ #if defined(_ARCH_CORTEXM3_STM32F1) || defined (_ARCH_CORTEXM4_STM32F3) \
|| defined(_ARCH_CORTEXM4_STM32L4) || defined(_ARCH_CORTEXM4_STM32L4)
/** /**
* \internal DMA1 Channel 7 IRQ (configured as USART2 TX) * \internal DMA1 Channel 7 IRQ (configured as USART2 TX)
...@@ -285,7 +285,7 @@ void __attribute__((naked)) DMA1_Stream5_IRQHandler() ...@@ -285,7 +285,7 @@ void __attribute__((naked)) DMA1_Stream5_IRQHandler()
*/ */
void __attribute__((noinline)) usart3txDmaImpl() void __attribute__((noinline)) usart3txDmaImpl()
{ {
#if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \ #if defined(_ARCH_CORTEXM3_STM32F1) || defined (_ARCH_CORTEXM4_STM32F3) \
|| defined(_ARCH_CORTEXM4_STM32L4) || defined(_ARCH_CORTEXM4_STM32L4)
DMA1->IFCR=DMA_IFCR_CGIF2; DMA1->IFCR=DMA_IFCR_CGIF2;
DMA1_Channel2->CCR=0; //Disable DMA DMA1_Channel2->CCR=0; //Disable DMA
...@@ -306,7 +306,7 @@ void __attribute__((noinline)) usart3rxDmaImpl() ...@@ -306,7 +306,7 @@ void __attribute__((noinline)) usart3rxDmaImpl()
if(ports[2]) ports[2]->IRQhandleDMArx(); if(ports[2]) ports[2]->IRQhandleDMArx();
} }
#if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \ #if defined(_ARCH_CORTEXM3_STM32F1) || defined (_ARCH_CORTEXM4_STM32F3) \
|| defined(_ARCH_CORTEXM4_STM32L4) || defined(_ARCH_CORTEXM4_STM32L4)
/** /**
* \internal DMA1 Channel 2 IRQ (configured as USART3 TX) * \internal DMA1 Channel 2 IRQ (configured as USART3 TX)
...@@ -385,14 +385,14 @@ STM32Serial::STM32Serial(int id, int baudrate, FlowCtrl flowControl) ...@@ -385,14 +385,14 @@ STM32Serial::STM32Serial(int id, int baudrate, FlowCtrl flowControl)
: Device(Device::TTY), rxQueue(rxQueueMin+baudrate/500), : Device(Device::TTY), rxQueue(rxQueueMin+baudrate/500),
flowControl(flowControl==RTSCTS), portId(id) flowControl(flowControl==RTSCTS), portId(id)
{ {
#if !defined(_ARCH_CORTEXM3_STM32) #if !defined(_ARCH_CORTEXM3_STM32F1)
//stm32f2, f4, l4, l1, f7, h7 require alternate function mapping //stm32f2, f4, l4, l1, f7, h7 require alternate function mapping
//stm32f0 family has different alternate function mapping //stm32f0 family has different alternate function mapping
//with respect to the other families //with respect to the other families
switch(id) switch(id)
{ {
case 1: case 1:
#if !defined(_ARCH_CORTEXM0_STM32) #if !defined(_ARCH_CORTEXM0_STM32F0)
u1tx::alternateFunction(7); u1tx::alternateFunction(7);
u1rx::alternateFunction(7); u1rx::alternateFunction(7);
if(flowControl) if(flowControl)
...@@ -400,7 +400,7 @@ STM32Serial::STM32Serial(int id, int baudrate, FlowCtrl flowControl) ...@@ -400,7 +400,7 @@ STM32Serial::STM32Serial(int id, int baudrate, FlowCtrl flowControl)
u1rts::alternateFunction(7); u1rts::alternateFunction(7);
u1cts::alternateFunction(7); u1cts::alternateFunction(7);
} }
#else //!defined(_ARCH_CORTEXM0_STM32) #else //!defined(_ARCH_CORTEXM0_STM32F0)
u1tx::alternateFunction(1); u1tx::alternateFunction(1);
u1rx::alternateFunction(1); u1rx::alternateFunction(1);
if(flowControl) if(flowControl)
...@@ -408,10 +408,10 @@ STM32Serial::STM32Serial(int id, int baudrate, FlowCtrl flowControl) ...@@ -408,10 +408,10 @@ STM32Serial::STM32Serial(int id, int baudrate, FlowCtrl flowControl)
u1rts::alternateFunction(1); u1rts::alternateFunction(1);
u1cts::alternateFunction(1); u1cts::alternateFunction(1);
} }
#endif //!defined(_ARCH_CORTEXM0_STM32) #endif //!defined(_ARCH_CORTEXM0_STM32F0)
break; break;
case 2: case 2:
#if !defined(_ARCH_CORTEXM0_STM32) #if !defined(_ARCH_CORTEXM0_STM32F0)
u2tx::alternateFunction(7); u2tx::alternateFunction(7);
u2rx::alternateFunction(7); u2rx::alternateFunction(7);
if(flowControl) if(flowControl)
...@@ -419,7 +419,7 @@ STM32Serial::STM32Serial(int id, int baudrate, FlowCtrl flowControl) ...@@ -419,7 +419,7 @@ STM32Serial::STM32Serial(int id, int baudrate, FlowCtrl flowControl)
u2rts::alternateFunction(7); u2rts::alternateFunction(7);
u2cts::alternateFunction(7); u2cts::alternateFunction(7);
} }
#else //!defined(_ARCH_CORTEXM0_STM32) #else //!defined(_ARCH_CORTEXM0_STM32F0)
u2tx::alternateFunction(1); u2tx::alternateFunction(1);
u2rx::alternateFunction(1); u2rx::alternateFunction(1);
if(flowControl) if(flowControl)
...@@ -427,10 +427,10 @@ STM32Serial::STM32Serial(int id, int baudrate, FlowCtrl flowControl) ...@@ -427,10 +427,10 @@ STM32Serial::STM32Serial(int id, int baudrate, FlowCtrl flowControl)
u2rts::alternateFunction(1); u2rts::alternateFunction(1);
u2cts::alternateFunction(1); u2cts::alternateFunction(1);
} }
#endif //!defined(_ARCH_CORTEXM0_STM32) #endif //!defined(_ARCH_CORTEXM0_STM32F0)
break; break;
case 3: case 3:
#if !defined(_ARCH_CORTEXM0_STM32) #if !defined(_ARCH_CORTEXM0_STM32F0)
u3tx::alternateFunction(7); u3tx::alternateFunction(7);
u3rx::alternateFunction(7); u3rx::alternateFunction(7);
if(flowControl) if(flowControl)
...@@ -438,7 +438,7 @@ STM32Serial::STM32Serial(int id, int baudrate, FlowCtrl flowControl) ...@@ -438,7 +438,7 @@ STM32Serial::STM32Serial(int id, int baudrate, FlowCtrl flowControl)
u3rts::alternateFunction(7); u3rts::alternateFunction(7);
u3cts::alternateFunction(7); u3cts::alternateFunction(7);
} }
#else //!defined(_ARCH_CORTEXM0_STM32) #else //!defined(_ARCH_CORTEXM0_STM32F0)
u3tx::alternateFunction(4); u3tx::alternateFunction(4);
u3rx::alternateFunction(4); u3rx::alternateFunction(4);
if(flowControl) if(flowControl)
...@@ -446,10 +446,10 @@ STM32Serial::STM32Serial(int id, int baudrate, FlowCtrl flowControl) ...@@ -446,10 +446,10 @@ STM32Serial::STM32Serial(int id, int baudrate, FlowCtrl flowControl)
u3rts::alternateFunction(4); u3rts::alternateFunction(4);
u3cts::alternateFunction(4); u3cts::alternateFunction(4);
} }
#endif //!defined(_ARCH_CORTEXM0_STM32) #endif //!defined(_ARCH_CORTEXM0_STM32F0)
break; break;
} }
#endif //_ARCH_CORTEXM3_STM32 #endif //_ARCH_CORTEXM3_STM32F1
switch(id) switch(id)
{ {
...@@ -499,11 +499,11 @@ void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx, ...@@ -499,11 +499,11 @@ void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx,
//Quirk the position of the PPRE1 and PPRE2 bitfields in RCC->CFGR changes //Quirk the position of the PPRE1 and PPRE2 bitfields in RCC->CFGR changes
//STM32F0 does not have ppre1 and ppre2, in this case the variables are not //STM32F0 does not have ppre1 and ppre2, in this case the variables are not
//defined in order to avoid "unused variable" warning //defined in order to avoid "unused variable" warning
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM3_STM32L1) \ #if defined(_ARCH_CORTEXM3_STM32F1) || defined(_ARCH_CORTEXM3_STM32L1) \
|| defined(_ARCH_CORTEXM4_STM32F3) || defined(_ARCH_CORTEXM4_STM32L4) || defined(_ARCH_CORTEXM4_STM32F3) || defined(_ARCH_CORTEXM4_STM32L4)
const unsigned int ppre1=8; const unsigned int ppre1=8;
const unsigned int ppre2=11; const unsigned int ppre2=11;
#elif !defined(_ARCH_CORTEXM7_STM32H7) && !defined(_ARCH_CORTEXM0_STM32) #elif !defined(_ARCH_CORTEXM7_STM32H7) && !defined(_ARCH_CORTEXM0_STM32F0)
const unsigned int ppre1=10; const unsigned int ppre1=10;
const unsigned int ppre2=13; const unsigned int ppre2=13;
#endif #endif
...@@ -514,7 +514,7 @@ void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx, ...@@ -514,7 +514,7 @@ void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx,
RCC->APB2ENR |= RCC_APB2ENR_USART1EN; RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
RCC_SYNC(); RCC_SYNC();
#ifdef SERIAL_1_DMA #ifdef SERIAL_1_DMA
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \ #if defined(_ARCH_CORTEXM3_STM32F1) || defined(_ARCH_CORTEXM4_STM32F3) \
|| defined(_ARCH_CORTEXM4_STM32L4) || defined(_ARCH_CORTEXM4_STM32L4)
#ifdef _ARCH_CORTEXM4_STM32L4 #ifdef _ARCH_CORTEXM4_STM32L4
RCC->AHB1ENR |= RCC_AHBENR_DMA1EN; RCC->AHB1ENR |= RCC_AHBENR_DMA1EN;
...@@ -548,9 +548,9 @@ void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx, ...@@ -548,9 +548,9 @@ void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx,
#endif //SERIAL_1_DMA #endif //SERIAL_1_DMA
NVIC_SetPriority(USART1_IRQn,15);//Lowest priority for serial NVIC_SetPriority(USART1_IRQn,15);//Lowest priority for serial
NVIC_EnableIRQ(USART1_IRQn); NVIC_EnableIRQ(USART1_IRQn);
#if !defined(_ARCH_CORTEXM7_STM32H7) && !defined(_ARCH_CORTEXM0_STM32) #if !defined(_ARCH_CORTEXM7_STM32H7) && !defined(_ARCH_CORTEXM0_STM32F0)
if(RCC->CFGR & RCC_CFGR_PPRE2_2) freq/=1<<(((RCC->CFGR>>ppre2) & 0x3)+1); if(RCC->CFGR & RCC_CFGR_PPRE2_2) freq/=1<<(((RCC->CFGR>>ppre2) & 0x3)+1);
#elif defined(_ARCH_CORTEXM0_STM32) #elif defined(_ARCH_CORTEXM0_STM32F0)
// STM32F0 family has only PPRE2 register // STM32F0 family has only PPRE2 register
if(RCC->CFGR & RCC_CFGR_PPRE_2) freq/=1<<(((RCC->CFGR>>8) & 0x3)+1); if(RCC->CFGR & RCC_CFGR_PPRE_2) freq/=1<<(((RCC->CFGR>>8) & 0x3)+1);
#else #else
...@@ -579,7 +579,7 @@ void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx, ...@@ -579,7 +579,7 @@ void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx,
#endif //_ARCH_CORTEXM7_STM32H7 #endif //_ARCH_CORTEXM7_STM32H7
RCC_SYNC(); RCC_SYNC();
#ifdef SERIAL_2_DMA #ifdef SERIAL_2_DMA
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \ #if defined(_ARCH_CORTEXM3_STM32F1) || defined(_ARCH_CORTEXM4_STM32F3) \
|| defined(_ARCH_CORTEXM4_STM32L4) || defined(_ARCH_CORTEXM4_STM32L4)
#ifdef _ARCH_CORTEXM4_STM32L4 #ifdef _ARCH_CORTEXM4_STM32L4
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN; RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
...@@ -613,9 +613,9 @@ void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx, ...@@ -613,9 +613,9 @@ void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx,
#endif //SERIAL_2_DMA #endif //SERIAL_2_DMA
NVIC_SetPriority(USART2_IRQn,15);//Lowest priority for serial NVIC_SetPriority(USART2_IRQn,15);//Lowest priority for serial
NVIC_EnableIRQ(USART2_IRQn); NVIC_EnableIRQ(USART2_IRQn);
#if !defined(_ARCH_CORTEXM7_STM32H7) && !defined(_ARCH_CORTEXM0_STM32) #if !defined(_ARCH_CORTEXM7_STM32H7) && !defined(_ARCH_CORTEXM0_STM32F0)
if(RCC->CFGR & RCC_CFGR_PPRE1_2) freq/=1<<(((RCC->CFGR>>ppre1) & 0x3)+1); if(RCC->CFGR & RCC_CFGR_PPRE1_2) freq/=1<<(((RCC->CFGR>>ppre1) & 0x3)+1);
#elif defined(_ARCH_CORTEXM0_STM32) #elif defined(_ARCH_CORTEXM0_STM32F0)
// STM32F0 family has only PPRE2 register // STM32F0 family has only PPRE2 register
if(RCC->CFGR & RCC_CFGR_PPRE_2) freq/=1<<(((RCC->CFGR>>8) & 0x3)+1); if(RCC->CFGR & RCC_CFGR_PPRE_2) freq/=1<<(((RCC->CFGR>>8) & 0x3)+1);
#else //_ARCH_CORTEXM7_STM32H7 #else //_ARCH_CORTEXM7_STM32H7
...@@ -643,7 +643,7 @@ void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx, ...@@ -643,7 +643,7 @@ void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx,
#endif //_ARCH_CORTEXM7_STM32H7 #endif //_ARCH_CORTEXM7_STM32H7
RCC_SYNC(); RCC_SYNC();
#ifdef SERIAL_3_DMA #ifdef SERIAL_3_DMA
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \ #if defined(_ARCH_CORTEXM3_STM32F1) || defined(_ARCH_CORTEXM4_STM32F3) \
|| defined(_ARCH_CORTEXM4_STM32L4) || defined(_ARCH_CORTEXM4_STM32L4)
#ifdef _ARCH_CORTEXM4_STM32L4 #ifdef _ARCH_CORTEXM4_STM32L4
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN; RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
...@@ -682,9 +682,9 @@ void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx, ...@@ -682,9 +682,9 @@ void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx,
NVIC_SetPriority(USART3_4_IRQn,15); NVIC_SetPriority(USART3_4_IRQn,15);
NVIC_EnableIRQ(USART3_4_IRQn); NVIC_EnableIRQ(USART3_4_IRQn);
#endif //STM32F072xB #endif //STM32F072xB
#if !defined(_ARCH_CORTEXM7_STM32H7) && !defined(_ARCH_CORTEXM0_STM32) #if !defined(_ARCH_CORTEXM7_STM32H7) && !defined(_ARCH_CORTEXM0_STM32F0)
if(RCC->CFGR & RCC_CFGR_PPRE1_2) freq/=1<<(((RCC->CFGR>>ppre1) & 0x3)+1); if(RCC->CFGR & RCC_CFGR_PPRE1_2) freq/=1<<(((RCC->CFGR>>ppre1) & 0x3)+1);
#elif defined(_ARCH_CORTEXM0_STM32) #elif defined(_ARCH_CORTEXM0_STM32F0)
// STM32F0 family has only PPRE2 register // STM32F0 family has only PPRE2 register
if(RCC->CFGR & RCC_CFGR_PPRE_2) freq/=1<<(((RCC->CFGR>>8) & 0x3)+1); if(RCC->CFGR & RCC_CFGR_PPRE_2) freq/=1<<(((RCC->CFGR>>8) & 0x3)+1);
#else //_ARCH_CORTEXM7_STM32H7 #else //_ARCH_CORTEXM7_STM32H7
...@@ -703,11 +703,11 @@ void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx, ...@@ -703,11 +703,11 @@ void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx,
} }
//Quirk: stm32f1 rx pin has to be in input mode, while stm32f2 and up want //Quirk: stm32f1 rx pin has to be in input mode, while stm32f2 and up want
//it in ALTERNATE mode. Go figure... //it in ALTERNATE mode. Go figure...
#ifdef _ARCH_CORTEXM3_STM32 #ifdef _ARCH_CORTEXM3_STM32F1
Mode::Mode_ rxPinMode=Mode::INPUT; Mode::Mode_ rxPinMode=Mode::INPUT;
#else //_ARCH_CORTEXM3_STM32 #else //_ARCH_CORTEXM3_STM32F1
Mode::Mode_ rxPinMode=Mode::ALTERNATE; Mode::Mode_ rxPinMode=Mode::ALTERNATE;
#endif //_ARCH_CORTEXM3_STM32 #endif //_ARCH_CORTEXM3_STM32F1
tx.mode(Mode::ALTERNATE); tx.mode(Mode::ALTERNATE);
rx.mode(rxPinMode); rx.mode(rxPinMode);
if(flowControl) if(flowControl)
...@@ -744,6 +744,7 @@ ssize_t STM32Serial::readBlock(void *buffer, size_t size, off_t where) ...@@ -744,6 +744,7 @@ ssize_t STM32Serial::readBlock(void *buffer, size_t size, off_t where)
char *buf=reinterpret_cast<char*>(buffer); char *buf=reinterpret_cast<char*>(buffer);
size_t result=0; size_t result=0;
FastInterruptDisableLock dLock; FastInterruptDisableLock dLock;
DeepSleepLock dpLock;
for(;;) for(;;)
{ {
//Try to get data from the queue //Try to get data from the queue
...@@ -771,6 +772,7 @@ ssize_t STM32Serial::readBlock(void *buffer, size_t size, off_t where) ...@@ -771,6 +772,7 @@ ssize_t STM32Serial::readBlock(void *buffer, size_t size, off_t where)
ssize_t STM32Serial::writeBlock(const void *buffer, size_t size, off_t where) ssize_t STM32Serial::writeBlock(const void *buffer, size_t size, off_t where)
{ {
Lock<FastMutex> l(txMutex); Lock<FastMutex> l(txMutex);
DeepSleepLock dpLock;
const char *buf=reinterpret_cast<const char*>(buffer); const char *buf=reinterpret_cast<const char*>(buffer);
#ifdef SERIAL_DMA #ifdef SERIAL_DMA
if(dmaTx) if(dmaTx)
...@@ -806,7 +808,7 @@ ssize_t STM32Serial::writeBlock(const void *buffer, size_t size, off_t where) ...@@ -806,7 +808,7 @@ ssize_t STM32Serial::writeBlock(const void *buffer, size_t size, off_t where)
for(size_t i=0;i<size;i++) for(size_t i=0;i<size;i++)
{ {
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \ #if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
&& !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \ && !defined(_ARCH_CORTEXM0_STM32F0) && !defined(_ARCH_CORTEXM4_STM32F3) \
&& !defined(_ARCH_CORTEXM4_STM32L4) && !defined(_ARCH_CORTEXM4_STM32L4)
while((port->SR & USART_SR_TXE)==0) ; while((port->SR & USART_SR_TXE)==0) ;
port->DR=*buf++; port->DR=*buf++;
...@@ -827,7 +829,7 @@ void STM32Serial::IRQwrite(const char *str) ...@@ -827,7 +829,7 @@ void STM32Serial::IRQwrite(const char *str)
#ifdef SERIAL_DMA #ifdef SERIAL_DMA
if(dmaTx) if(dmaTx)
{ {
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \ #if defined(_ARCH_CORTEXM3_STM32F1) || defined(_ARCH_CORTEXM4_STM32F3) \
|| defined(_ARCH_CORTEXM4_STM32L4) || defined(_ARCH_CORTEXM4_STM32L4)
//If no DMA transfer is in progress bit EN is zero. Otherwise wait until //If no DMA transfer is in progress bit EN is zero. Otherwise wait until
//DMA xfer ends, by waiting for the TC (or TE) interrupt flag //DMA xfer ends, by waiting for the TC (or TE) interrupt flag
...@@ -842,16 +844,16 @@ void STM32Serial::IRQwrite(const char *str) ...@@ -842,16 +844,16 @@ void STM32Serial::IRQwrite(const char *str)
constexpr unsigned int DMA_CCR4_EN = DMA_CCR_EN; constexpr unsigned int DMA_CCR4_EN = DMA_CCR_EN;
#endif #endif
while((dmaTx->CCR & DMA_CCR4_EN) && !(DMA1->ISR & irqMask[getId()-1])) ; while((dmaTx->CCR & DMA_CCR4_EN) && !(DMA1->ISR & irqMask[getId()-1])) ;
#else //_ARCH_CORTEXM3_STM32 #else //_ARCH_CORTEXM3_STM32F1
//Wait until DMA xfer ends. EN bit is cleared by hardware on transfer end //Wait until DMA xfer ends. EN bit is cleared by hardware on transfer end
while(dmaTx->CR & DMA_SxCR_EN) ; while(dmaTx->CR & DMA_SxCR_EN) ;
#endif //_ARCH_CORTEXM3_STM32 #endif //_ARCH_CORTEXM3_STM32F1
} }
#endif //SERIAL_DMA #endif //SERIAL_DMA
while(*str) while(*str)
{ {
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \ #if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
&& !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \ && !defined(_ARCH_CORTEXM0_STM32F0) && !defined(_ARCH_CORTEXM4_STM32F3) \
&& !defined(_ARCH_CORTEXM4_STM32L4) && !defined(_ARCH_CORTEXM4_STM32L4)
while((port->SR & USART_SR_TXE)==0) ; while((port->SR & USART_SR_TXE)==0) ;
port->DR=*str++; port->DR=*str++;
...@@ -893,7 +895,7 @@ int STM32Serial::ioctl(int cmd, void* arg) ...@@ -893,7 +895,7 @@ int STM32Serial::ioctl(int cmd, void* arg)
void STM32Serial::IRQhandleInterrupt() void STM32Serial::IRQhandleInterrupt()
{ {
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \ #if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
&& !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \ && !defined(_ARCH_CORTEXM0_STM32F0) && !defined(_ARCH_CORTEXM4_STM32F3) \
&& !defined(_ARCH_CORTEXM4_STM32L4) && !defined(_ARCH_CORTEXM4_STM32L4)
unsigned int status=port->SR; unsigned int status=port->SR;
#else //_ARCH_CORTEXM7_STM32F7/H7 #else //_ARCH_CORTEXM7_STM32F7/H7
...@@ -911,7 +913,7 @@ void STM32Serial::IRQhandleInterrupt() ...@@ -911,7 +913,7 @@ void STM32Serial::IRQhandleInterrupt()
{ {
//Always read data, since this clears interrupt flags //Always read data, since this clears interrupt flags
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \ #if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
&& !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \ && !defined(_ARCH_CORTEXM0_STM32F0) && !defined(_ARCH_CORTEXM4_STM32F3) \
&& !defined(_ARCH_CORTEXM4_STM32L4) && !defined(_ARCH_CORTEXM4_STM32L4)
c=port->DR; c=port->DR;
#else //_ARCH_CORTEXM7_STM32F7/H7 #else //_ARCH_CORTEXM7_STM32F7/H7
...@@ -925,7 +927,7 @@ void STM32Serial::IRQhandleInterrupt() ...@@ -925,7 +927,7 @@ void STM32Serial::IRQhandleInterrupt()
if(status & USART_SR_IDLE) if(status & USART_SR_IDLE)
{ {
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \ #if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
&& !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \ && !defined(_ARCH_CORTEXM0_STM32F0) && !defined(_ARCH_CORTEXM4_STM32F3) \
&& !defined(_ARCH_CORTEXM4_STM32L4) && !defined(_ARCH_CORTEXM4_STM32L4)
c=port->DR; //clears interrupt flags c=port->DR; //clears interrupt flags
#else //_ARCH_CORTEXM7_STM32F7/H7 #else //_ARCH_CORTEXM7_STM32F7/H7
...@@ -986,7 +988,7 @@ STM32Serial::~STM32Serial() ...@@ -986,7 +988,7 @@ STM32Serial::~STM32Serial()
case 1: case 1:
#ifdef SERIAL_1_DMA #ifdef SERIAL_1_DMA
IRQdmaReadStop(); IRQdmaReadStop();
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \ #if defined(_ARCH_CORTEXM3_STM32F1) || defined(_ARCH_CORTEXM4_STM32F3) \
|| defined(_ARCH_CORTEXM4_STM32L4) || defined(_ARCH_CORTEXM4_STM32L4)
NVIC_DisableIRQ(DMA1_Channel4_IRQn); NVIC_DisableIRQ(DMA1_Channel4_IRQn);
NVIC_ClearPendingIRQ(DMA1_Channel4_IRQn); NVIC_ClearPendingIRQ(DMA1_Channel4_IRQn);
...@@ -1008,7 +1010,7 @@ STM32Serial::~STM32Serial() ...@@ -1008,7 +1010,7 @@ STM32Serial::~STM32Serial()
case 2: case 2:
#ifdef SERIAL_2_DMA #ifdef SERIAL_2_DMA
IRQdmaReadStop(); IRQdmaReadStop();
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \ #if defined(_ARCH_CORTEXM3_STM32F1) || defined(_ARCH_CORTEXM4_STM32F3) \
|| defined(_ARCH_CORTEXM4_STM32L4) || defined(_ARCH_CORTEXM4_STM32L4)
NVIC_DisableIRQ(DMA1_Channel7_IRQn); NVIC_DisableIRQ(DMA1_Channel7_IRQn);
NVIC_ClearPendingIRQ(DMA1_Channel7_IRQn); NVIC_ClearPendingIRQ(DMA1_Channel7_IRQn);
...@@ -1037,7 +1039,7 @@ STM32Serial::~STM32Serial() ...@@ -1037,7 +1039,7 @@ STM32Serial::~STM32Serial()
case 3: case 3:
#ifdef SERIAL_3_DMA #ifdef SERIAL_3_DMA
IRQdmaReadStop(); IRQdmaReadStop();
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \ #if defined(_ARCH_CORTEXM3_STM32F1) || defined(_ARCH_CORTEXM4_STM32F3) \
|| defined(_ARCH_CORTEXM4_STM32L4) || defined(_ARCH_CORTEXM4_STM32L4)
NVIC_DisableIRQ(DMA1_Channel2_IRQn); NVIC_DisableIRQ(DMA1_Channel2_IRQn);
NVIC_ClearPendingIRQ(DMA1_Channel2_IRQn); NVIC_ClearPendingIRQ(DMA1_Channel2_IRQn);
...@@ -1105,8 +1107,9 @@ void STM32Serial::writeDma(const char *buffer, size_t size) ...@@ -1105,8 +1107,9 @@ void STM32Serial::writeDma(const char *buffer, size_t size)
//and the race condition is eliminated). This is the purpose of this //and the race condition is eliminated). This is the purpose of this
//instruction, it reads SR. When we start the DMA, the DMA controller //instruction, it reads SR. When we start the DMA, the DMA controller
//writes to DR and completes the TC clear sequence. //writes to DR and completes the TC clear sequence.
DeepSleepLock dpLock;
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \ #if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
&& !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \ && !defined(_ARCH_CORTEXM0_STM32F0) && !defined(_ARCH_CORTEXM4_STM32F3) \
&& !defined(_ARCH_CORTEXM4_STM32L4) && !defined(_ARCH_CORTEXM4_STM32L4)
while((port->SR & USART_SR_TXE)==0) ; while((port->SR & USART_SR_TXE)==0) ;
#else //_ARCH_CORTEXM7_STM32F7/H7 #else //_ARCH_CORTEXM7_STM32F7/H7
...@@ -1114,7 +1117,7 @@ void STM32Serial::writeDma(const char *buffer, size_t size) ...@@ -1114,7 +1117,7 @@ void STM32Serial::writeDma(const char *buffer, size_t size)
#endif //_ARCH_CORTEXM7_STM32F7/H7 #endif //_ARCH_CORTEXM7_STM32F7/H7
dmaTxInProgress=true; dmaTxInProgress=true;
#if defined(_ARCH_CORTEXM3_STM32) #if defined(_ARCH_CORTEXM3_STM32F1)
dmaTx->CPAR=reinterpret_cast<unsigned int>(&port->DR); dmaTx->CPAR=reinterpret_cast<unsigned int>(&port->DR);
dmaTx->CMAR=reinterpret_cast<unsigned int>(buffer); dmaTx->CMAR=reinterpret_cast<unsigned int>(buffer);
dmaTx->CNDTR=size; dmaTx->CNDTR=size;
...@@ -1135,7 +1138,7 @@ void STM32Serial::writeDma(const char *buffer, size_t size) ...@@ -1135,7 +1138,7 @@ void STM32Serial::writeDma(const char *buffer, size_t size)
| DMA_CCR_EN; //Start DMA | DMA_CCR_EN; //Start DMA
#else //_ARCH_CORTEXM4_STM32F3 #else //_ARCH_CORTEXM4_STM32F3
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \ #if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
&& !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM0_STM32F0)
dmaTx->PAR=reinterpret_cast<unsigned int>(&port->DR); dmaTx->PAR=reinterpret_cast<unsigned int>(&port->DR);
#else //_ARCH_CORTEXM7_STM32F7/H7 #else //_ARCH_CORTEXM7_STM32F7/H7
dmaTx->PAR=reinterpret_cast<unsigned int>(&port->TDR); dmaTx->PAR=reinterpret_cast<unsigned int>(&port->TDR);
...@@ -1155,7 +1158,7 @@ void STM32Serial::writeDma(const char *buffer, size_t size) ...@@ -1155,7 +1158,7 @@ void STM32Serial::writeDma(const char *buffer, size_t size)
| DMA_SxCR_DMEIE //Interrupt on direct mode error | DMA_SxCR_DMEIE //Interrupt on direct mode error
| DMA_SxCR_EN; //Start the DMA | DMA_SxCR_EN; //Start the DMA
#endif //_ARCH_CORTEXM4_STM32F3 #endif //_ARCH_CORTEXM4_STM32F3
#endif //_ARCH_CORTEXM3_STM32 #endif //_ARCH_CORTEXM3_STM32F1
} }
void STM32Serial::IRQreadDma() void STM32Serial::IRQreadDma()
...@@ -1169,7 +1172,7 @@ void STM32Serial::IRQreadDma() ...@@ -1169,7 +1172,7 @@ void STM32Serial::IRQreadDma()
void STM32Serial::IRQdmaReadStart() void STM32Serial::IRQdmaReadStart()
{ {
#if defined(_ARCH_CORTEXM3_STM32) #if defined(_ARCH_CORTEXM3_STM32F1)
dmaRx->CPAR=reinterpret_cast<unsigned int>(&port->DR); dmaRx->CPAR=reinterpret_cast<unsigned int>(&port->DR);
dmaRx->CMAR=reinterpret_cast<unsigned int>(rxBuffer); dmaRx->CMAR=reinterpret_cast<unsigned int>(rxBuffer);
dmaRx->CNDTR=rxQueueMin; dmaRx->CNDTR=rxQueueMin;
...@@ -1190,7 +1193,7 @@ void STM32Serial::IRQdmaReadStart() ...@@ -1190,7 +1193,7 @@ void STM32Serial::IRQdmaReadStart()
| DMA_CCR_EN; //Start DMA | DMA_CCR_EN; //Start DMA
#else //_ARCH_CORTEXM4_STM32F3 #else //_ARCH_CORTEXM4_STM32F3
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \ #if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
&& !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM0_STM32F0)
dmaRx->PAR=reinterpret_cast<unsigned int>(&port->DR); dmaRx->PAR=reinterpret_cast<unsigned int>(&port->DR);
#else //_ARCH_CORTEXM7_STM32F7/H7 #else //_ARCH_CORTEXM7_STM32F7/H7
dmaRx->PAR=reinterpret_cast<unsigned int>(&port->RDR); dmaRx->PAR=reinterpret_cast<unsigned int>(&port->RDR);
...@@ -1205,12 +1208,12 @@ void STM32Serial::IRQdmaReadStart() ...@@ -1205,12 +1208,12 @@ void STM32Serial::IRQdmaReadStart()
| DMA_SxCR_DMEIE //Interrupt on direct mode error | DMA_SxCR_DMEIE //Interrupt on direct mode error
| DMA_SxCR_EN; //Start the DMA | DMA_SxCR_EN; //Start the DMA
#endif //_ARCH_CORTEXM4_STM32F3 #endif //_ARCH_CORTEXM4_STM32F3
#endif //_ARCH_CORTEXM3_STM32 #endif //_ARCH_CORTEXM3_STM32F1
} }
int STM32Serial::IRQdmaReadStop() int STM32Serial::IRQdmaReadStop()
{ {
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \ #if defined(_ARCH_CORTEXM3_STM32F1) || defined(_ARCH_CORTEXM4_STM32F3) \
|| defined(_ARCH_CORTEXM4_STM32L4) || defined(_ARCH_CORTEXM4_STM32L4)
dmaRx->CCR=0; dmaRx->CCR=0;
static const unsigned int irqMask[]= static const unsigned int irqMask[]=
...@@ -1221,7 +1224,7 @@ int STM32Serial::IRQdmaReadStop() ...@@ -1221,7 +1224,7 @@ int STM32Serial::IRQdmaReadStop()
}; };
DMA1->IFCR=irqMask[getId()-1]; DMA1->IFCR=irqMask[getId()-1];
return rxQueueMin-dmaRx->CNDTR; return rxQueueMin-dmaRx->CNDTR;
#else //_ARCH_CORTEXM3_STM32 #else //_ARCH_CORTEXM3_STM32F1
//Stop DMA and wait for it to actually stop //Stop DMA and wait for it to actually stop
dmaRx->CR &= ~DMA_SxCR_EN; dmaRx->CR &= ~DMA_SxCR_EN;
while(dmaRx->CR & DMA_SxCR_EN) ; while(dmaRx->CR & DMA_SxCR_EN) ;
...@@ -1239,7 +1242,7 @@ int STM32Serial::IRQdmaReadStop() ...@@ -1239,7 +1242,7 @@ int STM32Serial::IRQdmaReadStop()
}; };
*irqRegs[getId()-1]=irqMask[getId()-1]; *irqRegs[getId()-1]=irqMask[getId()-1];
return rxQueueMin-dmaRx->NDTR; return rxQueueMin-dmaRx->NDTR;
#endif //_ARCH_CORTEXM3_STM32 #endif //_ARCH_CORTEXM3_STM32F1
} }
#endif //SERIAL_DMA #endif //SERIAL_DMA
......
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
#include "interfaces/gpio.h" #include "interfaces/gpio.h"
#include "board_settings.h" #include "board_settings.h"
#if defined(_ARCH_CORTEXM3_STM32) && defined(__ENABLE_XRAM) #if defined(_ARCH_CORTEXM3_STM32F1) && defined(__ENABLE_XRAM)
//Quirk: concurrent access to the FSMC from both core and DMA is broken in //Quirk: concurrent access to the FSMC from both core and DMA is broken in
//the stm32f1, so disable DMA mode if XRAM is enabled. //the stm32f1, so disable DMA mode if XRAM is enabled.
#undef SERIAL_1_DMA #undef SERIAL_1_DMA
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
#define SERIAL_DMA #define SERIAL_DMA
#endif #endif
#if defined(SERIAL_DMA) && defined(_ARCH_CORTEXM0_STM32) #if defined(SERIAL_DMA) && defined(_ARCH_CORTEXM0_STM32F0)
#undef SERIAL_1_DMA #undef SERIAL_1_DMA
#undef SERIAL_2_DMA #undef SERIAL_2_DMA
#undef SERIAL_3_DMA #undef SERIAL_3_DMA
...@@ -248,7 +248,7 @@ private: ...@@ -248,7 +248,7 @@ private:
void waitSerialTxFifoEmpty() void waitSerialTxFifoEmpty()
{ {
#if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \ #if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
&& !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \ && !defined(_ARCH_CORTEXM0_STM32F0) && !defined(_ARCH_CORTEXM4_STM32F3) \
&& !defined(_ARCH_CORTEXM4_STM32L4) && !defined(_ARCH_CORTEXM4_STM32L4)
while((port->SR & USART_SR_TC)==0) ; while((port->SR & USART_SR_TC)==0) ;
#else //_ARCH_CORTEXM7_STM32F7/H7 #else //_ARCH_CORTEXM7_STM32F7/H7
...@@ -265,14 +265,14 @@ private: ...@@ -265,14 +265,14 @@ private:
USART_TypeDef *port; ///< Pointer to USART peripheral USART_TypeDef *port; ///< Pointer to USART peripheral
#ifdef SERIAL_DMA #ifdef SERIAL_DMA
#if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \ #if defined(_ARCH_CORTEXM3_STM32F1) || defined(_ARCH_CORTEXM4_STM32F3) \
|| defined(_ARCH_CORTEXM4_STM32L4) || defined(_ARCH_CORTEXM4_STM32L4)
DMA_Channel_TypeDef *dmaTx; ///< Pointer to DMA TX peripheral DMA_Channel_TypeDef *dmaTx; ///< Pointer to DMA TX peripheral
DMA_Channel_TypeDef *dmaRx; ///< Pointer to DMA RX peripheral DMA_Channel_TypeDef *dmaRx; ///< Pointer to DMA RX peripheral
#else //_ARCH_CORTEXM3_STM32 and _ARCH_CORTEXM4_STM32F3 #else //_ARCH_CORTEXM3_STM32F1 and _ARCH_CORTEXM4_STM32F3
DMA_Stream_TypeDef *dmaTx; ///< Pointer to DMA TX peripheral DMA_Stream_TypeDef *dmaTx; ///< Pointer to DMA TX peripheral
DMA_Stream_TypeDef *dmaRx; ///< Pointer to DMA RX peripheral DMA_Stream_TypeDef *dmaRx; ///< Pointer to DMA RX peripheral
#endif //_ARCH_CORTEXM3_STM32 and _ARCH_CORTEXM4_STM32F3 #endif //_ARCH_CORTEXM3_STM32F1 and _ARCH_CORTEXM4_STM32F3
Thread *txWaiting; ///< Thread waiting for tx, or 0 Thread *txWaiting; ///< Thread waiting for tx, or 0
static const unsigned int txBufferSize=16; ///< Size of tx buffer, for tx speedup static const unsigned int txBufferSize=16; ///< Size of tx buffer, for tx speedup
/// Tx buffer, for tx speedup. This buffer must not end up in the CCM of the /// Tx buffer, for tx speedup. This buffer must not end up in the CCM of the
......
...@@ -96,33 +96,33 @@ void SynchronizedServo::enable(int channel) ...@@ -96,33 +96,33 @@ void SynchronizedServo::enable(int channel)
case 0: case 0:
TIM4->CCMR1 |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE; TIM4->CCMR1 |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE;
TIM4->CCER |= TIM_CCER_CC1E; TIM4->CCER |= TIM_CCER_CC1E;
#ifndef _ARCH_CORTEXM3_STM32 //Only stm32f2 and stm32f4 have it #ifndef _ARCH_CORTEXM3_STM32F1 //Only stm32f2 and stm32f4 have it
servo1out::alternateFunction(2); servo1out::alternateFunction(2);
#endif //_ARCH_CORTEXM3_STM32 #endif //_ARCH_CORTEXM3_STM32F1
servo1out::mode(Mode::ALTERNATE); servo1out::mode(Mode::ALTERNATE);
break; break;
case 1: case 1:
TIM4->CCMR1 |= TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE; TIM4->CCMR1 |= TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE;
TIM4->CCER |= TIM_CCER_CC2E; TIM4->CCER |= TIM_CCER_CC2E;
#ifndef _ARCH_CORTEXM3_STM32 //Only stm32f2 and stm32f4 have it #ifndef _ARCH_CORTEXM3_STM32F1 //Only stm32f2 and stm32f4 have it
servo2out::alternateFunction(2); servo2out::alternateFunction(2);
#endif //_ARCH_CORTEXM3_STM32 #endif //_ARCH_CORTEXM3_STM32F1
servo2out::mode(Mode::ALTERNATE); servo2out::mode(Mode::ALTERNATE);
break; break;
case 2: case 2:
TIM4->CCMR2 |= TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE; TIM4->CCMR2 |= TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE;
TIM4->CCER |= TIM_CCER_CC3E; TIM4->CCER |= TIM_CCER_CC3E;
#ifndef _ARCH_CORTEXM3_STM32 //Only stm32f2 and stm32f4 have it #ifndef _ARCH_CORTEXM3_STM32F1 //Only stm32f2 and stm32f4 have it
servo3out::alternateFunction(2); servo3out::alternateFunction(2);
#endif //_ARCH_CORTEXM3_STM32 #endif //_ARCH_CORTEXM3_STM32F1
servo3out::mode(Mode::ALTERNATE); servo3out::mode(Mode::ALTERNATE);
break; break;
case 3: case 3:
TIM4->CCMR2 |= TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE; TIM4->CCMR2 |= TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE;
TIM4->CCER |= TIM_CCER_CC4E; TIM4->CCER |= TIM_CCER_CC4E;
#ifndef _ARCH_CORTEXM3_STM32 //Only stm32f2 and stm32f4 have it #ifndef _ARCH_CORTEXM3_STM32F1 //Only stm32f2 and stm32f4 have it
servo4out::alternateFunction(2); servo4out::alternateFunction(2);
#endif //_ARCH_CORTEXM3_STM32 #endif //_ARCH_CORTEXM3_STM32F1
servo4out::mode(Mode::ALTERNATE); servo4out::mode(Mode::ALTERNATE);
break; break;
} }
...@@ -321,7 +321,7 @@ unsigned int SynchronizedServo::getPrescalerInputFrequency() ...@@ -321,7 +321,7 @@ unsigned int SynchronizedServo::getPrescalerInputFrequency()
unsigned int freq=SystemCoreClock; unsigned int freq=SystemCoreClock;
//The position of the PPRE1 bit in RCC->CFGR is different in some stm32 //The position of the PPRE1 bit in RCC->CFGR is different in some stm32
#ifdef _ARCH_CORTEXM3_STM32 #ifdef _ARCH_CORTEXM3_STM32F1
const unsigned int ppre1=8; const unsigned int ppre1=8;
#else //stm32f2 and f4 #else //stm32f2 and f4
const unsigned int ppre1=10; const unsigned int ppre1=10;
......
/***************************************************************************
* Copyright (C) 2024 by Niccolò Betto *
* *
* This program 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 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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. *
* *
* As a special exception, if other files instantiate templates or use *
* macros or inline functions from this file, or you compile this file *
* and link it with other works to produce a work based on this file, *
* this file does not by itself cause the resulting work to be covered *
* by the GNU General Public License. However the source code for this *
* file must still be made available in accordance with the GNU General *
* Public License. This exception does not invalidate any other reasons *
* why a work based on this file might be covered by the GNU General *
* Public License. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#include "stm32_bsram.h"
#include <miosix.h>
#include <cstring>
namespace miosix
{
static ResetReason lastReset = ResetReason::UNKNOWN;
static ResetReason readResetRegister()
{
uint32_t resetReg = RCC->CSR;
// Clear the reset flag
RCC->CSR |= RCC_CSR_RMVF;
if (resetReg & RCC_CSR_LPWRRSTF)
{
return ResetReason::LOW_POWER;
}
else if (resetReg & RCC_CSR_WWDGRSTF)
{
return ResetReason::WINDOW_WATCHDOG;
}
#ifdef _ARCH_CORTEXM7_STM32F7
else if (resetReg & RCC_CSR_IWDGRSTF)
#else
else if (resetReg & RCC_CSR_WDGRSTF)
#endif
{
return ResetReason::INDEPENDENT_WATCHDOG;
}
else if (resetReg & RCC_CSR_SFTRSTF)
{
return ResetReason::SOFTWARE;
}
else if (resetReg & RCC_CSR_PORRSTF)
{
return ResetReason::POWER_ON;
}
#ifdef _ARCH_CORTEXM7_STM32F7
else if (resetReg & RCC_CSR_PINRSTF)
#else
else if (resetReg & RCC_CSR_PADRSTF)
#endif
{
return ResetReason::PIN;
}
else
{
return ResetReason::UNKNOWN;
}
}
ResetReason lastResetReason() { return lastReset; }
namespace BSRAM
{
void init()
{
// Enable PWR clock
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
// Enable write on BSRAM since we need it to write on the PWR peripheral
enableWrite();
// Enable backup SRAM Clock
RCC->AHB1ENR |= RCC_AHB1ENR_BKPSRAMEN;
// Enable the backup regulator and wait for it to be ready
#ifdef _ARCH_CORTEXM7_STM32F7
PWR->CSR1 |= PWR_CSR1_BRE;
while (!(PWR->CSR1 & (PWR_CSR1_BRR)))
;
#else
PWR->CSR |= PWR_CSR_BRE;
while (!(PWR->CSR & (PWR_CSR_BRR)))
;
#endif
// Retrieve last reset reason
lastReset = readResetRegister();
// Disable write on BSRAM since we don't need to write on the peripheral for
// now. It will be necessary to re-enable the write on the backup SRAM
// around a PRESERVED variable to effectively change its value.
disableWrite();
}
void disableWrite()
{
// Ensure all memory instructions complete before disabling write
__DMB();
// Enable backup domain write protection
#ifdef _ARCH_CORTEXM7_STM32F7
PWR->CR1 &= ~PWR_CR1_DBP;
#else
PWR->CR &= ~PWR_CR_DBP;
#endif
// Ensure write is disabled when exiting this function
__DMB();
}
void enableWrite()
{
// Disable backup domain write protection
#ifdef _ARCH_CORTEXM7_STM32F7
PWR->CR1 |= PWR_CR1_DBP;
#else
PWR->CR |= PWR_CR_DBP;
#endif
// Ensure writes to the control registers complete before returning
__DMB();
}
} // namespace BSRAM
} // namespace miosix
/***************************************************************************
* Copyright (C) 2024 by Niccolò Betto *
* *
* This program 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 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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. *
* *
* As a special exception, if other files instantiate templates or use *
* macros or inline functions from this file, or you compile this file *
* and link it with other works to produce a work based on this file, *
* this file does not by itself cause the resulting work to be covered *
* by the GNU General Public License. However the source code for this *
* file must still be made available in accordance with the GNU General *
* Public License. This exception does not invalidate any other reasons *
* why a work based on this file might be covered by the GNU General *
* Public License. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#pragma once
namespace miosix
{
/**
* Enumeration of possible causes of an STM32 reset.
*/
enum class ResetReason
{
UNKNOWN = 0,
LOW_POWER,
WINDOW_WATCHDOG,
INDEPENDENT_WATCHDOG,
SOFTWARE,
POWER_ON,
PIN,
};
/**
* Returns the cause of the last reset of the microcontroller.
*/
ResetReason lastResetReason();
/**
* Driver for the STM32F2/F4/F7 backup SRAM.
*/
namespace BSRAM
{
/**
* Initialize the backup SRAM and populate the last reset reason variable.
*/
void init();
/**
* Disable writing to the backup SRAM.
*/
void disableWrite();
/**
* Enable writing to the backup SRAM.
*/
void enableWrite();
/**
* @brief This class is a RAII lock for disabling write protection on backup
* SRAM. This call avoids the error of not re-enabling write protection since it
* is done automatically.
*/
class EnableWriteLock
{
public:
EnableWriteLock() { enableWrite(); }
~EnableWriteLock() { disableWrite(); }
///< Delete copy/move constructors/operators.
EnableWriteLock(const EnableWriteLock& l) = delete;
EnableWriteLock(const EnableWriteLock&& l) = delete;
EnableWriteLock& operator=(const EnableWriteLock& l) = delete;
EnableWriteLock& operator=(const EnableWriteLock&& l) = delete;
};
} // namespace BSRAM
} // namespace miosix
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
* along with this program; if not, see <http://www.gnu.org/licenses/> * * along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/ ***************************************************************************/
#include "board_settings.h"
#include "stm32_sgm.h" #include "stm32_sgm.h"
#include <string.h> #include <string.h>
#include "miosix.h" #include "miosix.h"
......