diff --git a/lib/miosix-kernel/README.md b/lib/miosix-kernel/README.md
new file mode 100644
index 00000000..f9d5577f
--- /dev/null
+++ b/lib/miosix-kernel/README.md
@@ -0,0 +1,5 @@
+
+This repository contains an adapted version of the miosix kernel, tailored for the usage with the OpenRTX targets equipped with an ARM cortex M4 microcontroller.
+
+The original repository can be found here: [https://github.com/fedetft/miosix-kernel](https://github.com/fedetft/miosix-kernel)
+
diff --git a/lib/miosix-kernel/meson.build b/lib/miosix-kernel/meson.build
new file mode 100644
index 00000000..97f95692
--- /dev/null
+++ b/lib/miosix-kernel/meson.build
@@ -0,0 +1,28 @@
+
+miosix_inc = ['lib/miosix-kernel/miosix',
+ 'lib/miosix-kernel/miosix/config',
+ 'lib/miosix-kernel/miosix/arch/common',
+ 'lib/miosix-kernel/miosix/util']
+
+miosix_src = ['lib/miosix-kernel/miosix/kernel/scheduler/priority/priority_scheduler.cpp',
+ 'lib/miosix-kernel/miosix/kernel/error.cpp',
+ 'lib/miosix-kernel/miosix/kernel/kernel.cpp',
+ 'lib/miosix-kernel/miosix/kernel/pthread.cpp',
+ 'lib/miosix-kernel/miosix/kernel/stage_2_boot.cpp',
+ 'lib/miosix-kernel/miosix/kernel/sync.cpp',
+ 'lib/miosix-kernel/miosix/kernel/timeconversion.cpp',
+ 'lib/miosix-kernel/miosix/util/util.cpp',
+ 'lib/miosix-kernel/miosix/stdlib_integration/libc_integration.cpp',
+ 'lib/miosix-kernel/miosix/stdlib_integration/libstdcpp_integration.cpp']
+
+miosix_def = {'DONT_USE_CMSIS_INIT' : '',
+ 'COMPILING_MIOSIX' : '',
+ '_POSIX_PRIORITY_SCHEDULING': ''}
+
+##
+## ARM Cortex M4
+##
+miosix_cm4f_inc = miosix_inc + ['lib/miosix-kernel/miosix/arch/cortexM4F']
+miosix_cm4f_src = miosix_src + ['lib/miosix-kernel/miosix/arch/cortexM4F/portability.cpp',
+ 'lib/miosix-kernel/miosix/arch/common/core/interrupts_cortexMx.cpp']
+miosix_cm4f_def = miosix_def + {'_ARCH_CORTEXM4' : ''}
diff --git a/lib/miosix-kernel/miosix/arch/common/core/atomic_ops_impl_arm7.h b/lib/miosix-kernel/miosix/arch/common/core/atomic_ops_impl_arm7.h
new file mode 100644
index 00000000..02a25a09
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/core/atomic_ops_impl_arm7.h
@@ -0,0 +1,119 @@
+/***************************************************************************
+ * Copyright (C) 2013, 2014, 2015 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 *
+ ***************************************************************************/
+
+#ifndef ATOMIC_OPS_IMPL_H
+#define ATOMIC_OPS_IMPL_H
+
+namespace miosix {
+
+inline int atomicSwap(volatile int *p, int v)
+{
+ //This is the only atomic operation in the ARM7 assembler
+ register int result;
+ asm volatile("swp %0, %1, [%2]" : "=&r"(result) : "r"(v),"r"(p) : "memory");
+ return result;
+}
+
+inline void atomicAdd(volatile int *p, int incr)
+{
+ register int a,b; //Temporaries used by ASM code
+ asm volatile(" mrs %0, cpsr \n"
+ " tst %0, #0x80 \n"
+ " orreq %1, %0, #0x80 \n"
+ " msreq cpsr_c, %1 \n"
+ " ldr %1, [%3] \n"
+ " add %1, %1, %2 \n"
+ " str %1, [%3] \n"
+ " tst %0, #0x80 \n"
+ " msreq cpsr_c, %0 \n"
+ : "=&r"(a),"=&r"(b)
+ : "r"(incr),"r"(p)
+ : "cc","memory");
+}
+
+inline int atomicAddExchange(volatile int *p, int incr)
+{
+ register int a; //Temporaries used by ASM code
+ register int result;
+ asm volatile(" mrs %0, cpsr \n"
+ " tst %0, #0x80 \n"
+ " orreq %1, %0, #0x80 \n"
+ " msreq cpsr_c, %1 \n"
+ " ldr %1, [%3] \n"
+ " add %2, %2, %1 \n"
+ " str %2, [%3] \n"
+ " tst %0, #0x80 \n"
+ " msreq cpsr_c, %0 \n"
+ : "=&r"(a),"=&r"(result),"+&r"(incr)//Incr is read and clobbered
+ : "r"(p)
+ : "cc","memory");
+ return result;
+}
+
+inline int atomicCompareAndSwap(volatile int *p, int prev, int next)
+{
+ register int a; //Temporaries used by ASM code
+ register int result;
+ asm volatile(" mrs %0, cpsr \n"
+ " tst %0, #0x80 \n"
+ " orreq %1, %0, #0x80 \n"
+ " msreq cpsr_c, %1 \n"
+ " ldr %1, [%2] \n"
+ " cmp %1, %3 \n"
+ " streq %4, [%2] \n"
+ " tst %0, #0x80 \n"
+ " msreq cpsr_c, %0 \n"
+ : "=&r"(a),"=&r"(result)
+ : "r"(p),"r"(prev),"r"(next)
+ : "cc","memory");
+ return result;
+}
+
+inline void *atomicFetchAndIncrement(void * const volatile * p, int offset,
+ int incr)
+{
+ register int a,b; //Temporaries used by ASM code
+ register void *result;
+ asm volatile(" mrs %0, cpsr \n"
+ " tst %0, #0x80 \n"
+ " orreq %1, %0, #0x80 \n"
+ " msreq cpsr_c, %1 \n"
+ " ldr %2, [%3] \n"
+ " ldr %1, [%2, %4, asl #2] \n"
+ " add %1, %1, %5 \n"
+ " str %1, [%2, %4, asl #2] \n"
+ " tst %0, #0x80 \n"
+ " msreq cpsr_c, %0 \n"
+ : "=&r"(a),"=&r"(b),"=&r"(result)
+ : "r"(p),"r"(offset),"r"(incr)
+ : "cc","memory");
+ return result;
+}
+
+} //namespace miosix
+
+#endif //ATOMIC_OPS_IMPL_H
diff --git a/lib/miosix-kernel/miosix/arch/common/core/atomic_ops_impl_cortexM0.h b/lib/miosix-kernel/miosix/arch/common/core/atomic_ops_impl_cortexM0.h
new file mode 100644
index 00000000..f0d5b700
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/core/atomic_ops_impl_cortexM0.h
@@ -0,0 +1,96 @@
+/***************************************************************************
+ * Copyright (C) 2013, 2014, 2015 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 *
+ ***************************************************************************/
+
+#ifndef ATOMIC_OPS_IMPL_M0_H
+#define ATOMIC_OPS_IMPL_M0_H
+
+/**
+ * Cortex M0/M0+ architectures does not support __LDREXW, __STREXW and __CLREX
+ * instructions, so we have to redefine the atomic operations using functions
+ * that disable the interrupts.
+ *
+ * TODO: actually this implementation is not very efficient
+ *
+ */
+
+#include "interfaces/arch_registers.h"
+#include
+
+namespace miosix {
+
+
+inline int atomicSwap(volatile int *p, int v)
+{
+ InterruptDisableLock dLock;
+
+ int result = *p;
+ *p = v;
+ return result;
+}
+
+inline void atomicAdd(volatile int *p, int incr)
+{
+ InterruptDisableLock dLock;
+
+ *p += incr;
+}
+
+inline int atomicAddExchange(volatile int *p, int incr)
+{
+ InterruptDisableLock dLock;
+
+ int result = *p;
+ *p += incr;
+ return result;
+}
+
+inline int atomicCompareAndSwap(volatile int *p, int prev, int next)
+{
+ InterruptDisableLock dLock;
+
+ int result = *p;
+ if(*p == prev) *p = next;
+ return result;
+}
+
+inline void *atomicFetchAndIncrement(void * const volatile * p, int offset,
+ int incr)
+{
+ InterruptDisableLock dLock;
+
+ volatile uint32_t *pt;
+
+ void *result = *p;
+ if(result == 0) return 0;
+ pt = reinterpret_cast(result) + offset;
+ *pt += incr;
+ return result;
+}
+
+} //namespace miosix
+
+#endif //ATOMIC_OPS_IMPL_M0_H
diff --git a/lib/miosix-kernel/miosix/arch/common/core/atomic_ops_impl_cortexMx.h b/lib/miosix-kernel/miosix/arch/common/core/atomic_ops_impl_cortexMx.h
new file mode 100644
index 00000000..525829c1
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/core/atomic_ops_impl_cortexMx.h
@@ -0,0 +1,107 @@
+/***************************************************************************
+ * Copyright (C) 2013, 2014, 2015 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 *
+ ***************************************************************************/
+
+#ifndef ATOMIC_OPS_IMPL_H
+#define ATOMIC_OPS_IMPL_H
+
+#include "interfaces/arch_registers.h"
+
+namespace miosix {
+
+inline int atomicSwap(volatile int *p, int v)
+{
+ int result;
+ volatile uint32_t *ptr=reinterpret_cast(p);
+ do {
+ result=__LDREXW(ptr);
+ } while(__STREXW(v,ptr));
+ asm volatile("":::"memory");
+ return result;
+}
+
+inline void atomicAdd(volatile int *p, int incr)
+{
+ int value;
+ volatile uint32_t *ptr=reinterpret_cast(p);
+ do {
+ value=__LDREXW(ptr);
+ } while(__STREXW(value+incr,ptr));
+ asm volatile("":::"memory");
+}
+
+inline int atomicAddExchange(volatile int *p, int incr)
+{
+ int result;
+ volatile uint32_t *ptr=reinterpret_cast(p);
+ do {
+ result=__LDREXW(ptr);
+ } while(__STREXW(result+incr,ptr));
+ asm volatile("":::"memory");
+ return result;
+}
+
+inline int atomicCompareAndSwap(volatile int *p, int prev, int next)
+{
+ int result;
+ volatile uint32_t *ptr=reinterpret_cast(p);
+ do {
+ result=__LDREXW(ptr);
+ if(result!=prev)
+ {
+ __CLREX();
+ return result;
+ }
+ } while(__STREXW(next,ptr));
+ asm volatile("":::"memory");
+ return result;
+}
+
+inline void *atomicFetchAndIncrement(void * const volatile * p, int offset,
+ int incr)
+{
+ void *result;
+ volatile uint32_t *rcp;
+ int rc;
+ do {
+ for(;;)
+ {
+ result=*p;
+ if(result==0) return 0;
+ rcp=reinterpret_cast(result)+offset;
+ rc=__LDREXW(rcp);
+ asm volatile("":::"memory");
+ if(result==*p) break;
+ __CLREX();
+ }
+ } while(__STREXW(rc+incr,rcp));
+ asm volatile("":::"memory");
+ return result;
+}
+
+} //namespace miosix
+
+#endif //ATOMIC_OPS_IMPL_H
diff --git a/lib/miosix-kernel/miosix/arch/common/core/cache_cortexMx.cpp b/lib/miosix-kernel/miosix/arch/common/core/cache_cortexMx.cpp
new file mode 100644
index 00000000..1330b247
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/core/cache_cortexMx.cpp
@@ -0,0 +1,101 @@
+/***************************************************************************
+ * Copyright (C) 2018 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 *
+ ***************************************************************************/
+
+#include "cache_cortexMx.h"
+#include "mpu_cortexMx.h"
+#include
+
+using namespace std;
+
+namespace miosix {
+
+static const unsigned int cacheLine=32; //Cortex-M7 cache line size
+
+/**
+ * Using the MPU, configure a region of the memory space as
+ * - write-through cacheable
+ * - non-shareable
+ * - readable/writable/executable only by privileged code (for compatibility
+ * with the way processes use the MPU)
+ * \param region MPU region. Note that region 6 and 7 are used by processes, and
+ * should be avoided here
+ * \param base base address, aligned to a 32Byte cache line
+ * \param size size, must be at least 32 and a power of 2, or it is rounded to
+ * the next power of 2
+ */
+static void IRQconfigureCacheability(unsigned int region, unsigned int base,
+ unsigned int size)
+{
+ // NOTE: The ARM documentation is unclear about the effect of the shareable
+ // bit on a single core architecture. Experimental evidence on an STM32F476
+ // shows that setting it in IRQconfigureCache for the internal RAM region
+ // causes the boot to fail.
+ // For this reason, all regions are marked as not shareable
+ MPU->RBAR=(base & (~(cacheLine-1))) | MPU_RBAR_VALID_Msk | region;
+ MPU->RASR=1<(xramBase),xramSize);
+ IRQenableMPUatBoot();
+
+ SCB_EnableICache();
+ SCB_EnableDCache();
+}
+
+#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT==1)
+/**
+ * Align a generic buffer to another one that contains the first one, but the
+ * start size is aligned to a cache line
+ */
+static pair alignBuffer(void *buffer, int size)
+{
+ auto bufferAddr=reinterpret_cast(buffer);
+
+ auto base=bufferAddr & (~(cacheLine-1));
+ size+=bufferAddr-base;
+
+ return make_pair(reinterpret_cast(base),size);
+}
+
+void markBufferAfterDmaRead(void *buffer, int size)
+{
+ //Since the current cache policy is write-through, we just invalidate the
+ //cache lines corresponding to the buffer. No need to flush (clean) the cache.
+ auto result=alignBuffer(buffer,size);
+ SCB_InvalidateDCache_by_Addr(result.first,result.second);
+}
+#endif
+
+} //namespace miosix
diff --git a/lib/miosix-kernel/miosix/arch/common/core/cache_cortexMx.h b/lib/miosix-kernel/miosix/arch/common/core/cache_cortexMx.h
new file mode 100644
index 00000000..2c0e6667
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/core/cache_cortexMx.h
@@ -0,0 +1,153 @@
+/***************************************************************************
+ * Copyright (C) 2018 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 *
+ ***************************************************************************/
+
+#ifndef CACHE_CORTEX_MX_H
+#define CACHE_CORTEX_MX_H
+
+/*
+ * README: Essentials about how cache support is implemented.
+ *
+ * Caches in the Cortex M7 are transparent to software, except when using
+ * the DMA. As the DMA reads and writes directly to memory, explicit management
+ * is required. The default cache policy is write-back, but this has been deemed
+ * unsuitable for Miosix, so for the time being only write-through is supported.
+ *
+ * The IRQconfigureCache() configures the cache as write-through and enables it.
+ * It should be called early at boot, in stage_1_boot.cpp
+ *
+ * When writing DMA drivers, before passing a buffer to the DMA for it to be
+ * written to a peripheral, call markBufferBeforeDmaWrite().
+ * After a DMA read from a peripheral to a memory buffer has completed,
+ * call markBufferAfterDmaRead(). These take care of keeping the DMA operations
+ * in sync with the cache. These become no-ops for other architectures, so you
+ * can freely put the in any driver.
+ */
+
+/*
+ * Some more info about caches. Why not supporting write-back?
+ * When writing data from memory to a peripheral using DMA, things are easy
+ * also with write-back. You just flush (clean) the relevant cache lines, and
+ * the DMA has access to the correct values. So it looks like it's ok.
+ * When instead the DMA has to write to a memory location things become
+ * complicated. Assume that a buffer not aligned to a cache line is passed to
+ * a DMA read routine. After that a context switch happens and another thread
+ * writes to a memory location that is on the same cache line as the (beginning
+ * or end of) the buffer passed to the DMA. At the same time the DMA is writing
+ * to the buffer.
+ * At the end the situation looks like this, where the thread has written to
+ * location X in the cache, while the DMA has written Y to the buffer.
+ * <-- outside buffer --x-- buffer -->
+ * +----------------------------------+
+ * | X | | cache
+ * +----------------------------------+
+ * | |YYYYYYYYYYYYY| memory
+ * +----------------------------------+
+ * What are you suppose to do? If you flush (clean) the cache line, X will be
+ * committed to memory, but the Y data written by the DMA will be lost. If you
+ * invalidate the cache, Y is preserved, but X is lost.
+ * If you're just thinking that the problem can be solved by making sure buffers
+ * are aligned to the cache line (and their size is a multiple of the cache
+ * line), well, there's a problem.
+ * Miosix is a real-time OS, and for performance and safety, most drivers are
+ * zero copy. Applications routinely pass to DMA drivers such as the SDIO large
+ * buffers (think 100+KB). Of course allocating an aligned buffer inside the
+ * DMA driver as large as the user-passed buffer and copying the data there
+ * isn't just slow, it wouldn't be safe, as you risk to exceed the free heap
+ * memory or fragment the heap. Allocating a small buffer and splitting the
+ * large DMA transfer in many small ones where the user passed buffer is copyied
+ * one chunk at a time would be feasible, but even slower, and even more so
+ * considering that some peripherals such as SD cards are optimized for large
+ * sequential writes, not for small chunks.
+ * But what if we make sure all buffers passed to DMA drivers are aligned?
+ * That is a joke, as it the burden of doing so is unmaintainable. Buffers
+ * passed to DMA memory are everywhere, in the C/C++ standard library
+ * (think the buffer used for IO formatting inside printf/fprintf), and
+ * everywherein application code. Something like
+ * char s[128]="Hello world";
+ * puts(s);
+ * may cause s to be passed to a DMA driver. We would spend our lives chasing
+ * unaligned buffers, and the risk of this causing difficult to reproduce memory
+ * corruptions is too high. For this reason, for the time being, Miosix only
+ * supports write-through caching on the Cortex-M7.
+ *
+ * A note about performance. Using the testsuite benchmark, when caches are
+ * disabled the STM32F746 @ 216MHz is less than half the speed of the
+ * STM32F407 @ 168MHz. By enabling the ICACHE things get better, but it is
+ * still slower, and achieves a speedup of 1.53 only when both ICACHE and
+ * DCACHE are enabled. The speedup also includes the gains due to the faster
+ * clock frequency. So if you want speed you have to use caches.
+ */
+
+#include "interfaces/arch_registers.h"
+
+namespace miosix {
+
+/**
+ * To be called in stage_1_boot.cpp to configure caches.
+ * Only call this function if the board has caches.
+ * \param xramBase base address of external memory, if present, otherwise nullptr
+ * \param xramSize size of external memory, if present, otherwise 0
+ */
+void IRQconfigureCache(const unsigned int *xramBase=nullptr, unsigned int xramSize=0);
+
+/**
+ * Call this function to mark a buffer before starting a DMA transfer where
+ * the DMA will read the buffer.
+ * \param buffer buffer
+ * \param size buffer size
+ */
+inline void markBufferBeforeDmaWrite(const void *buffer, int size)
+{
+#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT==1)
+ // You may think that since the cache is configured as write-through,
+ // there's nothing to do before the DMA can read a memory buffer just
+ // written by the CPU, right? Wrong! Other than the cache, there's the
+ // write buffer to worry about. My hypothesis is that once a memory region
+ // is marked as cacheable, the write buffer becomes more lax in
+ // automatically flushing as soon as possible. In the stm32 serial port
+ // driver writing just a few characters causes garbage to be printed if
+ // this __DSB() is removed. Apparently, the characters remian in the write
+ // buffer.
+ __DSB();
+#endif
+}
+
+/**
+ * Call this function after having completed a DMA transfer where the DMA has
+ * written to the buffer.
+ * \param buffer buffer
+ * \param size buffer size
+ */
+#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT==1)
+void markBufferAfterDmaRead(void *buffer, int size);
+#else
+inline void markBufferAfterDmaRead(void *buffer, int size) {}
+#endif
+
+} //namespace miosix
+
+#endif //CACHE_CORTEX_MX_H
diff --git a/lib/miosix-kernel/miosix/arch/common/core/endianness_impl_arm7.h b/lib/miosix-kernel/miosix/arch/common/core/endianness_impl_arm7.h
new file mode 100644
index 00000000..0c96ef21
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/core/endianness_impl_arm7.h
@@ -0,0 +1,78 @@
+/***************************************************************************
+ * Copyright (C) 2011 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 *
+ ***************************************************************************/
+
+#ifndef ENDIANNESS_IMPL_H
+#define ENDIANNESS_IMPL_H
+
+#ifndef MIOSIX_BIG_ENDIAN
+//This target is little endian
+#define MIOSIX_LITTLE_ENDIAN
+#endif //MIOSIX_BIG_ENDIAN
+
+#ifdef __cplusplus
+#define __MIOSIX_INLINE inline
+#else //__cplusplus
+#define __MIOSIX_INLINE static inline
+#endif //__cplusplus
+
+
+__MIOSIX_INLINE unsigned short swapBytes16(unsigned short x)
+{
+ return (x>>8) | (x<<8);
+}
+
+__MIOSIX_INLINE unsigned int swapBytes32(unsigned int x)
+{
+ #ifdef __GNUC__
+ return __builtin_bswap32(x);
+ #else //__GNUC__
+ return ( x>>24) |
+ ((x<< 8) & 0x00ff0000) |
+ ((x>> 8) & 0x0000ff00) |
+ ( x<<24);
+ #endif //__GNUC__
+}
+
+__MIOSIX_INLINE unsigned long long swapBytes64(unsigned long long x)
+{
+ #ifdef __GNUC__
+ return __builtin_bswap64(x);
+ #else //__GNUC__
+ return ( x>>56) |
+ ((x<<40) & 0x00ff000000000000ull) |
+ ((x<<24) & 0x0000ff0000000000ull) |
+ ((x<< 8) & 0x000000ff00000000ull) |
+ ((x>> 8) & 0x00000000ff000000ull) |
+ ((x>>24) & 0x0000000000ff0000ull) |
+ ((x>>40) & 0x000000000000ff00ull) |
+ ( x<<56);
+ #endif //__GNUC__
+}
+
+#undef __MIOSIX_INLINE
+
+#endif //ENDIANNESS_IMPL_H
diff --git a/lib/miosix-kernel/miosix/arch/common/core/endianness_impl_cortexMx.h b/lib/miosix-kernel/miosix/arch/common/core/endianness_impl_cortexMx.h
new file mode 100644
index 00000000..6a656ea9
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/core/endianness_impl_cortexMx.h
@@ -0,0 +1,94 @@
+/***************************************************************************
+ * Copyright (C) 2011, 2012, 2013, 2014, 2015 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 *
+ ***************************************************************************/
+
+#ifndef ENDIANNESS_IMPL_H
+#define ENDIANNESS_IMPL_H
+
+#ifndef MIOSIX_BIG_ENDIAN
+//This target is little endian
+#define MIOSIX_LITTLE_ENDIAN
+#endif //MIOSIX_BIG_ENDIAN
+
+#ifdef __cplusplus
+#define __MIOSIX_INLINE inline
+#else //__cplusplus
+#define __MIOSIX_INLINE static inline
+#endif //__cplusplus
+
+__MIOSIX_INLINE unsigned short swapBytes16(unsigned short x)
+{
+ //It's kind of a shame that GCC can't automatically make use of
+ //instructions like rev and rev16 to do byte swapping.
+ //Moreover, while for 32 and 64 bit integers it has builtins, for 16 bit
+ //we're forced to use inline asm.
+ #ifdef __GNUC__
+ if(!__builtin_constant_p(x))
+ {
+ unsigned short y;
+ asm("rev16 %0, %1":"=r"(y):"r"(x));
+ return y;
+ } else {
+ //It gets worse: if value is constant inlining assembler disables
+ //contant folding, wtf...
+ return (x>>8) | (x<<8);
+ }
+ #else
+ return (x>>8) | (x<<8);
+ #endif
+}
+
+__MIOSIX_INLINE unsigned int swapBytes32(unsigned int x)
+{
+ #ifdef __GNUC__
+ return __builtin_bswap32(x);
+ #else
+ return ( x>>24) |
+ ((x<< 8) & 0x00ff0000) |
+ ((x>> 8) & 0x0000ff00) |
+ ( x<<24);
+ #endif
+}
+
+__MIOSIX_INLINE unsigned long long swapBytes64(unsigned long long x)
+{
+ #ifdef __GNUC__
+ return __builtin_bswap64(x);
+ #else
+ return ( x>>56) |
+ ((x<<40) & 0x00ff000000000000ull) |
+ ((x<<24) & 0x0000ff0000000000ull) |
+ ((x<< 8) & 0x000000ff00000000ull) |
+ ((x>> 8) & 0x00000000ff000000ull) |
+ ((x>>24) & 0x0000000000ff0000ull) |
+ ((x>>40) & 0x000000000000ff00ull) |
+ ( x<<56);
+ #endif
+}
+
+#undef __MIOSIX_INLINE
+
+#endif //ENDIANNESS_IMPL_H
diff --git a/lib/miosix-kernel/miosix/arch/common/core/interrupts.h b/lib/miosix-kernel/miosix/arch/common/core/interrupts.h
new file mode 100644
index 00000000..99089905
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/core/interrupts.h
@@ -0,0 +1,19 @@
+
+//Interrupt code is common for all the cortex M cores, so it has been put here
+
+#ifdef _ARCH_ARM7_LPC2000
+#include "interrupts_arm7.h"
+#elif defined(_ARCH_CORTEXM0) || defined(_ARCH_CORTEXM3) \
+ || defined(_ARCH_CORTEXM4) || defined(_ARCH_CORTEXM7)
+#include "interrupts_cortexMx.h"
+#else
+#error "Unknown arch"
+#endif
+
+// Cortex M0 and M0+ does not have some SCB registers, in order to avoid
+// compilation issues a flag is defined to disable code that accesses to
+// registers not present in these families
+
+#if defined(_ARCH_CORTEXM0_STM32)
+#define _ARCH_CORTEXM0
+#endif
diff --git a/lib/miosix-kernel/miosix/arch/common/core/interrupts_arm7.cpp b/lib/miosix-kernel/miosix/arch/common/core/interrupts_arm7.cpp
new file mode 100644
index 00000000..44b8fae9
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/core/interrupts_arm7.cpp
@@ -0,0 +1,154 @@
+/***************************************************************************
+ * Copyright (C) 2008, 2009, 2010 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 *
+ ***************************************************************************/
+
+#include "kernel/logging.h"
+#include "interfaces/portability.h"
+#include "config/miosix_settings.h"
+#include "interrupts.h"
+
+using namespace miosix;
+
+//Look up table used by printUnsignedInt()
+static const char hexdigits[]="0123456789abcdef";
+
+/**
+ * \internal
+ * Used to print an unsigned int in hexadecimal format, and to reboot the system
+ * Note that printf/iprintf cannot be used inside an IRQ, so that's why there's
+ * this function.
+ * \param x number to print
+ */
+static void printUnsignedInt(unsigned int x)
+{
+ char result[]="0x........\r\n";
+ for(int i=9;i>=2;i--)
+ {
+ result[i]=hexdigits[x & 0xf];
+ x>>=4;
+ }
+ IRQerrorLog(result);
+}
+
+/**
+ * \internal
+ * Spurious interrupt handler.
+ * The LPC2138 datasheet says that spurious interrups can occur, but until now
+ * it never happened. If and when spurious interruts will occur, this code will
+ * be modified to deal with them. Until then, this code just reboots the system.
+ */
+void default_IRQ_Routine()
+{
+ IRQerrorLog("\r\n***Unexpected IRQ\r\n");
+ miosix_private::IRQsystemReboot();
+}
+
+/**
+ * \internal
+ * FIQ is currently not used.
+ * Prints an error message, and reboots the system.
+ * Stack usage is 24 Bytes (measured with watermarking and stack dump)
+ * so a 32 byte stack is used (to leave some guard space).
+ * If the user wants to use FIQ, it is important to remember to increase the
+ * FIQ's stack size, which is defined in miosix.ld
+ */
+extern "C" void FIQ_Routine()
+{
+ IRQerrorLog("\r\n***Unexpected FIQ\r\n");
+ miosix_private::IRQsystemReboot();
+}
+
+/**
+ * \internal
+ * This ISR handles Undefined instruction.
+ * Prints an error message, showing an address near the instruction that caused
+ * the exception. This address together with the map file allows finding the
+ * function that caused the exception.
+ * Please note that when compiling with some level of optimization, the compiler
+ * can inline functions so the address is no longer accurate.
+ * Stack usage is 47 Bytes (measured with watermarking and stack dump)
+ * so a 48 byte stack is used (stak must be word-aligned).
+ */
+extern "C" void UNDEF_Routine()
+{
+ //These two instructions MUST be the first two instructions of the interrupt
+ //routine. They store in return_address the pc of the instruction that
+ //caused the interrupt.
+ register int returnAddress;
+ asm volatile("mov %0, lr" : "=r"(returnAddress));
+
+ IRQerrorLog("\r\n***Unexpected UNDEF @ ");
+ printUnsignedInt(returnAddress);
+ miosix_private::IRQsystemReboot();
+}
+
+/**
+ * \internal
+ * This ISR handles data abort.
+ * Prints an error message, showing an address near the instruction that caused
+ * the exception. This address together with the map file allows finding the
+ * function that caused the exception.
+ * Please note that when compiling with some level of optimization, the compiler
+ * can inline functions so the address is no longer accurate.
+ * Stack usage is 47 Bytes (measured with watermarking and stack dump)
+ * so a 48 byte stack is used (stak must be word-aligned).
+ */
+extern "C" void DABT_Routine()
+{
+ //These two instructions MUST be the first two instructions of the interrupt
+ //routine. They store in return_address the pc of the instruction that
+ //caused the interrupt. (lr has an offset of 8 during a data abort)
+ register int returnAddress;
+ asm volatile("sub %0, lr, #8" : "=r"(returnAddress));
+
+ IRQerrorLog("\r\n***Unexpected data abort @ ");
+ printUnsignedInt(returnAddress);
+ miosix_private::IRQsystemReboot();
+}
+
+/**
+ * \internal
+ * This ISR handles prefetch abort.
+ * Prints an error message, showing an address near the instruction that caused
+ * the exception. This address together with the map file allows finding the
+ * function that caused the exception.
+ * Please note that when compiling with some level of optimization, the compiler
+ * can inline functions so the address is no longer accurate.
+ * Stack usage is 47 Bytes (measured with watermarking and stack dump)
+ * so a 48 byte stack is used (stak must be word-aligned).
+ */
+extern "C" void PABT_Routine()
+{
+ //These two instructions MUST be the first two instructions of the interrupt
+ //routine. They store in return_address the pc of the instruction that
+ //caused the interrupt. (lr has an offset of 4 during a data abort)
+ register int returnAddress;
+ asm volatile("sub %0, lr, #4" : "=r"(returnAddress));
+
+ IRQerrorLog("\r\n***Unexpected prefetch abort @ ");
+ printUnsignedInt(returnAddress);
+ miosix_private::IRQsystemReboot();
+}
diff --git a/lib/miosix-kernel/miosix/arch/common/core/interrupts_arm7.h b/lib/miosix-kernel/miosix/arch/common/core/interrupts_arm7.h
new file mode 100644
index 00000000..743731b7
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/core/interrupts_arm7.h
@@ -0,0 +1,43 @@
+/***************************************************************************
+ * Copyright (C) 2008 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 *
+ ***************************************************************************/
+
+/***********************************************************************
+* interrupts.h Part of the Miosix Embedded OS.
+* Implementation of "Generic" interrupts, not related to a particular
+* hardware driver.
+************************************************************************/
+
+#ifndef INTERRUPTS_H
+#define INTERRUPTS_H
+
+void default_IRQ_Routine() __attribute__ ((interrupt("IRQ")));
+extern "C" void FIQ_Routine() __attribute__ ((interrupt("FIQ")));
+extern "C" void UNDEF_Routine() __attribute__ ((interrupt("UNDEF")));
+extern "C" void DABT_Routine() __attribute__ ((interrupt("DABT")));
+extern "C" void PABT_Routine() __attribute__ ((interrupt("PABT")));
+
+#endif //INTERRUPTS_H
diff --git a/lib/miosix-kernel/miosix/arch/common/core/interrupts_cortexMx.cpp b/lib/miosix-kernel/miosix/arch/common/core/interrupts_cortexMx.cpp
new file mode 100644
index 00000000..dd05ebde
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/core/interrupts_cortexMx.cpp
@@ -0,0 +1,282 @@
+/***************************************************************************
+ * Copyright (C) 2010, 2011, 2012, 2013, 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 *
+ ***************************************************************************/
+
+#include "kernel/logging.h"
+#include "kernel/kernel.h"
+#include "config/miosix_settings.h"
+#include "interfaces/portability.h"
+#include "interfaces/arch_registers.h"
+#include "interrupts.h"
+
+using namespace miosix;
+
+#ifdef WITH_ERRLOG
+
+/**
+ * \internal
+ * Used to print an unsigned int in hexadecimal format, and to reboot the system
+ * Note that printf/iprintf cannot be used inside an IRQ, so that's why there's
+ * this function.
+ * \param x number to print
+ */
+static void printUnsignedInt(unsigned int x)
+{
+ static const char hexdigits[]="0123456789abcdef";
+ char result[]="0x........\r\n";
+ for(int i=9;i>=2;i--)
+ {
+ result[i]=hexdigits[x & 0xf];
+ x>>=4;
+ }
+ IRQerrorLog(result);
+}
+
+#endif //WITH_ERRLOG
+
+#if defined(WITH_PROCESSES) || defined(WITH_ERRLOG)
+
+/**
+ * \internal
+ * \return the program counter of the thread that was running when the exception
+ * occurred.
+ */
+static unsigned int getProgramCounter()
+{
+ register unsigned int result;
+ // Get program counter when the exception was thrown from stack frame
+ asm volatile("mrs %0, psp \n\t"
+ "add %0, %0, #24 \n\t"
+ "ldr %0, [%0] \n\t":"=r"(result));
+ return result;
+}
+
+#endif //WITH_PROCESSES || WITH_ERRLOG
+
+void NMI_Handler()
+{
+ IRQerrorLog("\r\n***Unexpected NMI\r\n");
+ miosix_private::IRQsystemReboot();
+}
+
+void __attribute__((naked)) HardFault_Handler()
+{
+ saveContext();
+ //Call HardFault_impl(). Name is a C++ mangled name.
+ asm volatile("bl _Z14HardFault_implv");
+ restoreContext();
+}
+
+void __attribute__((noinline)) HardFault_impl()
+{
+ #ifdef WITH_PROCESSES
+ if(miosix::Thread::IRQreportFault(miosix_private::FaultData(
+ HARDFAULT,getProgramCounter()))) return;
+ #endif //WITH_PROCESSES
+ #ifdef WITH_ERRLOG
+ IRQerrorLog("\r\n***Unexpected HardFault @ ");
+ printUnsignedInt(getProgramCounter());
+ #ifndef _ARCH_CORTEXM0
+ unsigned int hfsr=SCB->HFSR;
+ if(hfsr & 0x40000000) //SCB_HFSR_FORCED
+ IRQerrorLog("Fault escalation occurred\r\n");
+ if(hfsr & 0x00000002) //SCB_HFSR_VECTTBL
+ IRQerrorLog("A BusFault occurred during a vector table read\r\n");
+ #endif //_ARCH_CORTEXM0
+ #endif //WITH_ERRLOG
+ miosix_private::IRQsystemReboot();
+}
+
+// Cortex M0/M0+ architecture does not have the interrupts handled by code
+// below this point
+#ifndef _ARCH_CORTEXM0
+
+void __attribute__((naked)) MemManage_Handler()
+{
+ saveContext();
+ //Call MemManage_impl(). Name is a C++ mangled name.
+ asm volatile("bl _Z14MemManage_implv");
+ restoreContext();
+}
+
+void __attribute__((noinline)) MemManage_impl()
+{
+ #if defined(WITH_PROCESSES) || defined(WITH_ERRLOG)
+ unsigned int cfsr=SCB->CFSR;
+ #endif //WITH_PROCESSES || WITH_ERRLOG
+ #ifdef WITH_PROCESSES
+ int id, arg=0;
+ if(cfsr & 0x00000001) id=MP_XN;
+ else if(cfsr & 0x00000080) { id=MP; arg=SCB->MMFAR; }
+ else id=MP_NOADDR;
+ if(miosix::Thread::IRQreportFault(miosix_private::FaultData(
+ id,getProgramCounter(),arg)))
+ {
+ SCB->SHCSR &= ~(1<<13); //Clear MEMFAULTPENDED bit
+ return;
+ }
+ #endif //WITH_PROCESSES
+ #ifdef WITH_ERRLOG
+ IRQerrorLog("\r\n***Unexpected MemManage @ ");
+ printUnsignedInt(getProgramCounter());
+ if(cfsr & 0x00000080) //SCB_CFSR_MMARVALID
+ {
+ IRQerrorLog("Fault caused by attempted access to ");
+ printUnsignedInt(SCB->MMFAR);
+ } else IRQerrorLog("The address that caused the fault is missing\r\n");
+ if(cfsr & 0x00000010) //SCB_CFSR_MSTKERR
+ IRQerrorLog("Fault occurred during exception stacking\r\n");
+ if(cfsr & 0x00000008) //SCB_CFSR_MUNSTKERR
+ IRQerrorLog("Fault occurred during exception unstacking\r\n");
+ if(cfsr & 0x00000002) //SCB_CFSR_DACCVIOL
+ IRQerrorLog("Fault was caused by invalid PC\r\n");
+ if(cfsr & 0x00000001) //SCB_CFSR_IACCVIOL
+ IRQerrorLog("Fault was caused by attempted execution from XN area\r\n");
+ #endif //WITH_ERRLOG
+ miosix_private::IRQsystemReboot();
+}
+
+void __attribute__((naked)) BusFault_Handler()
+{
+ saveContext();
+ //Call BusFault_impl(). Name is a C++ mangled name.
+ asm volatile("bl _Z13BusFault_implv");
+ restoreContext();
+}
+
+void __attribute__((noinline)) BusFault_impl()
+{
+ #if defined(WITH_PROCESSES) || defined(WITH_ERRLOG)
+ unsigned int cfsr=SCB->CFSR;
+ #endif //WITH_PROCESSES || WITH_ERRLOG
+ #ifdef WITH_PROCESSES
+ int id, arg=0;
+ if(cfsr & 0x00008000) { id=BF; arg=SCB->BFAR; }
+ else id=BF_NOADDR;
+ if(miosix::Thread::IRQreportFault(miosix_private::FaultData(
+ id,getProgramCounter(),arg)))
+ {
+ SCB->SHCSR &= ~(1<<14); //Clear BUSFAULTPENDED bit
+ return;
+ }
+ #endif //WITH_PROCESSES
+ #ifdef WITH_ERRLOG
+ IRQerrorLog("\r\n***Unexpected BusFault @ ");
+ printUnsignedInt(getProgramCounter());
+ if(cfsr & 0x00008000) //SCB_CFSR_BFARVALID
+ {
+ IRQerrorLog("Fault caused by attempted access to ");
+ printUnsignedInt(SCB->BFAR);
+ } else IRQerrorLog("The address that caused the fault is missing\r\n");
+ if(cfsr & 0x00001000) //SCB_CFSR_STKERR
+ IRQerrorLog("Fault occurred during exception stacking\r\n");
+ if(cfsr & 0x00000800) //SCB_CFSR_UNSTKERR
+ IRQerrorLog("Fault occurred during exception unstacking\r\n");
+ if(cfsr & 0x00000400) //SCB_CFSR_IMPRECISERR
+ IRQerrorLog("Fault is imprecise\r\n");
+ if(cfsr & 0x00000200) //SCB_CFSR_PRECISERR
+ IRQerrorLog("Fault is precise\r\n");
+ if(cfsr & 0x00000100) //SCB_CFSR_IBUSERR
+ IRQerrorLog("Fault happened during instruction fetch\r\n");
+ #endif //WITH_ERRLOG
+ miosix_private::IRQsystemReboot();
+}
+
+void __attribute__((naked)) UsageFault_Handler()
+{
+ saveContext();
+ //Call UsageFault_impl(). Name is a C++ mangled name.
+ asm volatile("bl _Z15UsageFault_implv");
+ restoreContext();
+}
+
+void __attribute__((noinline)) UsageFault_impl()
+{
+ #if defined(WITH_PROCESSES) || defined(WITH_ERRLOG)
+ unsigned int cfsr=SCB->CFSR;
+ #endif //WITH_PROCESSES || WITH_ERRLOG
+ #ifdef WITH_PROCESSES
+ int id;
+ if(cfsr & 0x02000000) id=UF_DIVZERO;
+ else if(cfsr & 0x01000000) id=UF_UNALIGNED;
+ else if(cfsr & 0x00080000) id=UF_COPROC;
+ else if(cfsr & 0x00040000) id=UF_EXCRET;
+ else if(cfsr & 0x00020000) id=UF_EPSR;
+ else if(cfsr & 0x00010000) id=UF_UNDEF;
+ else id=UF_UNEXP;
+ if(miosix::Thread::IRQreportFault(miosix_private::FaultData(
+ id,getProgramCounter())))
+ {
+ SCB->SHCSR &= ~(1<<12); //Clear USGFAULTPENDED bit
+ return;
+ }
+ #endif //WITH_PROCESSES
+ #ifdef WITH_ERRLOG
+ IRQerrorLog("\r\n***Unexpected UsageFault @ ");
+ printUnsignedInt(getProgramCounter());
+ if(cfsr & 0x02000000) //SCB_CFSR_DIVBYZERO
+ IRQerrorLog("Divide by zero\r\n");
+ if(cfsr & 0x01000000) //SCB_CFSR_UNALIGNED
+ IRQerrorLog("Unaligned memory access\r\n");
+ if(cfsr & 0x00080000) //SCB_CFSR_NOCP
+ IRQerrorLog("Attempted coprocessor access\r\n");
+ if(cfsr & 0x00040000) //SCB_CFSR_INVPC
+ IRQerrorLog("EXC_RETURN not expected now\r\n");
+ if(cfsr & 0x00020000) //SCB_CFSR_INVSTATE
+ IRQerrorLog("Invalid EPSR usage\r\n");
+ if(cfsr & 0x00010000) //SCB_CFSR_UNDEFINSTR
+ IRQerrorLog("Undefined instruction\r\n");
+ #endif //WITH_ERRLOG
+ miosix_private::IRQsystemReboot();
+}
+
+void DebugMon_Handler()
+{
+ #ifdef WITH_ERRLOG
+ IRQerrorLog("\r\n***Unexpected DebugMon @ ");
+ printUnsignedInt(getProgramCounter());
+ #endif //WITH_ERRLOG
+ miosix_private::IRQsystemReboot();
+}
+
+#endif //_ARCH_CORTEXM0
+
+void PendSV_Handler()
+{
+ #ifdef WITH_ERRLOG
+ IRQerrorLog("\r\n***Unexpected PendSV @ ");
+ printUnsignedInt(getProgramCounter());
+ #endif //WITH_ERRLOG
+ miosix_private::IRQsystemReboot();
+}
+
+void unexpectedInterrupt()
+{
+ #ifdef WITH_ERRLOG
+ IRQerrorLog("\r\n***Unexpected Peripheral interrupt\r\n");
+ #endif //WITH_ERRLOG
+ miosix_private::IRQsystemReboot();
+}
diff --git a/lib/miosix-kernel/miosix/arch/common/core/interrupts_cortexMx.h b/lib/miosix-kernel/miosix/arch/common/core/interrupts_cortexMx.h
new file mode 100644
index 00000000..2238d32b
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/core/interrupts_cortexMx.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+ * Copyright (C) 2010, 2011, 2012, 2013, 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 *
+ ***************************************************************************/
+
+#ifndef INTERRUPTS_H
+#define INTERRUPTS_H
+
+/**
+ * Called when an unexpected interrupt occurs.
+ * It is called by stage_1_boot.cpp for all weak interrupts not defined.
+ */
+void unexpectedInterrupt();
+
+/**
+ * Possible kind of faults that the Cortex-M3 can report.
+ * They are used to print debug information if a process causes a fault
+ */
+enum FaultType
+{
+ MP=1, //Process attempted data access outside its memory
+ MP_NOADDR=2, //Process attempted data access outside its memory (missing addr)
+ MP_XN=3, //Process attempted code access outside its memory
+ UF_DIVZERO=4, //Process attempted to divide by zero
+ UF_UNALIGNED=5,//Process attempted unaligned memory access
+ UF_COPROC=6, //Process attempted a coprocessor access
+ UF_EXCRET=7, //Process attempted an exception return
+ UF_EPSR=8, //Process attempted to access the EPSR
+ UF_UNDEF=9, //Process attempted to execute an invalid instruction
+ UF_UNEXP=10, //Unexpected usage fault
+ HARDFAULT=11, //Hardfault (for example process executed a BKPT instruction)
+ BF=12, //Busfault
+ BF_NOADDR=13 //Busfault (missing addr)
+};
+
+#endif //INTERRUPTS_H
diff --git a/lib/miosix-kernel/miosix/arch/common/core/memory_protection.h b/lib/miosix-kernel/miosix/arch/common/core/memory_protection.h
new file mode 100644
index 00000000..5d4fd249
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/core/memory_protection.h
@@ -0,0 +1,13 @@
+
+//MPU code is common for all the cortex M cores, so it has been put here
+
+#ifndef MEMORY_PROTECTION_H
+#define MEMORY_PROTECTION_H
+
+#if defined(_ARCH_CORTEXM3_STM32F2) || defined(_ARCH_CORTEXM4_STM32F4) \
+ || defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7) \
+ || defined(_ARCH_CORTEXM3_EFM32GG) || defined(_ARCH_CORTEXM4_STM32L4)
+#include "mpu_cortexMx.h"
+#endif
+
+#endif //MEMORY_PROTECTION_H
diff --git a/lib/miosix-kernel/miosix/arch/common/core/mpu_cortexMx.cpp b/lib/miosix-kernel/miosix/arch/common/core/mpu_cortexMx.cpp
new file mode 100644
index 00000000..77a85db1
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/core/mpu_cortexMx.cpp
@@ -0,0 +1,126 @@
+/***************************************************************************
+ * Copyright (C) 2018 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 *
+ ***************************************************************************/
+
+#include "mpu_cortexMx.h"
+#include
+#include
+#include
+
+namespace miosix {
+
+unsigned int sizeToMpu(unsigned int size)
+{
+ assert(size>=32);
+ unsigned int result=30-__builtin_clz(size);
+ if(size & (size-1)) result++;
+ return result;
+}
+
+#ifdef WITH_PROCESSES
+
+//
+// class MPUConfiguration
+//
+
+MPUConfiguration::MPUConfiguration(unsigned int *elfBase, unsigned int elfSize,
+ unsigned int *imageBase, unsigned int imageSize)
+{
+ // NOTE: The ARM documentation is unclear about the effect of the shareable
+ // bit on a single core architecture. Experimental evidence on an STM32F476
+ // shows that setting it in IRQconfigureCache for the internal RAM region
+ // causes the boot to fail.
+ // For this reason, all regions are marked as not shareable
+ regValues[0]=(reinterpret_cast(elfBase) & (~0x1f))
+ | MPU_RBAR_VALID_Msk | 6; //Region 6
+ regValues[2]=(reinterpret_cast(imageBase) & (~0x1f))
+ | MPU_RBAR_VALID_Msk | 7; //Region 7
+ regValues[1]=2<>1) & 31)+1));
+ char w=regValues[2*i+1] & (1<>1) & 31)+1));
+ size_t dataStart=regValues[2] & (~0x1f);
+ size_t dataEnd=dataStart+(1<<(((regValues[3]>>1) & 31)+1));
+ size_t base=reinterpret_cast(ptr);
+ //The last check is to prevent a wraparound to be considered valid
+ return ( (base>=codeStart && base+size=dataStart && base+size=base;
+}
+
+bool MPUConfiguration::withinForWriting(const void *ptr, size_t size) const
+{
+ size_t dataStart=regValues[2] & (~0x1f);
+ size_t dataEnd=dataStart+(1<<(((regValues[3]>>1) & 31)+1));
+ size_t base=reinterpret_cast(ptr);
+ //The last check is to prevent a wraparound to be considered valid
+ return base>=dataStart && base+size=base;
+}
+
+bool MPUConfiguration::withinForReading(const char* str) const
+{
+ size_t codeStart=regValues[0] & (~0x1f);
+ size_t codeEnd=codeStart+(1<<(((regValues[1]>>1) & 31)+1));
+ size_t dataStart=regValues[2] & (~0x1f);
+ size_t dataEnd=dataStart+(1<<(((regValues[3]>>1) & 31)+1));
+ size_t base=reinterpret_cast(str);
+ if((base>=codeStart) && (base=dataStart) && (base *
+ ***************************************************************************/
+
+#ifndef MPU_CORTEX_MX_H
+#define MPU_CORTEX_MX_H
+
+#include "config/miosix_settings.h"
+#include "interfaces/arch_registers.h"
+#include
+
+namespace miosix {
+
+/**
+ * \param size in bytes >32
+ * \return a value that can be written to MPU->RASR to represent that size
+ */
+unsigned int sizeToMpu(unsigned int size);
+
+/**
+ * To be called at boot to enable the MPU.
+ * Without calling this function, the MPU will not work even if regions are
+ * configured in MPUConfiguration
+ */
+inline void IRQenableMPUatBoot()
+{
+ MPU->CTRL=MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk;
+}
+
+#ifdef WITH_PROCESSES
+
+/**
+ * \internal
+ * This class is used to manage the MemoryProtectionUnit
+ */
+class MPUConfiguration
+{
+public:
+ /**
+ * Default constructor, leaves the MPU regions unconfigured
+ */
+ MPUConfiguration() {}
+
+ /**
+ * \internal
+ * \param elfBase base address of the ELF file
+ * \param elfSize size of the ELF file
+ * \param imageBase base address of the Process RAM image
+ * \param imageSize size of the Process RAM image
+ */
+ MPUConfiguration(unsigned int *elfBase, unsigned int elfSize,
+ unsigned int *imageBase, unsigned int imageSize);
+
+ /**
+ * \internal
+ * This method is used to configure the Memoy Protection region for a
+ * Process during a context-switch to a userspace thread.
+ * Can only be called inside an IRQ, not even with interrupts disabled
+ */
+ void IRQenable()
+ {
+ MPU->RBAR=regValues[0];
+ MPU->RASR=regValues[1];
+ MPU->RBAR=regValues[2];
+ MPU->RASR=regValues[3];
+ __set_CONTROL(3);
+ }
+
+ /**
+ * \internal
+ * This method is used to disable the MPU during a context-switch to a
+ * kernelspace thread.
+ * Can only be called inside an IRQ, not even with interrupts disabled
+ */
+ static void IRQdisable()
+ {
+ __set_CONTROL(2);
+ }
+
+ /**
+ * Print the MPU configuration for debugging purposes
+ */
+ void dumpConfiguration();
+
+ /**
+ * Some MPU implementations may not allow regions of arbitrary size,
+ * this function allows to round a size up to the minimum value that
+ * the MPU support.
+ * \param size the size of a memory area to be configured as an MPU
+ * region
+ * \return the size rounded to the minimum MPU region allowed that is
+ * greater or equal to the given size
+ */
+ static unsigned int roundSizeForMPU(unsigned int size);
+
+ /**
+ * Check if a buffer is within a readable segment of the process
+ * \param ptr base pointer of the buffer to check
+ * \param size buffer size
+ * \return true if the buffer is correctly within the process
+ */
+ bool withinForReading(const void *ptr, size_t size) const;
+
+ /**
+ * Check if a buffer is within a writable segment of the process
+ * \param ptr base pointer of the buffer to check
+ * \param size buffer size
+ * \return true if the buffer is correctly within the process
+ */
+ bool withinForWriting(const void *ptr, size_t size) const;
+
+ /**
+ * Check if a nul terminated string is entirely contained in the process,
+ * \param str a pointer to a nul terminated string
+ * \return true if the buffer is correctly within the process
+ */
+ bool withinForReading(const char *str) const;
+
+ //Uses default copy constructor and operator=
+private:
+ ///These value are copied into the MPU registers to configure them
+ unsigned int regValues[4];
+};
+
+#endif //WITH_PROCESSES
+
+} //namespace miosix
+
+#endif //MPU_CORTEX_MX_H
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/dcc.cpp b/lib/miosix-kernel/miosix/arch/common/drivers/dcc.cpp
new file mode 100644
index 00000000..c4d93752
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/dcc.cpp
@@ -0,0 +1,94 @@
+/***************************************************************************
+ * Copyright (C) 2011, 2012, 2013, 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 *
+ ***************************************************************************/
+
+/*
+ * DCC support inspired by dcc_stdio.c in OpenOCD
+ */
+
+#include
+#include
+#include "filesystem/ioctl.h"
+#include "dcc.h"
+
+namespace miosix {
+
+ssize_t ARMDCC::readBlock(void* buffer, size_t size, off_t where)
+{
+ return -EBADF;
+}
+
+ssize_t ARMDCC::writeBlock(const void* buffer, size_t size, off_t where)
+{
+ Lock l(mutex);
+ debugStr(reinterpret_cast(buffer),size);
+ return size;
+}
+
+void ARMDCC::IRQwrite(const char* str)
+{
+ //Actually, there is a race condition if IRQdebugWrite is called from an
+ //interrupt while the main code is printing with debugWrite, but it is
+ //not such an issue since IRQdebugWrite is called only
+ //- at boot before the kernel is started, so there are no other threads
+ //- in case of a serious error, to print what went wrong before rebooting
+ //In the second case, which is rare, the data may not be printed correctly
+ debugStr(str,-1);
+}
+
+int ARMDCC::ioctl(int cmd, void* arg)
+{
+ if(cmd==IOCTL_SYNC) return 0; //Nothing to do, but say we did somaething
+ return -ENOTTY; //Means the operation does not apply to this descriptor
+}
+
+void ARMDCC::send(unsigned char c)
+{
+ const unsigned int busy=1;
+ while(CoreDebug->DCRDR & busy) ;
+ CoreDebug->DCRDR=(static_cast(c)<<8) | busy;
+}
+
+void ARMDCC::debugStr(const char *str, int length)
+{
+ //If not being debugged, don't print anything
+ if((CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)==0) return;
+
+ if(length<0) length=strlen(str);
+ if(length>0 && str[0]=='\r') str++, length--; //TODO: better \r\n removal
+ if(length>0 && str[0]=='\n') str++, length--;
+ if(length==0) return;
+ send(1); //1=sending a string
+ send(0);
+ send(length & 0xff);
+ send(length>>8);
+ for(int i=0;i *
+ ***************************************************************************/
+
+#ifndef DCC_H
+#define DCC_H
+
+#include "filesystem/console/console_device.h"
+#include "kernel/sync.h"
+
+namespace miosix {
+
+/**
+ * This class exposes the ARM debug communication channel through the Device
+ * interface, to allow redirecting stdin and stdout to the DCC for boards that
+ * have JTAG/SWD but no serial port
+ */
+class ARMDCC : public Device
+{
+public:
+ /**
+ * Constructor.
+ */
+ ARMDCC() : Device(Device::TTY) {}
+
+ /**
+ * Read a block of data. As the DCC is write only, this function returns an
+ * error
+ * \param buffer buffer where read data will be stored
+ * \param size buffer size
+ * \param where where to read from
+ * \return number of bytes read or a negative number on failure
+ */
+ ssize_t readBlock(void *buffer, size_t size, off_t where);
+
+ /**
+ * Write a block of data
+ * \param buffer buffer where take data to write
+ * \param size buffer size
+ * \param where where to write to
+ * \return number of bytes written or a negative number on failure
+ */
+ ssize_t writeBlock(const void *buffer, size_t size, off_t where);
+
+ /**
+ * Write a string.
+ * An extension to the Device interface that adds a new member function,
+ * which is used by the kernel on console devices to write debug information
+ * before the kernel is started or in case of serious errors, right before
+ * rebooting.
+ * Can ONLY be called when the kernel is not yet started, paused or within
+ * an interrupt. This default implementation ignores writes.
+ * \param str the string to write. The string must be NUL terminated.
+ */
+ void IRQwrite(const char *str);
+
+ /**
+ * Performs device-specific operations
+ * \param cmd specifies the operation to perform
+ * \param arg optional argument that some operation require
+ * \return the exact return value depends on CMD, -1 is returned on error
+ */
+ int ioctl(int cmd, void *arg);
+
+private:
+ /**
+ * Send data to the host
+ */
+ static void send(unsigned char c);
+
+ /**
+ * Send a line of text to the host.
+ * OpenOCD will insert a \n after each line, unfortunately,
+ * as this complicates things.
+ */
+ static void debugStr(const char *str, int length);
+
+ FastMutex mutex;
+};
+
+} //namespace miosix
+
+#endif //DCC_H
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/sd_lpc2000.cpp b/lib/miosix-kernel/miosix/arch/common/drivers/sd_lpc2000.cpp
new file mode 100644
index 00000000..4ff56e68
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/sd_lpc2000.cpp
@@ -0,0 +1,587 @@
+/*
+ * Integration in Miosix by Terraneo Federico.
+ * Based on code by Martin Thomas to initialize SD cards from LPC2000
+ */
+
+#include "sd_lpc2000.h"
+#include "interfaces/bsp.h"
+#include "LPC213x.h"
+#include "board_settings.h" //For sdVoltage
+#include
+#include
+
+//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)
+
+namespace miosix {
+
+///\internal Type of card (1<<0)=MMC (1<<1)=SDv1 (1<<2)=SDv2 (1<<2)|(1<<3)=SDHC
+static unsigned char cardType=0;
+
+/*
+ * Definitions for MMC/SDC command.
+ * A command has the following format:
+ * - 1 bit @ 0 (start bit)
+ * - 1 bit @ 1 (transmission bit)
+ * - 6 bit which identify command index (CMD0..CMD63)
+ * - 32 bit command argument
+ * - 7 bit CRC
+ * - 1 bit @ 1 (end bit)
+ * In addition, ACMDxx are the sequence of two commands, CMD55 and CMDxx
+ * These constants have the following meaninig:
+ * - bit #7 @ 1 indicates that it is an ACMD. send_cmd() will send CMD55, then
+ * clear this bit and send the command with this bit @ 0 (which is start bit)
+ * - bit #6 always @ 1, because it is the transmission bit
+ * - remaining 6 bit represent command index
+ */
+#define CMD0 (0x40+0) /* GO_IDLE_STATE */
+#define CMD1 (0x40+1) /* SEND_OP_COND (MMC) */
+#define ACMD41 (0xC0+41) /* SEND_OP_COND (SDC) */
+#define CMD8 (0x40+8) /* SEND_IF_COND */
+#define CMD9 (0x40+9) /* SEND_CSD */
+#define CMD10 (0x40+10) /* SEND_CID */
+#define CMD12 (0x40+12) /* STOP_TRANSMISSION */
+#define CMD13 (0x40+13) /* SEND_STATUS */
+#define ACMD13 (0xC0+13) /* SD_STATUS (SDC) */
+#define CMD16 (0x40+16) /* SET_BLOCKLEN */
+#define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */
+#define CMD18 (0x40+18) /* READ_MULTIPLE_BLOCK */
+#define CMD23 (0x40+23) /* SET_BLOCK_COUNT (MMC) */
+#define ACMD23 (0xC0+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
+#define CMD24 (0x40+24) /* WRITE_BLOCK */
+#define CMD25 (0x40+25) /* WRITE_MULTIPLE_BLOCK */
+#define CMD42 (0x40+42) /* LOCK_UNLOCK */
+#define CMD55 (0x40+55) /* APP_CMD */
+#define CMD58 (0x40+58) /* READ_OCR */
+
+// SSPCR0 Bit-Definitions
+#define CPOL 6
+#define CPHA 7
+// SSPCR1 Bit-Defintions
+#define SSE 1
+#define MS 2
+#define SCR 8
+// SSPSR Bit-Definitions
+#define TNF 1
+#define RNE 2
+#define BSY 4
+
+#define SPI_SCK_PIN 17 // SCK P0.17 out
+#define SPI_MISO_PIN 18 // MISO P0.18 in
+#define SPI_MOSI_PIN 19 // MOSI P0.19 out
+#define SPI_SS_PIN 20 // CS p0.20 out
+
+#define SPI_SCK_FUNCBIT 2
+#define SPI_MISO_FUNCBIT 4
+#define SPI_MOSI_FUNCBIT 6
+#define SPI_SS_FUNCBIT 8
+
+///\internal Maximum speed 14745600/2=7372800
+#define SPI_PRESCALE_MIN 2
+
+///\internal Select/unselect card
+#define CS_LOW() IOCLR0 = (1<0x00FF)
+ print_error_code((unsigned char)(value>>8));
+ else
+ DBGERR("Unknown error 0x%x\n",value);
+ break;
+ }
+ return -1;
+}
+
+/**
+ * \internal
+ * Wait until card is ready
+ */
+static unsigned char wait_ready()
+{
+ unsigned char result;
+ spi_1_send(0xff);
+ for(int i=0;i<460800;i++)//Timeout ~500ms
+ {
+ result=spi_1_send(0xff);
+ if(result==0xff) return 0xff;
+ if(i%500==0) DBG("*\n");
+ }
+ DBGERR("Error: wait_ready()\n");
+ return result;
+}
+
+/**
+ * \internal
+ * Send a command to the SD card
+ * \param cmd one among the #define'd commands
+ * \param arg command's argument
+ * \return command's r1 response. If command returns a longer response, the user
+ * can continue reading the response with spi_1_send(0xff)
+ */
+static unsigned char send_cmd(unsigned char cmd, unsigned int arg)
+{
+ unsigned char n, res;
+ if(cmd & 0x80)
+ { // ACMD is the command sequence of CMD55-CMD
+ cmd&=0x7f;
+ res=send_cmd(CMD55,0);
+ if(res>1) return res;
+ }
+
+ // Select the card and wait for ready
+ CS_HIGH();
+ CS_LOW();
+
+ if(cmd==CMD0)
+ {
+ //wait_ready on CMD0 seems to cause infinite loop
+ spi_1_send(0xff);
+ } else {
+ if(wait_ready()!=0xff) return 0xff;
+ }
+ // Send command
+ spi_1_send(cmd); // Start + Command index
+ spi_1_send((unsigned char)(arg >> 24)); // Argument[31..24]
+ spi_1_send((unsigned char)(arg >> 16)); // Argument[23..16]
+ spi_1_send((unsigned char)(arg >> 8)); // Argument[15..8]
+ spi_1_send((unsigned char)arg); // Argument[7..0]
+ n=0x01; // Dummy CRC + Stop
+ if (cmd==CMD0) n=0x95; // Valid CRC for CMD0(0)
+ if (cmd==CMD8) n=0x87; // Valid CRC for CMD8(0x1AA)
+ spi_1_send(n);
+ // Receive response
+ if (cmd==CMD12) spi_1_send(0xff); // Skip a stuff byte when stop reading
+ n=10; // Wait response, try 10 times
+ do
+ res=spi_1_send(0xff);
+ while ((res & 0x80) && --n);
+ return res; // Return with the response value
+}
+
+/**
+ * \internal
+ * Receive a data packet from the SD card
+ * \param buf data buffer to store received data
+ * \param byte count (must be multiple of 4)
+ * \return true on success, false on failure
+ */
+static bool rx_datablock(unsigned char *buf, unsigned int btr)
+{
+ unsigned char token;
+ for(int i=0;i<0xffff;i++)
+ {
+ token=spi_1_send(0xff);
+ if(token!=0xff) break;
+ }
+ if(token!=0xfe) return false; // If not valid data token, retutn error
+
+ do { // Receive the data block into buffer
+ *buf=spi_1_send(0xff); buf++;
+ *buf=spi_1_send(0xff); buf++;
+ *buf=spi_1_send(0xff); buf++;
+ *buf=spi_1_send(0xff); buf++;
+ } while(btr-=4);
+ spi_1_send(0xff); // Discard CRC
+ spi_1_send(0xff);
+
+ return true; // Return success
+}
+
+/**
+ * \internal
+ * Send a data packet to the SD card
+ * \param buf 512 byte data block to be transmitted
+ * \param token data start/stop token
+ * \return true on success, false on failure
+ */
+static bool tx_datablock (const unsigned char *buf, unsigned char token)
+{
+ unsigned char resp;
+ if(wait_ready()!=0xff) return false;
+
+ spi_1_send(token); // Xmit data token
+ if (token!=0xfd)
+ { // Is data token
+ for(int i=0;i<256;i++)
+ { // Xmit the 512 byte data block
+ spi_1_send(*buf); buf++;
+ spi_1_send(*buf); buf++;
+ }
+ spi_1_send(0xff); // CRC (Dummy)
+ spi_1_send(0xff);
+ resp=spi_1_send(0xff); // Receive data response
+ if((resp & 0x1f)!=0x05) // If not accepted, return error
+ return false;
+ }
+ return true;
+}
+
+//
+// class SPISDDriver
+//
+
+intrusive_ref_ptr SPISDDriver::instance()
+{
+ static FastMutex m;
+ static intrusive_ref_ptr instance;
+ Lock l(m);
+ if(!instance) instance=new SPISDDriver();
+ return instance;
+}
+
+ssize_t SPISDDriver::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;
+ unsigned char *buf=reinterpret_cast(buffer);
+ Lock l(mutex);
+ DBG("SPISDDriver::readBlock(): nSectors=%d\n",nSectors);
+ if(!(cardType & 8)) lba*=512; // Convert to byte address if needed
+ unsigned char result;
+ if(nSectors==1)
+ { // Single block read
+ result=send_cmd(CMD17,lba); // READ_SINGLE_BLOCK
+ if(result!=0)
+ {
+ print_error_code(result);
+ CS_HIGH();
+ return -EBADF;
+ }
+ if(rx_datablock(buf,512)==false)
+ {
+ DBGERR("Block read error\n");
+ CS_HIGH();
+ return -EBADF;
+ }
+ } else { // Multiple block read
+ //DBGERR("Mbr\n");//debug only
+ result=send_cmd(CMD18,lba); // READ_MULTIPLE_BLOCK
+ if(result!=0)
+ {
+ print_error_code(result);
+ CS_HIGH();
+ return -EBADF;
+ }
+ do {
+ if(!rx_datablock(buf,512)) break;
+ buf+=512;
+ } while(--nSectors);
+ send_cmd(CMD12,0); // STOP_TRANSMISSION
+ if(nSectors!=0)
+ {
+ DBGERR("Multiple block read error\n");
+ CS_HIGH();
+ return -EBADF;
+ }
+ }
+ CS_HIGH();
+ return size;
+}
+
+ssize_t SPISDDriver::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;
+ const unsigned char *buf=reinterpret_cast(buffer);
+ Lock l(mutex);
+ DBG("SPISDDriver::writeBlock(): nSectors=%d\n",nSectors);
+ if(!(cardType & 8)) lba*=512; // Convert to byte address if needed
+ unsigned char result;
+ if(nSectors==1)
+ { // Single block write
+ result=send_cmd(CMD24,lba); // WRITE_BLOCK
+ if(result!=0)
+ {
+ print_error_code(result);
+ CS_HIGH();
+ return -EBADF;
+ }
+ if(tx_datablock(buf,0xfe)==false) // WRITE_BLOCK
+ {
+ DBGERR("Block write error\n");
+ CS_HIGH();
+ return -EBADF;
+ }
+ } else { // Multiple block write
+ //DBGERR("Mbw\n");//debug only
+ if(cardType & 6) send_cmd(ACMD23,nSectors);//Only if it is SD card
+ result=send_cmd(CMD25,lba); // WRITE_MULTIPLE_BLOCK
+ if(result!=0)
+ {
+ print_error_code(result);
+ CS_HIGH();
+ return -EBADF;
+ }
+ do {
+ if(!tx_datablock(buf,0xfc)) break;
+ buf+=512;
+ } while(--nSectors);
+ if(!tx_datablock(0,0xfd)) // STOP_TRAN token
+ {
+ DBGERR("Multiple block write error\n");
+ CS_HIGH();
+ return -EBADF;
+ }
+ }
+ CS_HIGH();
+ return size;
+}
+
+int SPISDDriver::ioctl(int cmd, void* arg)
+{
+ DBG("SPISDDriver::ioctl()\n");
+ if(cmd!=IOCTL_SYNC) return -ENOTTY;
+ Lock l(mutex);
+ CS_LOW();
+ unsigned char result=wait_ready();
+ CS_HIGH();
+ if(result==0xff) return 0;
+ else return -EFAULT;
+}
+
+SPISDDriver::SPISDDriver() : Device(Device::BLOCK)
+{
+ const int MAX_RETRY=20;//Maximum command retry before failing
+ spi_1_init(); /* init at low speed */
+ unsigned char resp;
+ int i;
+ for(i=0;i *
+ ***************************************************************************/
+
+#ifndef SD_LPC2000_H
+#define SD_LPC2000_H
+
+#include "kernel/sync.h"
+#include "filesystem/devfs/devfs.h"
+#include "filesystem/ioctl.h"
+
+namespace miosix {
+
+/**
+ * Driver for interfacing to an SD card through SPI
+ */
+class SPISDDriver : public Device
+{
+public:
+ /**
+ * \return an instance to this class, singleton
+ */
+ static intrusive_ref_ptr 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
+ */
+ SPISDDriver();
+
+ FastMutex mutex;
+};
+
+} //namespace miosix
+
+#endif //SD_LPC2000_H
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/sd_stm32f1.cpp b/lib/miosix-kernel/miosix/arch/common/drivers/sd_stm32f1.cpp
new file mode 100644
index 00000000..13a5ee23
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/sd_stm32f1.cpp
@@ -0,0 +1,1776 @@
+/***************************************************************************
+ * Copyright (C) 2010, 2011, 2012, 2013, 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 *
+ ***************************************************************************/
+
+#include "sd_stm32f1.h"
+#include "interfaces/bsp.h"
+#include "interfaces/arch_registers.h"
+#include "interfaces/delays.h"
+#include "kernel/kernel.h"
+#include "kernel/scheduler/scheduler.h"
+#include "board_settings.h" //For sdVoltage
+#include
+#include
+#include
+
+/*
+ * This driver is quite a bit complicated, due to a silicon errata in the
+ * STM32F1 microcontrollers, that prevents concurrent access to the FSMC
+ * (i.e., the external memory controller) by both the CPU and DMA.
+ * Therefore, if __ENABLE_XRAM is defined, the SDIO peripheral is used in
+ * polled mode, otherwise in DMA mode. The use in polled mode is further
+ * complicated by the fact that the SDIO peripheral does not halt the clock
+ * to the SD card if its internal fifo is full. Therefore, when using the
+ * SDIO in polled mode the only solution is to disable interrupts during
+ * the data transfer. To optimize reading and writing speed this code
+ * automatically chooses the best transfer speed using a binary search during
+ * card initialization. Also, other sources of mess are the requirement for
+ * word alignment of pointers when doing DMA transfers or writing to the SDIO
+ * peripheral. Because of that, tryng to fwrite() large bloks of data is faster
+ * if they are word aligned. An easy way to do so is to allocate them on the
+ * heap (and not doing any pointer arithmetic on the value returned by
+ * malloc/new)
+ */
+
+//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)
+
+#ifndef __ENABLE_XRAM
+/**
+ * \internal
+ * DMA2 Channel4 interrupt handler
+ */
+void __attribute__((naked)) DMA2_Channel4_5_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _ZN6miosix19DMA2channel4irqImplEv");
+ restoreContext();
+}
+
+/**
+ * \internal
+ * SDIO interrupt handler
+ */
+void __attribute__((naked)) SDIO_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _ZN6miosix11SDIOirqImplEv");
+ restoreContext();
+}
+#endif //__ENABLE_XRAM
+
+namespace miosix {
+
+#ifndef __ENABLE_XRAM
+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 Channel4 interrupt handler actual implementation
+ */
+void __attribute__((used)) DMA2channel4irqImpl()
+{
+ dmaFlags=DMA2->ISR;
+ if(dmaFlags & DMA_ISR_TEIF4) transferError=true;
+
+ DMA2->IFCR=DMA_IFCR_CGIF4;
+
+ if(!waiting) return;
+ waiting->IRQwakeup();
+ if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+ Scheduler::IRQfindNextThread();
+ waiting=0;
+}
+
+/**
+ * \internal
+ * DMA2 Channel4 interrupt handler actual implementation
+ */
+void __attribute__((used)) SDIOirqImpl()
+{
+ sdioFlags=SDIO->STA;
+ if(sdioFlags & (SDIO_STA_STBITERR | SDIO_STA_RXOVERR |
+ SDIO_STA_TXUNDERR | SDIO_STA_DTIMEOUT | SDIO_STA_DCRCFAIL))
+ transferError=true;
+
+ SDIO->ICR=0x7ff;//Clear flags
+
+ if(!waiting) return;
+ waiting->IRQwakeup();
+ if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+ Scheduler::IRQfindNextThread();
+ waiting=0;
+}
+#endif //__ENABLE_XRAM
+
+/*
+ * 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
+ */
+//const unsigned char sdVoltage=33; //Is defined in board_settings.h
+const unsigned int sdVoltageMask=1<<(sdVoltage-13); //See OCR register 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 sdD0;
+typedef Gpio sdD1;
+typedef Gpio sdD2;
+typedef Gpio sdD3;
+typedef Gpio sdCLK;
+typedef Gpio sdCMD;
+
+//
+// Class BufferConverter
+//
+
+/**
+ * \internal
+ * Convert a single buffer of *fixed* and predetermined size to and from
+ * word-aligned. To do so, if the buffer is already word aligned a cast is made,
+ * otherwise a new buffer is allocated.
+ * Note that this class allocates at most ONE buffer at any given time.
+ * Therefore any call to toWordAligned(), toWordAlignedWithoutCopy(),
+ * toOriginalBuffer() or deallocateBuffer() invalidates the buffer previousy
+ * returned by toWordAligned() and toWordAlignedWithoutCopy()
+ */
+class BufferConverter
+{
+public:
+ /**
+ * \internal
+ * The buffer will be of this size only.
+ */
+ static const int BUFFER_SIZE=512;
+
+ /**
+ * \internal
+ * \return true if the pointer is word aligned
+ */
+ static bool isWordAligned(const void *x)
+ {
+ return (reinterpret_cast(x) & 0x3)==0;
+ }
+
+ /**
+ * \internal
+ * Convert from a constunsigned char* buffer of size BUFFER_SIZE to a
+ * const unsigned int* word aligned buffer.
+ * If the original buffer is already word aligned it only does a cast,
+ * otherwise it copies the data on the original buffer to a word aligned
+ * buffer. Useful if subseqent code will read from the buffer.
+ * \param a buffer of size BUFFER_SIZE. Can be word aligned or not.
+ * \return a word aligned buffer with the same data of the given buffer
+ */
+ static const unsigned int *toWordAligned(const unsigned char *buffer);
+
+ /**
+ * \internal
+ * Convert from an unsigned char* buffer of size BUFFER_SIZE to an
+ * unsigned int* word aligned buffer.
+ * If the original buffer is already word aligned it only does a cast,
+ * otherwise it returns a new buffer which *does not* contain the data
+ * on the original buffer. Useful if subseqent code will write to the
+ * buffer. To move the written data to the original buffer, use
+ * toOriginalBuffer()
+ * \param a buffer of size BUFFER_SIZE. Can be word aligned or not.
+ * \return a word aligned buffer with undefined content.
+ */
+ static unsigned int *toWordAlignedWithoutCopy(unsigned char *buffer);
+
+ /**
+ * \internal
+ * Convert the buffer got through toWordAlignedWithoutCopy() to the
+ * original buffer. If the original buffer was word aligned, nothing
+ * happens, otherwise a memcpy is done.
+ * Note that this function does not work on buffers got through
+ * toWordAligned().
+ */
+ static void toOriginalBuffer();
+
+ /**
+ * \internal
+ * Can be called to deallocate the buffer
+ */
+ static void deallocateBuffer();
+
+private:
+ static unsigned char *originalBuffer;
+ static unsigned int *wordAlignedBuffer;
+};
+
+const unsigned int *BufferConverter::toWordAligned(const unsigned char *buffer)
+{
+ originalBuffer=0; //Tell toOriginalBuffer() that there's nothing to do
+ if(isWordAligned(buffer))
+ {
+ return reinterpret_cast(buffer);
+ } else {
+ if(wordAlignedBuffer==0)
+ wordAlignedBuffer=new unsigned int[BUFFER_SIZE/sizeof(unsigned int)];
+ std::memcpy(wordAlignedBuffer,buffer,BUFFER_SIZE);
+ return wordAlignedBuffer;
+ }
+}
+
+unsigned int *BufferConverter::toWordAlignedWithoutCopy(
+ unsigned char *buffer)
+{
+ if(isWordAligned(buffer))
+ {
+ originalBuffer=0; //Tell toOriginalBuffer() that there's nothing to do
+ return reinterpret_cast(buffer);
+ } else {
+ originalBuffer=buffer; //Save original pointer for toOriginalBuffer()
+ if(wordAlignedBuffer==0)
+ wordAlignedBuffer=new unsigned int[BUFFER_SIZE/sizeof(unsigned int)];
+ return wordAlignedBuffer;
+ }
+}
+
+void BufferConverter::toOriginalBuffer()
+{
+ if(originalBuffer==0) return;
+ std::memcpy(originalBuffer,wordAlignedBuffer,BUFFER_SIZE);
+ originalBuffer=0;
+}
+
+void BufferConverter::deallocateBuffer()
+{
+ originalBuffer=0; //Invalidate also original buffer
+ if(wordAlignedBuffer!=0)
+ {
+ delete[] wordAlignedBuffer;
+ wordAlignedBuffer=0;
+ }
+}
+
+unsigned char *BufferConverter::originalBuffer=0;
+unsigned int *BufferConverter::wordAlignedBuffer=0;
+
+//
+// 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(SDIO->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)
+ {
+ if(static_cast(cmd) & 0x80)
+ {
+ DBG("ACMD%d\n",static_cast(cmd) & 0x3f);
+ } else {
+ DBG("CMD%d\n",static_cast(cmd) & 0x3f);
+ }
+ return IRQsend(cmd,arg);
+ }
+
+ /**
+ * \internal
+ * Send a command. Can be called with interrupts disabled as it does not
+ * print any debug information.
+ * \param cmd command index (CMD0..CMD63) or ACMDxx command
+ * \param arg the 32 bit argument to the command
+ * \return a CmdResult object
+ */
+ static CmdResult IRQsend(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(rca); }
+
+private:
+ static unsigned short rca;///<\internal Card's relative address
+};
+
+CmdResult Command::IRQsend(CommandType cmd, unsigned int arg)
+{
+ unsigned char cc=static_cast(cmd);
+ //Handle ACMDxx as CMD55, CMDxx
+ if(cc & 0x80)
+ {
+ CmdResult r=IRQsend(CMD55,(static_cast(rca))<<16);
+ if(r.IRQvalidateR1Response()==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);
+ }
+
+ //Send command
+ cc &= 0x3f;
+ unsigned int command=SDIO_CMD_CPSMEN | static_cast(cc);
+ if(cc!=CMD0) command |= SDIO_CMD_WAITRESP_0; //CMD0 has no response
+ if(cc==CMD2) command |= SDIO_CMD_WAITRESP_1; //CMD2 has long response
+ if(cc==CMD9) command |= SDIO_CMD_WAITRESP_1; //CMD9 has long response
+ SDIO->ARG=arg;
+ SDIO->CMD=command;
+
+ //CMD0 has no response, so wait until it is sent
+ if(cc==CMD0)
+ {
+ for(int i=0;i<500;i++)
+ {
+ if(SDIO->STA & SDIO_STA_CMDSENT)
+ {
+ SDIO->ICR=0x7ff;//Clear flags
+ return CmdResult(cc,CmdResult::Ok);
+ }
+ delayUs(1);
+ }
+ SDIO->ICR=0x7ff;//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=SDIO->STA;
+ if(status & SDIO_STA_CMDREND)
+ {
+ SDIO->ICR=0x7ff;//Clear flags
+ if(SDIO->RESPCMD==cc) return CmdResult(cc,CmdResult::Ok);
+ else return CmdResult(cc,CmdResult::RespNotMatch);
+ }
+ if(status & SDIO_STA_CCRCFAIL)
+ {
+ SDIO->ICR=SDIO_ICR_CCRCFAILC;
+ return CmdResult(cc,CmdResult::CRCFail);
+ }
+ if(status & SDIO_STA_CTIMEOUT) break;
+ delayUs(1);
+ }
+ SDIO->ICR=SDIO_ICR_CTIMEOUTC;
+ return CmdResult(cc,CmdResult::Timeout);
+}
+
+unsigned short Command::rca=0;
+
+//
+// Class DataResult
+//
+
+/**
+ * \internal
+ * Contains the result of sending/receiving a data block
+ */
+class DataResult
+{
+public:
+
+ /**
+ * \internal
+ * Possible outcomes of sending or receiving data
+ */
+ enum Error
+ {
+ Ok=0,
+ Timeout,
+ CRCFail,
+ RXOverrun,
+ TXUnderrun,
+ StartBitFail
+ };
+
+ /**
+ * \internal
+ * Default constructor
+ */
+ DataResult(): error(Ok) {}
+
+ /**
+ * \internal
+ * Constructor, set the result.
+ * \param error error type
+ */
+ DataResult(Error error): error(error) {}
+
+ /**
+ * \internal
+ * \return the error flags
+ */
+ Error getError() { return error; }
+
+ /**
+ * \internal
+ * Checks if errors occurred while sending/receiving data.
+ * \return true if no errors, false otherwise
+ */
+ bool validateError();
+
+private:
+ Error error;
+};
+
+
+bool DataResult::validateError()
+{
+ switch(error)
+ {
+ case Ok:
+ return true;
+ case Timeout:
+ DBGERR("Data Timeout\n");
+ break;
+ case CRCFail:
+ DBGERR("Data CRC Fail\n");
+ break;
+ case RXOverrun:
+ DBGERR("Data overrun\n");
+ break;
+ case TXUnderrun:
+ DBGERR("Data underrun\n");
+ break;
+ case StartBitFail:
+ DBGERR("Data start bit Fail\n");
+ break;
+ }
+ return false;
+}
+
+//
+// Class ClockController
+//
+
+/**
+ * \internal
+ * This class controls the clock speed of the SDIO peripheral. The SDIO
+ * peripheral, when used in polled mode, requires two timing critical pieces of
+ * code: the one to send and the one to receive a data block. This because
+ * the peripheral has a 128 byte fifo while the block size is 512 byte, and
+ * if fifo underrun/overrun occurs the peripheral does not pause communcation,
+ * instead it simply aborts the data transfer. Since the speed of the code to
+ * read/write a data block depends on too many factors, such as compiler
+ * optimizations, code running from internal flash or external ram, and the
+ * cpu clock speed, a dynamic clocking approach was chosen.
+ */
+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, 72MHz/400KHz-2=178
+ SDIO->CLKCR=CLOCK_400KHz | SDIO_CLKCR_CLKEN;
+ SDIO->DTIMER=240000; //Timeout 600ms expressed in SD_CK cycles
+ }
+
+ /**
+ * \internal
+ * Automatically select the data speed.
+ * Since the maximum speed depends on many factors, such as code running in
+ * internal or external RAM, compiler optimizations etc. 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 bynary 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() { return IRQreduceClockSpeed(); }
+
+ /**
+ * \internal
+ * Same as reduceClockSpeed(), can be called with interrupts disabled.
+ * \return true on success, false on failure
+ */
+ static bool IRQreduceClockSpeed();
+
+ /**
+ * \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);
+
+ /**
+ * \internal
+ * Value of SDIO->CLKCR that will give a 400KHz clock, depending on cpu
+ * clock speed.
+ */
+ #ifdef SYSCLK_FREQ_72MHz
+ static const unsigned int CLOCK_400KHz=178;
+ #elif SYSCLK_FREQ_56MHz
+ static const unsigned int CLOCK_400KHz=138;
+ #elif SYSCLK_FREQ_48MHz
+ static const unsigned int CLOCK_400KHz=118;
+ #elif SYSCLK_FREQ_36MHz
+ static const unsigned int CLOCK_400KHz=88;
+ #elif SYSCLK_FREQ_24MHz
+ static const unsigned int CLOCK_400KHz=58;
+ #else
+ static const unsigned int CLOCK_400KHz=18;
+ #endif
+
+ ///\internal Clock enabled, bus width 4bit, clock powersave enabled.
+ static const unsigned int CLKCR_FLAGS=SDIO_CLKCR_CLKEN |
+ SDIO_CLKCR_WIDBUS_0 | SDIO_CLKCR_PWRSAV;
+
+ ///\internal Maximum number of calls to IRQreduceClockSpeed() allowed
+ ///When using polled mode this is a critical parameter, if SDIO driver
+ ///starts to fail, it might be a good idea to increase this
+ static const unsigned char MAX_ALLOWED_REDUCTIONS=7;
+
+ ///\internal value returned by getRetryCount() while *not* calibrating clock.
+ ///When using polled mode this is a critical parameter, if SDIO driver
+ ///starts to fail, it might be a good idea to increase this
+ static const unsigned char MAX_RETRY=10;
+
+ ///\internal Used to allow only one call to reduceClockSpeed()
+ static unsigned char clockReductionAvailable;
+
+ 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; //400KHz, independent of CPU clock
+ unsigned int maxFreq=1; //24MHz with CPU running @ 72MHz
+ unsigned int selected;
+ while(minFreq-maxFreq>1)
+ {
+ selected=(minFreq+maxFreq)/2;
+ DBG("Trying CLKCR=%d\n",selected);
+ setClockSpeed(selected);
+ if(sdio->readBlock(reinterpret_cast(buffer),512,0)==512)
+ minFreq=selected;
+ else maxFreq=selected;
+ }
+ //Last round of algorithm
+ setClockSpeed(maxFreq);
+ if(sdio->readBlock(reinterpret_cast(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::IRQreduceClockSpeed()
+{
+ //Ensure this function can be called only twice per calibration
+ if(clockReductionAvailable==0) return false;
+ clockReductionAvailable--;
+
+ unsigned int currentClkcr=SDIO->CLKCR & 0xff;
+ 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<10) currentClkcr++;
+ else currentClkcr+=2;
+
+ setClockSpeed(currentClkcr);
+ return true;
+}
+
+void ClockController::setClockSpeed(unsigned int clkdiv)
+{
+ SDIO->CLKCR=clkdiv | CLKCR_FLAGS;
+ //Timeout 600ms expressed in SD_CK cycles
+ SDIO->DTIMER=(6*SystemCoreClock)/((clkdiv+2)*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;iDLEN and must match the size parameter given to this
+ * function.
+ * \param buffer buffer where to store received data. Its size must be >=size
+ * \param buffer size, which *must* be multiple of 8 words (32bytes)
+ * Note that the size parameter must be expressed in word (4bytes), while
+ * the value in SDIO->DLEN is expressed in bytes.
+ * \return a DataResult object
+ */
+static DataResult IRQreceiveDataBlock(unsigned int *buffer, unsigned int size)
+{
+ // A note on speed.
+ // Due to the auto calibration of SDIO clock speed being done with
+ // IRQreceiveDataBlock(), the speed of this function must be comparable
+ // with the speed of IRQsendDataBlock(), otherwise IRQsendDataBlock()
+ // will fail because of data underrun.
+ const unsigned int *bufend=buffer+size;
+ unsigned int status;
+ for(;;)
+ {
+ status=SDIO->STA;
+ if(status & (SDIO_STA_RXOVERR | SDIO_STA_DCRCFAIL |
+ SDIO_STA_DTIMEOUT | SDIO_STA_STBITERR | SDIO_STA_DBCKEND)) break;
+ if((status & SDIO_STA_RXFIFOHF) && (buffer!=bufend))
+ {
+ //Read 8 words from the fifo, loop entirely unrolled for speed
+ *buffer=SDIO->FIFO; buffer++;
+ *buffer=SDIO->FIFO; buffer++;
+ *buffer=SDIO->FIFO; buffer++;
+ *buffer=SDIO->FIFO; buffer++;
+ *buffer=SDIO->FIFO; buffer++;
+ *buffer=SDIO->FIFO; buffer++;
+ *buffer=SDIO->FIFO; buffer++;
+ *buffer=SDIO->FIFO; buffer++;
+ }
+ }
+ SDIO->ICR=0x7ff;//Clear flags
+ if(status & SDIO_STA_RXOVERR) return DataResult(DataResult::RXOverrun);
+ if(status & SDIO_STA_DCRCFAIL) return DataResult(DataResult::CRCFail);
+ if(status & SDIO_STA_DTIMEOUT) return DataResult(DataResult::Timeout);
+ if(status & SDIO_STA_STBITERR) return DataResult(DataResult::StartBitFail);
+ //Read eventual data left in the FIFO
+ for(;;)
+ {
+ if((SDIO->STA & SDIO_STA_RXDAVL)==0) break;
+ *buffer=SDIO->FIFO; buffer++;
+ }
+ return DataResult(DataResult::Ok);
+}
+
+/**
+ * \internal
+ * Send a data block. The end of the data block must be told to the SDIO
+ * peripheral in SDIO->DLEN and must match the size parameter given to this
+ * function.
+ * \param buffer buffer where to store received data. Its size must be >=size
+ * \param buffer size, which *must* be multiple of 8 words (32bytes).
+ * Note that the size parameter must be expressed in word (4bytes), while
+ * the value in SDIO->DLEN is expressed in bytes.
+ * \return a DataResult object
+ */
+static DataResult IRQsendDataBlock(const unsigned int *buffer, unsigned int size)
+{
+ // A note on speed.
+ // Due to the auto calibration of SDIO clock speed being done with
+ // IRQreceiveDataBlock(), the speed of this function must be comparable
+ // with the speed of IRQreceiveDataBlock(), otherwise this function
+ // will fail because of data underrun.
+ const unsigned int *bufend=buffer+size;
+ unsigned int status;
+ for(;;)
+ {
+ status=SDIO->STA;
+ if(status & (SDIO_STA_TXUNDERR | SDIO_STA_DCRCFAIL |
+ SDIO_STA_DTIMEOUT | SDIO_STA_STBITERR | SDIO_STA_DBCKEND)) break;
+ if((status & SDIO_STA_TXFIFOHE) && (buffer!=bufend))
+ {
+ //Write 8 words to the fifo, loop entirely unrolled for speed
+ SDIO->FIFO=*buffer; buffer++;
+ SDIO->FIFO=*buffer; buffer++;
+ SDIO->FIFO=*buffer; buffer++;
+ SDIO->FIFO=*buffer; buffer++;
+ SDIO->FIFO=*buffer; buffer++;
+ SDIO->FIFO=*buffer; buffer++;
+ SDIO->FIFO=*buffer; buffer++;
+ SDIO->FIFO=*buffer; buffer++;
+ }
+ }
+ SDIO->ICR=0x7ff;//Clear flags
+ if(status & SDIO_STA_TXUNDERR) return DataResult(DataResult::TXUnderrun);
+ if(status & SDIO_STA_DCRCFAIL) return DataResult(DataResult::CRCFail);
+ if(status & SDIO_STA_DTIMEOUT) return DataResult(DataResult::Timeout);
+ if(status & SDIO_STA_STBITERR) return DataResult(DataResult::StartBitFail);
+ return DataResult(DataResult::Ok);
+}
+
+/**
+ * \internal
+ * Read a single block of 512 bytes from an SD/MMC card.
+ * Card must be selected prior to caling this function.
+ * \param buffer, a buffer whose size is >=512 bytes, word aligned
+ * \param lba logical block address of the block to read.
+ */
+static bool singleBlockRead(unsigned int *buffer, unsigned int lba)
+{
+ if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC
+
+ if(waitForCardReady()==false) return false;
+
+ CmdResult cr;
+ DataResult dr;
+ bool failed=true;
+ for(;;)
+ {
+ // Since we read with polling, a context switch or interrupt here
+ // would cause a fifo overrun, so we disable interrupts.
+ FastInterruptDisableLock dLock;
+
+ SDIO->DLEN=512;
+ //Block size 512 bytes, block data xfer, from card to controller
+ SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTEN;
+
+ cr=Command::IRQsend(Command::CMD17,lba);
+ if(cr.IRQvalidateR1Response())
+ {
+ dr=IRQreceiveDataBlock(buffer,512/sizeof(unsigned int));
+ SDIO->DCTRL=0; //Disable data path state machine
+
+ //If failed because too slow check if it is possible to reduce speed
+ if(dr.getError()==DataResult::RXOverrun)
+ {
+ if(ClockController::IRQreduceClockSpeed())
+ {
+ //Disabling interrupts for too long is bad
+ FastInterruptEnableLock eLock(dLock);
+ //After an error during data xfer the card might be a little
+ //confused. So send STOP_TRANSMISSION command to reassure it
+ cr=Command::send(Command::CMD12,0);
+ if(cr.validateR1Response()) continue;
+ }
+ }
+
+ if(dr.getError()==DataResult::Ok) failed=false;
+ }
+ break;
+ }
+ if(failed)
+ {
+ cr.validateR1Response();
+ dr.validateError();
+ //After an error during data xfer the card might be a little
+ //confused. So send STOP_TRANSMISSION command to reassure it
+ cr=Command::send(Command::CMD12,0);
+ cr.validateR1Response();
+ return false;
+ }
+ return true;
+}
+
+/**
+ * \internal
+ * Write a single block of 512 bytes to an SD/MMC card
+ * Card must be selected prior to caling this function.
+ * \param buffer, a buffer whose size is >=512 bytes
+ * \param lba logical block address of the block to write.
+ */
+static bool singleBlockWrite(const unsigned int *buffer, unsigned int lba)
+{
+ if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC
+
+ if(waitForCardReady()==false) return false;
+
+ bool failed=true;
+ CmdResult cr;
+ DataResult dr;
+ for(;;)
+ {
+ // Since we write with polling, a context switch or interrupt here
+ // would cause a fifo overrun, so we disable interrupts.
+ FastInterruptDisableLock dLock;
+
+ cr=Command::IRQsend(Command::CMD24,lba);
+ if(cr.IRQvalidateR1Response())
+ {
+ SDIO->DLEN=512;
+ //Block size 512 bytes, block data xfer, from controller to card
+ SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DTEN;
+
+ dr=IRQsendDataBlock(buffer,512/sizeof(unsigned int));
+ SDIO->DCTRL=0; //Disable data path state machine
+
+ //If failed because too slow check if it is possible to reduce speed
+ if(dr.getError()==DataResult::TXUnderrun)
+ {
+ if(ClockController::IRQreduceClockSpeed())
+ {
+ //Disabling interrupts for too long is bad
+ FastInterruptEnableLock eLock(dLock);
+ //After an error during data xfer the card might be a little
+ //confused. So send STOP_TRANSMISSION command to reassure it
+ cr=Command::send(Command::CMD12,0);
+ if(cr.validateR1Response()) continue;
+ }
+ }
+
+ if(dr.getError()==DataResult::Ok) failed=false;
+ }
+ break;
+ }
+ if(failed)
+ {
+ cr.validateR1Response();
+ dr.validateError();
+ //After an error during data xfer the card might be a little
+ //confused. So send STOP_TRANSMISSION command to reassure it
+ cr=Command::send(Command::CMD12,0);
+ cr.validateR1Response();
+ return false;
+ }
+ return true;
+}
+
+#else //__ENABLE_XRAM
+
+/**
+ * \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 & SDIO_STA_STBITERR) DBGERR("* SDIO Start bit error\n");
+ if(sdioFlags & SDIO_STA_RXOVERR) DBGERR("* SDIO RX Overrun\n");
+ if(sdioFlags & SDIO_STA_TXUNDERR) DBGERR("* SDIO TX Underrun error\n");
+ if(sdioFlags & SDIO_STA_DCRCFAIL) DBGERR("* SDIO Data CRC fail\n");
+ if(sdioFlags & SDIO_STA_DTIMEOUT) DBGERR("* SDIO Data timeout\n");
+}
+
+/**
+ * \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, word aligned
+ * \param nblk number of blocks to read.
+ * \param lba logical block address of the first block to read.
+ */
+static bool multipleBlockRead(unsigned int *buffer, unsigned int nblk,
+ unsigned int lba)
+{
+ if(nblk==0) return true;
+ while(nblk>511)
+ {
+ if(multipleBlockRead(buffer,511,lba)==false) return false;
+ buffer+=511*512;
+ nblk-=511;
+ lba+=511;
+ }
+ if(waitForCardReady()==false) return false;
+
+ if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC
+
+ //Clear both SDIO and DMA interrupt flags
+ SDIO->ICR=0x7ff;
+ DMA2->IFCR=DMA_IFCR_CGIF4;
+
+ transferError=false;
+ dmaFlags=sdioFlags=0;
+ waiting=Thread::getCurrentThread();
+
+ //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
+ SDIO->MASK=SDIO_MASK_STBITERRIE | //Interrupt on start bit error
+ SDIO_MASK_RXOVERRIE | //Interrupt on rx underrun
+ SDIO_MASK_TXUNDERRIE | //Interrupt on tx underrun
+ SDIO_MASK_DCRCFAILIE | //Interrupt on data CRC fail
+ SDIO_MASK_DTIMEOUTIE; //Interrupt on data timeout
+ DMA2_Channel4->CPAR=reinterpret_cast(&SDIO->FIFO);
+ DMA2_Channel4->CMAR=reinterpret_cast(buffer);
+ DMA2_Channel4->CNDTR=nblk*512/sizeof(unsigned int);
+ DMA2_Channel4->CCR=DMA_CCR4_PL_1 | //High priority DMA stream
+ DMA_CCR4_MSIZE_1 | //Write 32bit at a time to RAM
+ DMA_CCR4_PSIZE_1 | //Read 32bit at a time from SDIO
+ DMA_CCR4_MINC | //Increment RAM pointer
+ 0 | //Peripheral to memory direction
+ DMA_CCR4_TCIE | //Interrupt on transfer complete
+ DMA_CCR4_TEIE | //Interrupt on transfer error
+ DMA_CCR4_EN; //Start the DMA
+
+ SDIO->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
+ SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DMAEN | SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTEN;
+ FastInterruptDisableLock dLock;
+ while(waiting)
+ {
+ Thread::IRQwait();
+ {
+ FastInterruptEnableLock eLock(dLock);
+ Thread::yield();
+ }
+ }
+ } else transferError=true;
+ DMA2_Channel4->CCR=0;
+ while(DMA2_Channel4->CCR & DMA_CCR4_EN) ; //DMA may take time to stop
+ SDIO->DCTRL=0; //Disable data path state machine
+ SDIO->MASK=0;
+
+ // 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;
+ }
+ 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, word aligned
+ * \param nblk number of blocks to write.
+ * \param lba logical block address of the first block to write.
+ */
+static bool multipleBlockWrite(const unsigned int *buffer, unsigned int nblk,
+ unsigned int lba)
+{
+ if(nblk==0) return true;
+ while(nblk>511)
+ {
+ if(multipleBlockWrite(buffer,511,lba)==false) return false;
+ buffer+=511*512;
+ nblk-=511;
+ lba+=511;
+ }
+ 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;
+ }
+
+ //Clear both SDIO and DMA interrupt flags
+ SDIO->ICR=0x7ff;
+ DMA2->IFCR=DMA_IFCR_CGIF4;
+
+ transferError=false;
+ dmaFlags=sdioFlags=0;
+ waiting=Thread::getCurrentThread();
+
+ //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
+ SDIO->MASK=SDIO_MASK_DATAENDIE | //Interrupt on data end
+ SDIO_MASK_STBITERRIE | //Interrupt on start bit error
+ SDIO_MASK_RXOVERRIE | //Interrupt on rx underrun
+ SDIO_MASK_TXUNDERRIE | //Interrupt on tx underrun
+ SDIO_MASK_DCRCFAILIE | //Interrupt on data CRC fail
+ SDIO_MASK_DTIMEOUTIE; //Interrupt on data timeout
+ DMA2_Channel4->CPAR=reinterpret_cast(&SDIO->FIFO);
+ DMA2_Channel4->CMAR=reinterpret_cast(buffer);
+ DMA2_Channel4->CNDTR=nblk*512/sizeof(unsigned int);
+ DMA2_Channel4->CCR=DMA_CCR4_PL_1 | //High priority DMA stream
+ DMA_CCR4_MSIZE_1 | //Read 32bit at a time from RAM
+ DMA_CCR4_PSIZE_1 | //Write 32bit at a time to SDIO
+ DMA_CCR4_MINC | //Increment RAM pointer
+ DMA_CCR4_DIR | //Memory to peripheral direction
+ DMA_CCR4_TEIE | //Interrupt on transfer error
+ DMA_CCR4_EN; //Start the DMA
+
+ SDIO->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
+ SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DMAEN | SDIO_DCTRL_DTEN;
+ FastInterruptDisableLock dLock;
+ while(waiting)
+ {
+ Thread::IRQwait();
+ {
+ FastInterruptEnableLock eLock(dLock);
+ Thread::yield();
+ }
+ }
+ } else transferError=true;
+ DMA2_Channel4->CCR=0;
+ while(DMA2_Channel4->CCR & DMA_CCR4_EN) ; //DMA may take time to stop
+ SDIO->DCTRL=0; //Disable data path state machine
+ SDIO->MASK=0;
+
+ // 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;
+}
+#endif //__ENABLE_XRAM
+
+//
+// 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->APB2ENR |= RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN;
+ RCC_SYNC();
+ #ifdef __ENABLE_XRAM
+ RCC->AHBENR |= RCC_AHBENR_SDIOEN;
+ #else //__ENABLE_XRAM
+ RCC->AHBENR |= RCC_AHBENR_SDIOEN | RCC_AHBENR_DMA2EN;
+ #endif //__ENABLE_XRAM
+ RCC_SYNC();
+ sdD0::mode(Mode::ALTERNATE);
+ sdD1::mode(Mode::ALTERNATE);
+ sdD2::mode(Mode::ALTERNATE);
+ sdD3::mode(Mode::ALTERNATE);
+ sdCLK::mode(Mode::ALTERNATE);
+ sdCMD::mode(Mode::ALTERNATE);
+ }
+ #ifndef __ENABLE_XRAM
+ NVIC_SetPriority(DMA2_Channel4_5_IRQn,15);//Low priority for DMA
+ NVIC_EnableIRQ(DMA2_Channel4_5_IRQn);
+ NVIC_SetPriority(SDIO_IRQn,15);//Low priority for SDIO
+ NVIC_EnableIRQ(SDIO_IRQn);
+ #endif //__ENABLE_XRAM
+
+ SDIO->POWER=0; //Power off state
+ delayUs(1);
+ SDIO->CLKCR=0;
+ SDIO->CMD=0;
+ SDIO->DCTRL=0;
+ SDIO->ICR=0xc007ff;
+ SDIO->POWER=SDIO_POWER_PWRCTRL_1 | SDIO_POWER_PWRCTRL_0; //Power on state
+ //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 SDIODriver::instance()
+{
+ static FastMutex m;
+ static intrusive_ref_ptr instance;
+ Lock 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 l(mutex);
+ DBG("SDIODriver::readBlock(): nSectors=%d\n",nSectors);
+ bool aligned=BufferConverter::isWordAligned(buffer);
+ if(aligned==false) DBG("Buffer misaligned\n");
+
+ for(int i=0;i(buffer);
+ unsigned int tempLba=lba;
+ for(unsigned int j=0;j(buffer),
+ nSectors,lba)==false) error=true;
+ } else {
+ unsigned char *tempBuffer=reinterpret_cast(buffer);
+ unsigned int tempLba=lba;
+ for(unsigned int j=0;j0) 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 l(mutex);
+ DBG("SDIODriver::writeBlock(): nSectors=%d\n",nSectors);
+ bool aligned=BufferConverter::isWordAligned(buffer);
+ if(aligned==false) DBG("Buffer misaligned\n");
+
+ for(int i=0;i(buffer);
+ unsigned int tempLba=lba;
+ for(unsigned int j=0;j(buffer),
+ nSectors,lba)==false) error=true;
+ } else {
+ const unsigned char *tempBuffer=
+ reinterpret_cast(buffer);
+ unsigned int tempLba=lba;
+ for(unsigned int j=0;j0) 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 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;
+ }
+
+ r=Command::send(Command::ACMD6,2); //Set 4 bit bus width
+ if(r.validateR1Response()==false) return;
+
+ 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");
+}
+
+} //namespace miosix
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/sd_stm32f1.h b/lib/miosix-kernel/miosix/arch/common/drivers/sd_stm32f1.h
new file mode 100644
index 00000000..f9b0627d
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/sd_stm32f1.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ * 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 *
+ ***************************************************************************/
+
+#ifndef SD_STM32F1_H
+#define SD_STM32F1_H
+
+#include "kernel/sync.h"
+#include "filesystem/devfs/devfs.h"
+#include "filesystem/ioctl.h"
+
+namespace miosix {
+
+/**
+ * Driver for the SDIO peripheral in STM32F1 microcontrollers
+ */
+class SDIODriver : public Device
+{
+public:
+ /**
+ * \return an instance to this class, singleton
+ */
+ static intrusive_ref_ptr 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_STM32F1_H
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/sd_stm32f2_f4.cpp b/lib/miosix-kernel/miosix/arch/common/drivers/sd_stm32f2_f4.cpp
new file mode 100644
index 00000000..da9e577e
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/sd_stm32f2_f4.cpp
@@ -0,0 +1,1478 @@
+/***************************************************************************
+ * Copyright (C) 2010, 2011, 2012, 2013, 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 *
+ ***************************************************************************/
+
+#include "sd_stm32f2_f4.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
+#include
+#include
+
+//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)
+
+/*
+ * The SDMMC1 peripheral in the STM32F7 is basically the old SDIO with the
+ * registers renamed and a few bits changed. Let's map the old names in the new
+ */
+#if defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)
+
+#define SDIO SDMMC1
+#define RCC_APB2ENR_SDIOEN RCC_APB2ENR_SDMMC1EN
+#define SDIO_IRQn SDMMC1_IRQn
+
+#define SDIO_STA_STBITERR 0 //This bit has been removed
+#define SDIO_STA_RXOVERR SDMMC_STA_RXOVERR
+#define SDIO_STA_TXUNDERR SDMMC_STA_TXUNDERR
+#define SDIO_STA_DTIMEOUT SDMMC_STA_DTIMEOUT
+#define SDIO_STA_DCRCFAIL SDMMC_STA_DCRCFAIL
+#define SDIO_STA_CMDSENT SDMMC_STA_CMDSENT
+#define SDIO_STA_CMDREND SDMMC_STA_CMDREND
+#define SDIO_STA_CCRCFAIL SDMMC_STA_CCRCFAIL
+#define SDIO_STA_CTIMEOUT SDMMC_STA_CTIMEOUT
+
+#define SDIO_CMD_CPSMEN SDMMC_CMD_CPSMEN
+#define SDIO_CMD_WAITRESP_0 SDMMC_CMD_WAITRESP_0
+#define SDIO_CMD_WAITRESP_1 SDMMC_CMD_WAITRESP_1
+
+#define SDIO_ICR_CTIMEOUTC SDMMC_ICR_CTIMEOUTC
+#define SDIO_ICR_CCRCFAILC SDMMC_ICR_CCRCFAILC
+
+#define SDIO_CLKCR_CLKEN SDMMC_CLKCR_CLKEN
+#define SDIO_CLKCR_PWRSAV SDMMC_CLKCR_PWRSAV
+#define SDIO_CLKCR_PWRSAV SDMMC_CLKCR_PWRSAV
+
+#define SDIO_MASK_STBITERRIE 0 //This bit has been removed
+#define SDIO_MASK_RXOVERRIE SDMMC_MASK_RXOVERRIE
+#define SDIO_MASK_TXUNDERRIE SDMMC_MASK_TXUNDERRIE
+#define SDIO_MASK_DCRCFAILIE SDMMC_MASK_DCRCFAILIE
+#define SDIO_MASK_DTIMEOUTIE SDMMC_MASK_DTIMEOUTIE
+#define SDIO_MASK_DATAENDIE SDMMC_MASK_DATAENDIE
+
+#define SDIO_DCTRL_DMAEN SDMMC_DCTRL_DMAEN
+#define SDIO_DCTRL_DTDIR SDMMC_DCTRL_DTDIR
+#define SDIO_DCTRL_DTEN SDMMC_DCTRL_DTEN
+
+#define SDIO_POWER_PWRCTRL_1 SDMMC_POWER_PWRCTRL_1
+#define SDIO_POWER_PWRCTRL_0 SDMMC_POWER_PWRCTRL_0
+
+#endif //defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)
+
+/**
+ * \internal
+ * DMA2 Stream3 interrupt handler
+ */
+void __attribute__((naked)) DMA2_Stream3_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _ZN6miosix18DMA2stream3irqImplEv");
+ restoreContext();
+}
+
+/**
+ * \internal
+ * SDIO interrupt handler
+ */
+#if defined(_ARCH_CORTEXM7_STM32F7) || defined(_ARCH_CORTEXM7_STM32H7)
+void __attribute__((naked)) SDMMC1_IRQHandler()
+#else //stm32f2 and stm32f4
+void __attribute__((naked)) SDIO_IRQHandler()
+#endif
+{
+ saveContext();
+ asm volatile("bl _ZN6miosix11SDIOirqImplEv");
+ 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)) DMA2stream3irqImpl()
+{
+ dmaFlags=DMA2->LISR;
+ if(dmaFlags & (DMA_LISR_TEIF3 | DMA_LISR_DMEIF3 | DMA_LISR_FEIF3))
+ transferError=true;
+
+ DMA2->LIFCR=DMA_LIFCR_CTCIF3 |
+ DMA_LIFCR_CTEIF3 |
+ DMA_LIFCR_CDMEIF3 |
+ DMA_LIFCR_CFEIF3;
+
+ if(!waiting) return;
+ waiting->IRQwakeup();
+ if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+ Scheduler::IRQfindNextThread();
+ waiting=0;
+}
+
+/**
+ * \internal
+ * DMA2 Stream3 interrupt handler actual implementation
+ */
+void __attribute__((used)) SDIOirqImpl()
+{
+ sdioFlags=SDIO->STA;
+ if(sdioFlags & (SDIO_STA_STBITERR | SDIO_STA_RXOVERR |
+ SDIO_STA_TXUNDERR | SDIO_STA_DTIMEOUT | SDIO_STA_DCRCFAIL))
+ transferError=true;
+
+ SDIO->ICR=0x7ff;//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 sdD0;
+typedef Gpio sdD1;
+typedef Gpio sdD2;
+typedef Gpio sdD3;
+typedef Gpio sdCLK;
+typedef Gpio sdCMD;
+
+//
+// Class BufferConverter
+//
+
+/**
+ * \internal
+ * After fixing the FSMC bug in the stm32f1, ST decided to willingly introduce
+ * another quirk in the stm32f4. They introduced a core coupled memory that is
+ * not accessible by the DMA. While from an hardware perspective it may make
+ * sense, it is a bad design decision when viewed from the software side.
+ * This is because if application code allocates a buffer in the core coupled
+ * memory and passes that to an fread() or fwrite() call, that buffer is
+ * forwarded here, and this driver is DMA-based... Now, in an OS such as Miosix
+ * that tries to shield the application developer from such quirks, it is
+ * unacceptable to fail to work in such an use case, so this class exists to
+ * try and work around this.
+ * In essence, the first "bad buffer" that is passed to a readBlock() or
+ * writeBlock() causes the allocation on the heap (which Miosix guarantees
+ * is not allocated in the core coupled memory) of a 512 byte buffer which is
+ * then never deallocated and always reused to deal with these bad buffers.
+ * While this works, performance suffers for two reasons: first, when dealing
+ * with those bad buffers, the filesystem code is no longer zero copy, and
+ * second because multiple block read/writes between bad buffers and the SD
+ * card are implemented as a sequence of single block read/writes.
+ * If you're an application developer and care about speed, try to allocate
+ * your buffers in the heap if you're coding for the STM32F4.
+ */
+class BufferConverter
+{
+public:
+ /**
+ * \internal
+ * The buffer will be of this size only.
+ */
+ static const int BUFFER_SIZE=512;
+
+ /**
+ * \internal
+ * \return true if the pointer is not inside the CCM
+ */
+ static bool isGoodBuffer(const void *x)
+ {
+ unsigned int ptr=reinterpret_cast(x);
+ return (ptr<0x10000000) || (ptr>=(0x10000000+64*1024));
+ }
+
+ /**
+ * \internal
+ * Convert from a constunsigned char* buffer of size BUFFER_SIZE to a
+ * const unsigned int* word aligned buffer.
+ * If the original buffer is already word aligned it only does a cast,
+ * otherwise it copies the data on the original buffer to a word aligned
+ * buffer. Useful if subseqent code will read from the buffer.
+ * \param a buffer of size BUFFER_SIZE. Can be word aligned or not.
+ * \return a word aligned buffer with the same data of the given buffer
+ */
+ static const unsigned char *toWordAligned(const unsigned char *buffer);
+
+ /**
+ * \internal
+ * Convert from an unsigned char* buffer of size BUFFER_SIZE to an
+ * unsigned int* word aligned buffer.
+ * If the original buffer is already word aligned it only does a cast,
+ * otherwise it returns a new buffer which *does not* contain the data
+ * on the original buffer. Useful if subseqent code will write to the
+ * buffer. To move the written data to the original buffer, use
+ * toOriginalBuffer()
+ * \param a buffer of size BUFFER_SIZE. Can be word aligned or not.
+ * \return a word aligned buffer with undefined content.
+ */
+ static unsigned char *toWordAlignedWithoutCopy(unsigned char *buffer);
+
+ /**
+ * \internal
+ * Convert the buffer got through toWordAlignedWithoutCopy() to the
+ * original buffer. If the original buffer was word aligned, nothing
+ * happens, otherwise a memcpy is done.
+ * Note that this function does not work on buffers got through
+ * toWordAligned().
+ */
+ static void toOriginalBuffer();
+
+ /**
+ * \internal
+ * Can be called to deallocate the buffer
+ */
+ static void deallocateBuffer();
+
+private:
+ static unsigned char *originalBuffer;
+ static unsigned char *wordAlignedBuffer;
+};
+
+const unsigned char *BufferConverter::toWordAligned(const unsigned char *buffer)
+{
+ originalBuffer=0; //Tell toOriginalBuffer() that there's nothing to do
+ if(isGoodBuffer(buffer))
+ {
+ return buffer;
+ } else {
+ if(wordAlignedBuffer==0)
+ wordAlignedBuffer=new unsigned char[BUFFER_SIZE];
+ std::memcpy(wordAlignedBuffer,buffer,BUFFER_SIZE);
+ return wordAlignedBuffer;
+ }
+}
+
+unsigned char *BufferConverter::toWordAlignedWithoutCopy(
+ unsigned char *buffer)
+{
+ if(isGoodBuffer(buffer))
+ {
+ originalBuffer=0; //Tell toOriginalBuffer() that there's nothing to do
+ return buffer;
+ } else {
+ originalBuffer=buffer; //Save original pointer for toOriginalBuffer()
+ if(wordAlignedBuffer==0)
+ wordAlignedBuffer=new unsigned char[BUFFER_SIZE];
+ return wordAlignedBuffer;
+ }
+}
+
+void BufferConverter::toOriginalBuffer()
+{
+ if(originalBuffer==0) return;
+ std::memcpy(originalBuffer,wordAlignedBuffer,BUFFER_SIZE);
+ originalBuffer=0;
+}
+
+void BufferConverter::deallocateBuffer()
+{
+ originalBuffer=0; //Invalidate also original buffer
+ if(wordAlignedBuffer!=0)
+ {
+ delete[] wordAlignedBuffer;
+ wordAlignedBuffer=0;
+ }
+}
+
+unsigned char *BufferConverter::originalBuffer=0;
+unsigned char *BufferConverter::wordAlignedBuffer=0;
+
+//
+// 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(SDIO->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(rca); }
+
+private:
+ static unsigned short rca;///<\internal Card's relative address
+};
+
+CmdResult Command::send(CommandType cmd, unsigned int arg)
+{
+ unsigned char cc=static_cast(cmd);
+ //Handle ACMDxx as CMD55, CMDxx
+ if(cc & 0x80)
+ {
+ DBG("ACMD%d\n",cc & 0x3f);
+ CmdResult r=send(CMD55,(static_cast(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=SDIO_CMD_CPSMEN | static_cast(cc);
+ if(cc!=CMD0) command |= SDIO_CMD_WAITRESP_0; //CMD0 has no response
+ if(cc==CMD2) command |= SDIO_CMD_WAITRESP_1; //CMD2 has long response
+ if(cc==CMD9) command |= SDIO_CMD_WAITRESP_1; //CMD9 has long response
+ SDIO->ARG=arg;
+ SDIO->CMD=command;
+
+ //CMD0 has no response, so wait until it is sent
+ if(cc==CMD0)
+ {
+ for(int i=0;i<500;i++)
+ {
+ if(SDIO->STA & SDIO_STA_CMDSENT)
+ {
+ SDIO->ICR=0x7ff;//Clear flags
+ return CmdResult(cc,CmdResult::Ok);
+ }
+ delayUs(1);
+ }
+ SDIO->ICR=0x7ff;//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=SDIO->STA;
+ if(status & SDIO_STA_CMDREND)
+ {
+ SDIO->ICR=0x7ff;//Clear flags
+ if(SDIO->RESPCMD==cc) return CmdResult(cc,CmdResult::Ok);
+ else return CmdResult(cc,CmdResult::RespNotMatch);
+ }
+ if(status & SDIO_STA_CCRCFAIL)
+ {
+ SDIO->ICR=SDIO_ICR_CCRCFAILC;
+ return CmdResult(cc,CmdResult::CRCFail);
+ }
+ if(status & SDIO_STA_CTIMEOUT) break;
+ delayUs(1);
+ }
+ SDIO->ICR=SDIO_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
+ SDIO->CLKCR=CLOCK_400KHz | SDIO_CLKCR_CLKEN;
+ SDIO->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=118; //48MHz/(118+2)=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=SDIO_CLKCR_CLKEN | SDIO_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 |
+ SDIO_CLKCR_WIDBUS_0 | SDIO_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(buffer),512,0)==512)
+ minFreq=selected;
+ else maxFreq=selected;
+ }
+ //Last round of algorithm
+ setClockSpeed(maxFreq);
+ if(sdio->readBlock(reinterpret_cast(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=SDIO->CLKCR & 0xff;
+ 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<10) currentClkcr++;
+ else currentClkcr+=2;
+
+ setClockSpeed(currentClkcr);
+ return true;
+}
+
+void ClockController::setClockSpeed(unsigned int clkdiv)
+{
+ SDIO->CLKCR=clkdiv | CLKCR_FLAGS;
+ //Timeout 600ms expressed in SD_CK cycles
+ SDIO->DTIMER=(6*SDIOCLK)/((clkdiv+2)*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;iICR=0x7ff;
+ DMA2->LIFCR=DMA_LIFCR_CTCIF3 |
+ DMA_LIFCR_CTEIF3 |
+ DMA_LIFCR_CDMEIF3 |
+ DMA_LIFCR_CFEIF3;
+
+ transferError=false;
+ dmaFlags=sdioFlags=0;
+ waiting=Thread::getCurrentThread();
+
+ //Select DMA transfer size based on buffer alignment. Best performance
+ //is achieved when the buffer is aligned on a 4 byte boundary
+ switch(reinterpret_cast(buffer) & 0x3)
+ {
+ case 0: return DMA_SxCR_MSIZE_1; //DMA reads 32bit at a time
+ case 2: return DMA_SxCR_MSIZE_0; //DMA reads 16bit at a time
+ default: return 0; //DMA reads 8bit at a time
+ }
+}
+
+/**
+ * \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
+
+ unsigned int memoryTransferSize=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
+ SDIO->MASK=SDIO_MASK_STBITERRIE | //Interrupt on start bit error
+ SDIO_MASK_RXOVERRIE | //Interrupt on rx underrun
+ SDIO_MASK_TXUNDERRIE | //Interrupt on tx underrun
+ SDIO_MASK_DCRCFAILIE | //Interrupt on data CRC fail
+ SDIO_MASK_DTIMEOUTIE; //Interrupt on data timeout
+ DMA2_Stream3->PAR=reinterpret_cast(&SDIO->FIFO);
+ DMA2_Stream3->M0AR=reinterpret_cast(buffer);
+ //Note: DMA2_Stream3->NDTR is don't care in peripheral flow control mode
+ DMA2_Stream3->FCR=DMA_SxFCR_FEIE | //Interrupt on fifo error
+ DMA_SxFCR_DMDIS | //Fifo enabled
+ DMA_SxFCR_FTH_0; //Take action if fifo half full
+ DMA2_Stream3->CR=DMA_SxCR_CHSEL_2 | //Channel 4 (SDIO)
+ DMA_SxCR_PBURST_0 | //4-beat bursts read from SDIO
+ DMA_SxCR_PL_0 | //Medium priority DMA stream
+ memoryTransferSize | //RAM data size depends on alignment
+ DMA_SxCR_PSIZE_1 | //Read 32bit at a time from SDIO
+ DMA_SxCR_MINC | //Increment RAM pointer
+ 0 | //Peripheral to memory direction
+ 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;
+ 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
+ SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DMAEN | SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTEN;
+ FastInterruptDisableLock dLock;
+ while(waiting)
+ {
+ Thread::IRQwait();
+ {
+ FastInterruptEnableLock eLock(dLock);
+ Thread::yield();
+ }
+ }
+ } else transferError=true;
+ DMA2_Stream3->CR=0;
+ while(DMA2_Stream3->CR & DMA_SxCR_EN) ; //DMA may take time to stop
+ SDIO->DCTRL=0; //Disable data path state machine
+ SDIO->MASK=0;
+
+ // 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;
+ }
+
+ unsigned int memoryTransferSize=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
+ SDIO->MASK=SDIO_MASK_DATAENDIE | //Interrupt on data end
+ SDIO_MASK_STBITERRIE | //Interrupt on start bit error
+ SDIO_MASK_RXOVERRIE | //Interrupt on rx underrun
+ SDIO_MASK_TXUNDERRIE | //Interrupt on tx underrun
+ SDIO_MASK_DCRCFAILIE | //Interrupt on data CRC fail
+ SDIO_MASK_DTIMEOUTIE; //Interrupt on data timeout
+ DMA2_Stream3->PAR=reinterpret_cast(&SDIO->FIFO);
+ DMA2_Stream3->M0AR=reinterpret_cast(buffer);
+ //Note: DMA2_Stream3->NDTR is don't care in peripheral flow control mode
+ //Quirk: not enabling DMA_SxFCR_FEIE because the SDIO seems to generate
+ //a spurious fifo error. The code was tested and the transfer completes
+ //successfully even in the presence of this fifo error
+ DMA2_Stream3->FCR=DMA_SxFCR_DMDIS | //Fifo enabled
+ DMA_SxFCR_FTH_1 | //Take action if fifo full
+ DMA_SxFCR_FTH_0;
+ DMA2_Stream3->CR=DMA_SxCR_CHSEL_2 | //Channel 4 (SDIO)
+ DMA_SxCR_PBURST_0 | //4-beat bursts write to SDIO
+ DMA_SxCR_PL_0 | //Medium priority DMA stream
+ memoryTransferSize | //RAM data size depends on alignment
+ DMA_SxCR_PSIZE_1 | //Write 32bit at a time to SDIO
+ DMA_SxCR_MINC | //Increment RAM pointer
+ 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;
+ 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
+ SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DMAEN | SDIO_DCTRL_DTEN;
+ FastInterruptDisableLock dLock;
+ while(waiting)
+ {
+ Thread::IRQwait();
+ {
+ FastInterruptEnableLock eLock(dLock);
+ Thread::yield();
+ }
+ }
+ } else transferError=true;
+ DMA2_Stream3->CR=0;
+ while(DMA2_Stream3->CR & DMA_SxCR_EN) ; //DMA may take time to stop
+ SDIO->DCTRL=0; //Disable data path state machine
+ SDIO->MASK=0;
+
+ // 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->AHB1ENR |= RCC_AHB1ENR_GPIOCEN
+ | RCC_AHB1ENR_GPIODEN
+ | RCC_AHB1ENR_DMA2EN;
+ RCC_SYNC();
+ RCC->APB2ENR |= RCC_APB2ENR_SDIOEN;
+ 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(DMA2_Stream3_IRQn,15);//Low priority for DMA
+ NVIC_EnableIRQ(DMA2_Stream3_IRQn);
+ NVIC_SetPriority(SDIO_IRQn,15);//Low priority for SDIO
+ NVIC_EnableIRQ(SDIO_IRQn);
+
+ SDIO->POWER=0; //Power off state
+ delayUs(1);
+ SDIO->CLKCR=0;
+ SDIO->CMD=0;
+ SDIO->DCTRL=0;
+ SDIO->ICR=0xc007ff;
+ SDIO->POWER=SDIO_POWER_PWRCTRL_1 | SDIO_POWER_PWRCTRL_0; //Power on state
+ //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 SDIODriver::instance()
+{
+ static FastMutex m;
+ static intrusive_ref_ptr instance;
+ Lock 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 l(mutex);
+ DBG("SDIODriver::readBlock(): nSectors=%d\n",nSectors);
+ bool goodBuffer=BufferConverter::isGoodBuffer(buffer);
+ if(goodBuffer==false) DBG("Buffer inside CCM\n");
+
+ for(int i=0;i(buffer),
+ nSectors,lba)==false) error=true;
+ } else {
+ //Fallback code to work around CCM
+ unsigned char *tempBuffer=reinterpret_cast(buffer);
+ unsigned int tempLba=lba;
+ for(unsigned int j=0;j0) 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 l(mutex);
+ DBG("SDIODriver::writeBlock(): nSectors=%d\n",nSectors);
+ bool goodBuffer=BufferConverter::isGoodBuffer(buffer);
+ if(goodBuffer==false) DBG("Buffer inside CCM\n");
+
+ for(int i=0;i(buffer),
+ nSectors,lba)==false) error=true;
+ } else {
+ //Fallback code to work around CCM
+ const unsigned char *tempBuffer=
+ reinterpret_cast(buffer);
+ unsigned int tempLba=lba;
+ for(unsigned int j=0;j0) 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 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");
+}
+
+} //namespace miosix
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/sd_stm32f2_f4.h b/lib/miosix-kernel/miosix/arch/common/drivers/sd_stm32f2_f4.h
new file mode 100644
index 00000000..49216dcc
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/sd_stm32f2_f4.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ * 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 *
+ ***************************************************************************/
+
+#ifndef SD_STM32F2_F4_H
+#define SD_STM32F2_F4_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 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_STM32F2_F4_H
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/serial.h b/lib/miosix-kernel/miosix/arch/common/drivers/serial.h
new file mode 100644
index 00000000..1ccb3d3b
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/serial.h
@@ -0,0 +1,18 @@
+
+//Serial code is common for all the cortex M cores, so it has been put here
+
+#ifdef _ARCH_ARM7_LPC2000
+#include "serial_lpc2000.h"
+#elif defined(_ARCH_CORTEXM0_STM32) || defined(_ARCH_CORTEXM3_STM32) \
+ || defined(_ARCH_CORTEXM4_STM32F4) || defined(_ARCH_CORTEXM3_STM32F2) \
+ || defined(_ARCH_CORTEXM3_STM32L1) || defined(_ARCH_CORTEXM7_STM32F7) \
+ || defined(_ARCH_CORTEXM7_STM32H7) || defined(_ARCH_CORTEXM4_STM32F3) \
+ || defined(_ARCH_CORTEXM4_STM32L4)
+#include "serial_stm32.h"
+#elif defined(_ARCH_CORTEXM3_EFM32GG)
+#include "serial_efm32.h"
+#elif defined(_ARCH_CORTEXM4_ATSAM4L)
+#include "serial_atsam4l.h"
+#else
+#error "Unknown arch"
+#endif
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/serial_atsam4l.cpp b/lib/miosix-kernel/miosix/arch/common/drivers/serial_atsam4l.cpp
new file mode 100644
index 00000000..a62b515b
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/serial_atsam4l.cpp
@@ -0,0 +1,245 @@
+/***************************************************************************
+ * Copyright (C) 2020 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 *
+ ***************************************************************************/
+
+#include
+#include
+#include
+#include
+#include "serial_atsam4l.h"
+#include "kernel/sync.h"
+#include "kernel/scheduler/scheduler.h"
+#include "interfaces/portability.h"
+#include "filesystem/ioctl.h"
+
+using namespace std;
+using namespace miosix;
+
+static const int numPorts=4; //USART0 to USART3
+
+/// Pointer to serial port classes to let interrupts access the classes
+static ATSAMSerial *ports[numPorts]={0};
+
+/**
+ * \internal interrupt routine for usart2 rx actual implementation
+ */
+void __attribute__((noinline)) usart2rxIrqImpl()
+{
+ if(ports[2]) ports[2]->IRQhandleInterrupt();
+}
+
+/**
+ * \internal interrupt routine for usart2 rx
+ */
+void __attribute__((naked)) USART2_Handler()
+{
+ saveContext();
+ asm volatile("bl _Z15usart2rxIrqImplv");
+ restoreContext();
+}
+
+namespace miosix {
+
+//
+// class ATSAMSerial
+//
+
+// A note on the baudrate/500: the buffer is selected so as to withstand
+// 20ms of full data rate. In the 8N1 format one char is made of 10 bits.
+// So (baudrate/10)*0.02=baudrate/500
+ATSAMSerial::ATSAMSerial(int id, int baudrate)
+ : Device(Device::TTY), rxQueue(rxQueueMin+baudrate/500), rxWaiting(0),
+ idle(true), portId(id)
+{
+ if(id!=2 || ports[portId]) errorHandler(UNEXPECTED);
+
+ {
+ InterruptDisableLock dLock;
+ ports[portId]=this;
+
+ port=USART2;
+
+ //TODO: USART2 hardcoded
+ PM->PM_UNLOCK=0xaa<<24 | PM_PBAMASK_OFFSET;
+ PM->PM_PBAMASK |= PM_PBAMASK_USART2;
+ NVIC_SetPriority(USART2_IRQn,15);//Lowest priority for serial
+ NVIC_EnableIRQ(USART2_IRQn);
+ }
+
+ port->US_BRGR = ((SystemCoreClock/baudrate/4)+1)/2; //TODO: fractional part
+ port->US_MR = US_MR_FILTER // Filter input with majority of 3 samples
+ | US_MR_OVER // 8 cycles oversample
+ | US_MR_PAR_NONE // No parity
+ | US_MR_CHRL_8 // 8 bit char
+ | US_MR_USCLKS_MCK // CLK_USART is clock source
+ | US_MR_MODE_NORMAL;// Just a plain usart, please
+
+ port->US_RTOR=10; //Timeout 10 bits (one char time)
+ port->US_IER = US_IER_RXRDY | US_IER_TIMEOUT;
+
+ port->US_CR = US_CR_TXEN | US_CR_RXEN;
+}
+
+ssize_t ATSAMSerial::readBlock(void *buffer, size_t size, off_t where)
+{
+ Lock l(rxMutex);
+ char *buf=reinterpret_cast(buffer);
+ size_t result=0;
+ FastInterruptDisableLock dLock;
+ for(;;)
+ {
+ //Try to get data from the queue
+ for(;result0) break;
+ if(result==size) break;
+ //Wait for data in the queue
+ do {
+ rxWaiting=Thread::IRQgetCurrentThread();
+ Thread::IRQwait();
+ {
+ FastInterruptEnableLock eLock(dLock);
+ Thread::yield();
+ }
+ } while(rxWaiting);
+ }
+ return result;
+}
+
+ssize_t ATSAMSerial::writeBlock(const void *buffer, size_t size, off_t where)
+{
+ Lock l(txMutex);
+ const char *buf=reinterpret_cast(buffer);
+ for(size_t i=0;iUS_CSR & US_CSR_TXRDY) == 0) ;
+ port->US_THR =*buf++;
+ }
+ return size;
+}
+
+void ATSAMSerial::IRQwrite(const char *str)
+{
+ // We can reach here also with only kernel paused, so make sure
+ // interrupts are disabled.
+ bool interrupts=areInterruptsEnabled();
+ if(interrupts) fastDisableInterrupts();
+ while(*str)
+ {
+ while((port->US_CSR & US_CSR_TXRDY) == 0) ;
+ port->US_THR = *str++;
+ }
+ waitSerialTxFifoEmpty();
+ if(interrupts) fastEnableInterrupts();
+}
+
+int ATSAMSerial::ioctl(int cmd, void *arg)
+{
+ if(reinterpret_cast(arg) & 0b11) return -EFAULT; //Unaligned
+ termios *t=reinterpret_cast(arg);
+ switch(cmd)
+ {
+ case IOCTL_SYNC:
+ waitSerialTxFifoEmpty();
+ return 0;
+ case IOCTL_TCGETATTR:
+ t->c_iflag=IGNBRK | IGNPAR;
+ t->c_oflag=0;
+ t->c_cflag=CS8;
+ t->c_lflag=0;
+ return 0;
+ case IOCTL_TCSETATTR_NOW:
+ case IOCTL_TCSETATTR_DRAIN:
+ case IOCTL_TCSETATTR_FLUSH:
+ //Changing things at runtime unsupported, so do nothing, but don't
+ //return error as console_device.h implements some attribute changes
+ return 0;
+ default:
+ return -ENOTTY; //Means the operation does not apply to this descriptor
+ }
+}
+
+void ATSAMSerial::IRQhandleInterrupt()
+{
+ bool wake=false;
+ unsigned int status=port->US_CSR;
+
+ if(status & US_CSR_RXRDY)
+ {
+ wake=true;
+ //Always read the char (resets flags), but put it in the queue only if
+ //no framing error
+ char c=port->US_RHR;
+ if((status & US_CSR_FRAME) == 0)
+ {
+ if(rxQueue.tryPut(c & 0xff)==false) /*fifo overflow*/;
+ idle=false;
+ }
+ }
+ if(status & US_CSR_TIMEOUT)
+ {
+ wake=true;
+ port->US_CR=US_CR_STTTO;
+ idle=true;
+ }
+
+ if(wake && rxWaiting)
+ {
+ rxWaiting->IRQwakeup();
+ if(rxWaiting->IRQgetPriority()>
+ Thread::IRQgetCurrentThread()->IRQgetPriority())
+ Scheduler::IRQfindNextThread();
+ rxWaiting=0;
+ }
+}
+
+ATSAMSerial::~ATSAMSerial()
+{
+ waitSerialTxFifoEmpty();
+
+ port->US_CR = US_CR_TXDIS | US_CR_RXDIS;
+ port->US_IDR = US_IDR_RXRDY | US_IDR_TIMEOUT;
+
+ InterruptDisableLock dLock;
+ ports[portId]=nullptr;
+
+ //TODO: USART2 hardcoded
+ NVIC_DisableIRQ(USART2_IRQn);
+ NVIC_ClearPendingIRQ(USART2_IRQn);
+ PM->PM_UNLOCK=0xaa<<24 | PM_PBAMASK_OFFSET;
+ PM->PM_PBAMASK &= ~PM_PBAMASK_USART2;
+}
+
+void ATSAMSerial::waitSerialTxFifoEmpty()
+{
+ while((port->US_CSR & US_CSR_TXEMPTY) == 0) ;
+}
+
+} //namespace miosix
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/serial_atsam4l.h b/lib/miosix-kernel/miosix/arch/common/drivers/serial_atsam4l.h
new file mode 100644
index 00000000..620a7077
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/serial_atsam4l.h
@@ -0,0 +1,136 @@
+/***************************************************************************
+ * Copyright (C) 2020 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 *
+ ***************************************************************************/
+
+#pragma once
+
+#include "filesystem/console/console_device.h"
+#include "kernel/sync.h"
+#include "kernel/queue.h"
+#include "board_settings.h"
+
+namespace miosix {
+
+/**
+ * Serial port class for ATSAM4L microcontrollers.
+ *
+ * This is a simple implementation with the following limitations:
+ * -only supports USART2
+ * -no DMA, TX uses polling, while RX is interrupt-based
+ *
+ * Classes of this type are reference counted, must be allocated on the heap
+ * and managed through intrusive_ref_ptr
+ */
+class ATSAMSerial : public Device
+{
+public:
+ /**
+ * Constructor, initializes the serial port.
+ * Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
+ * attempting to construct multiple objects with the same id. That is,
+ * it is possible to instantiate only one instance of this class for each
+ * hardware USART.
+ * \param id a number to select the USART
+ * \param baudrate serial port baudrate
+ */
+ ATSAMSerial(int id, int baudrate);
+
+ /**
+ * Read a block of data
+ * \param buffer buffer where read data will be stored
+ * \param size buffer size
+ * \param where where to read from
+ * \return number of bytes read or a negative number on failure. Note that
+ * it is normal for this function to return less character than the amount
+ * asked
+ */
+ ssize_t readBlock(void *buffer, size_t size, off_t where);
+
+ /**
+ * Write a block of data
+ * \param buffer buffer where take data to write
+ * \param size buffer size
+ * \param where where to write to
+ * \return number of bytes written or a negative number on failure
+ */
+ ssize_t writeBlock(const void *buffer, size_t size, off_t where);
+
+ /**
+ * Write a string.
+ * An extension to the Device interface that adds a new member function,
+ * which is used by the kernel on console devices to write debug information
+ * before the kernel is started or in case of serious errors, right before
+ * rebooting.
+ * Can ONLY be called when the kernel is not yet started, paused or within
+ * an interrupt. This default implementation ignores writes.
+ * \param str the string to write. The string must be NUL terminated.
+ */
+ void IRQwrite(const char *str);
+
+ /**
+ * Performs device-specific operations
+ * \param cmd specifies the operation to perform
+ * \param arg optional argument that some operation require
+ * \return the exact return value depends on CMD, -1 is returned on error
+ */
+ int ioctl(int cmd, void *arg);
+
+ /**
+ * \internal the serial port interrupts call this member function.
+ * Never call this from user code.
+ */
+ void IRQhandleInterrupt();
+
+ /**
+ * \return port id, 0 for USART0, ...
+ */
+ int getId() const { return portId; }
+
+ /**
+ * Destructor
+ */
+ ~ATSAMSerial();
+
+private:
+ /**
+ * Wait until all characters have been written to the serial port
+ */
+ void waitSerialTxFifoEmpty();
+
+ FastMutex txMutex; ///< Mutex locked during transmission
+ FastMutex rxMutex; ///< Mutex locked during reception
+
+ DynUnsyncQueue rxQueue; ///< Receiving queue
+ static const unsigned int rxQueueMin=1; ///< Minimum queue size
+ Thread *rxWaiting; ///< Thread waiting for rx, or 0
+ bool idle; ///< Receiver idle
+
+ Usart *port; ///< Pointer to USART peripheral
+
+ const unsigned char portId; ///< 0 for USART0, ...
+};
+
+} //namespace miosix
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/serial_efm32.cpp b/lib/miosix-kernel/miosix/arch/common/drivers/serial_efm32.cpp
new file mode 100644
index 00000000..7ce27ba0
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/serial_efm32.cpp
@@ -0,0 +1,279 @@
+/***************************************************************************
+ * Copyright (C) 2015 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 *
+ ***************************************************************************/
+
+#include
+#include
+#include
+#include
+#include "serial_efm32.h"
+#include "kernel/sync.h"
+#include "kernel/scheduler/scheduler.h"
+#include "interfaces/portability.h"
+#include "interfaces/gpio.h"
+#include "filesystem/ioctl.h"
+
+using namespace std;
+using namespace miosix;
+
+static const int numPorts=1; //Supporting only USART0 for now
+
+//Hope GPIO mapping doesn't change among EFM32 microcontrollers, otherwise
+//we'll fix that with #ifdefs
+typedef Gpio u0tx;
+typedef Gpio u0rx;
+
+/// Pointer to serial port classes to let interrupts access the classes
+static EFM32Serial *ports[numPorts]={0};
+
+/**
+ * \internal interrupt routine for usart0 rx actual implementation
+ */
+void __attribute__((noinline)) usart0rxIrqImpl()
+{
+ if(ports[0]) ports[0]->IRQhandleInterrupt();
+}
+
+/**
+ * \internal interrupt routine for usart0 rx
+ */
+void __attribute__((naked)) USART0_RX_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15usart0rxIrqImplv");
+ restoreContext();
+}
+
+namespace miosix {
+
+//
+// class EFM32Serial
+//
+
+// A note on the baudrate/500: the buffer is selected so as to withstand
+// 20ms of full data rate. In the 8N1 format one char is made of 10 bits.
+// So (baudrate/10)*0.02=baudrate/500
+EFM32Serial::EFM32Serial(int id, int baudrate)
+ : Device(Device::TTY), rxQueue(rxQueueMin+baudrate/500), rxWaiting(0),
+ portId(id), baudrate(baudrate)
+{
+ if(id<0 || id>=numPorts || ports[id]!=0) errorHandler(UNEXPECTED);
+
+ {
+ InterruptDisableLock dLock;
+ ports[id]=this;
+
+ switch(id)
+ {
+ case 0:
+ u0tx::mode(Mode::OUTPUT_HIGH);
+ u0rx::mode(Mode::INPUT_PULL_UP_FILTER);
+
+ CMU->HFPERCLKEN0|=CMU_HFPERCLKEN0_USART0;
+ port=USART0;
+
+ port->IEN=USART_IEN_RXDATAV;
+ port->IRCTRL=0; //USART0 also has IrDA mode
+
+ NVIC_SetPriority(USART0_RX_IRQn,15);//Lowest priority for serial
+ NVIC_EnableIRQ(USART0_RX_IRQn);
+ break;
+ }
+ }
+
+ port->CTRL=USART_CTRL_TXBIL_HALFFULL; //Use the buffer more efficiently
+ port->FRAME=USART_FRAME_STOPBITS_ONE
+ | USART_FRAME_PARITY_NONE
+ | USART_FRAME_DATABITS_EIGHT;
+ port->TRIGCTRL=0;
+ port->INPUT=0;
+ port->I2SCTRL=0;
+ port->ROUTE=USART_ROUTE_LOCATION_LOC0 //Default location
+ | USART_ROUTE_TXPEN //Enable TX pin
+ | USART_ROUTE_RXPEN; //Enable RX pin
+ unsigned int periphClock=SystemHFClockGet()/(1<<(CMU->HFPERCLKDIV & 0xf));
+ //The number we need is periphClock/baudrate/16-1, but with two bits of
+ //fractional part. We divide by 2 instead of 16 to have 3 bit of fractional
+ //part. We use the additional fractional bit to add one to round towards
+ //the nearest. This gets us a little more precision. Then we subtract 8
+ //which is one with three fractional bits. Then we shift to fit the integer
+ //part in bits 20:8 and the fractional part in bits 7:6, masking away the
+ //third fractional bit. Easy, isn't it? Not quite.
+ port->CLKDIV=((((periphClock/baudrate/2)+1)-8)<<5) & 0x1fffc0;
+ port->CMD=USART_CMD_CLEARRX
+ | USART_CMD_CLEARTX
+ | USART_CMD_TXTRIDIS
+ | USART_CMD_RXBLOCKDIS
+ | USART_CMD_MASTEREN
+ | USART_CMD_TXEN
+ | USART_CMD_RXEN;
+}
+
+ssize_t EFM32Serial::readBlock(void *buffer, size_t size, off_t where)
+{
+ Lock l(rxMutex);
+ char *buf=reinterpret_cast(buffer);
+ size_t result=0;
+ FastInterruptDisableLock dLock;
+ for(;;)
+ {
+ //Try to get data from the queue
+ for(;result0) break;
+ //Wait for data in the queue
+ do {
+ rxWaiting=Thread::IRQgetCurrentThread();
+ Thread::IRQwait();
+ {
+ FastInterruptEnableLock eLock(dLock);
+ Thread::yield();
+ }
+ } while(rxWaiting);
+ }
+ return result;
+}
+
+ssize_t EFM32Serial::writeBlock(const void *buffer, size_t size, off_t where)
+{
+ Lock l(txMutex);
+ const char *buf=reinterpret_cast(buffer);
+ for(size_t i=0;iSTATUS & USART_STATUS_TXBL)==0) ;
+ port->TXDATA=*buf++;
+ }
+ return size;
+}
+
+void EFM32Serial::IRQwrite(const char *str)
+{
+ // We can reach here also with only kernel paused, so make sure
+ // interrupts are disabled.
+ bool interrupts=areInterruptsEnabled();
+ if(interrupts) fastDisableInterrupts();
+ while(*str)
+ {
+ while((port->STATUS & USART_STATUS_TXBL)==0) ;
+ port->TXDATA=*str++;
+ }
+ waitSerialTxFifoEmpty();
+ if(interrupts) fastEnableInterrupts();
+}
+
+int EFM32Serial::ioctl(int cmd, void* arg)
+{
+ if(reinterpret_cast(arg) & 0b11) return -EFAULT; //Unaligned
+ termios *t=reinterpret_cast(arg);
+ switch(cmd)
+ {
+ case IOCTL_SYNC:
+ waitSerialTxFifoEmpty();
+ return 0;
+ case IOCTL_TCGETATTR:
+ t->c_iflag=IGNBRK | IGNPAR;
+ t->c_oflag=0;
+ t->c_cflag=CS8;
+ t->c_lflag=0;
+ return 0;
+ case IOCTL_TCSETATTR_NOW:
+ case IOCTL_TCSETATTR_DRAIN:
+ case IOCTL_TCSETATTR_FLUSH:
+ //Changing things at runtime unsupported, so do nothing, but don't
+ //return error as console_device.h implements some attribute changes
+ return 0;
+ default:
+ return -ENOTTY; //Means the operation does not apply to this descriptor
+ }
+}
+
+void EFM32Serial::IRQhandleInterrupt()
+{
+ bool atLeastOne=false;
+ while(port->STATUS & USART_STATUS_RXDATAV)
+ {
+ unsigned int c=port->RXDATAX;
+ if((c & (USART_RXDATAX_FERR | USART_RXDATAX_PERR))==0)
+ {
+ atLeastOne=true;
+ if(rxQueue.tryPut(c & 0xff)==false) /*fifo overflow*/;
+ }
+ }
+ if(atLeastOne && rxWaiting)
+ {
+ rxWaiting->IRQwakeup();
+ if(rxWaiting->IRQgetPriority()>
+ Thread::IRQgetCurrentThread()->IRQgetPriority())
+ Scheduler::IRQfindNextThread();
+ rxWaiting=0;
+ }
+
+}
+
+EFM32Serial::~EFM32Serial()
+{
+ waitSerialTxFifoEmpty();
+
+ port->CMD=USART_CMD_TXDIS
+ | USART_CMD_RXDIS;
+ port->ROUTE=0;
+
+ InterruptDisableLock dLock;
+ ports[portId]=0;
+ switch(portId)
+ {
+ case 0:
+ NVIC_DisableIRQ(USART0_RX_IRQn);
+ NVIC_ClearPendingIRQ(USART0_RX_IRQn);
+
+ u0tx::mode(Mode::DISABLED);
+ u0rx::mode(Mode::DISABLED);
+
+ CMU->HFPERCLKEN0 &= ~CMU_HFPERCLKEN0_USART0;
+ break;
+ }
+}
+
+void EFM32Serial::waitSerialTxFifoEmpty()
+{
+ //The documentation states that the TXC bit goes to one as soon as a
+ //transmission is complete. However, this bit is initially zero, so if we
+ //call this function before transmitting, the loop will wait forever. As a
+ //solution, add a timeout having as value the time needed to send three
+ //bytes (the current one in the shift register plus the two in the buffer).
+ //The +1 is to produce rounding on the safe side, the 30 is the time to send
+ //three char through the port, including start and stop bits.
+ int timeout=(SystemCoreClock/baudrate+1)*30;
+ while(timeout-->0 && (port->STATUS & USART_STATUS_TXC)==0) ;
+}
+
+} //namespace miosix
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/serial_efm32.h b/lib/miosix-kernel/miosix/arch/common/drivers/serial_efm32.h
new file mode 100644
index 00000000..05733eaf
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/serial_efm32.h
@@ -0,0 +1,139 @@
+/***************************************************************************
+ * Copyright (C) 2015 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 *
+ ***************************************************************************/
+
+#ifndef SERIAL_EFM32_H
+#define SERIAL_EFM32_H
+
+#include "filesystem/console/console_device.h"
+#include "kernel/sync.h"
+#include "kernel/queue.h"
+#include "board_settings.h"
+
+namespace miosix {
+
+/**
+ * Serial port class for EFM32 microcontrollers.
+ *
+ * This is a simple implementation with the following limitations:
+ * -only supports USART0
+ * -no DMA, TX uses polling, while RX is interrupt-based
+ *
+ * Classes of this type are reference counted, must be allocated on the heap
+ * and managed through intrusive_ref_ptr
+ */
+class EFM32Serial : public Device
+{
+public:
+ /**
+ * Constructor, initializes the serial port.
+ * Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
+ * attempting to construct multiple objects with the same id. That is,
+ * it is possible to instantiate only one instance of this class for each
+ * hardware USART.
+ * \param id a number to select the USART
+ * \param baudrate serial port baudrate
+ */
+ EFM32Serial(int id, int baudrate);
+
+ /**
+ * Read a block of data
+ * \param buffer buffer where read data will be stored
+ * \param size buffer size
+ * \param where where to read from
+ * \return number of bytes read or a negative number on failure. Note that
+ * it is normal for this function to return less character than the amount
+ * asked
+ */
+ ssize_t readBlock(void *buffer, size_t size, off_t where);
+
+ /**
+ * Write a block of data
+ * \param buffer buffer where take data to write
+ * \param size buffer size
+ * \param where where to write to
+ * \return number of bytes written or a negative number on failure
+ */
+ ssize_t writeBlock(const void *buffer, size_t size, off_t where);
+
+ /**
+ * Write a string.
+ * An extension to the Device interface that adds a new member function,
+ * which is used by the kernel on console devices to write debug information
+ * before the kernel is started or in case of serious errors, right before
+ * rebooting.
+ * Can ONLY be called when the kernel is not yet started, paused or within
+ * an interrupt. This default implementation ignores writes.
+ * \param str the string to write. The string must be NUL terminated.
+ */
+ void IRQwrite(const char *str);
+
+ /**
+ * Performs device-specific operations
+ * \param cmd specifies the operation to perform
+ * \param arg optional argument that some operation require
+ * \return the exact return value depends on CMD, -1 is returned on error
+ */
+ int ioctl(int cmd, void *arg);
+
+ /**
+ * \internal the serial port interrupts call this member function.
+ * Never call this from user code.
+ */
+ void IRQhandleInterrupt();
+
+ /**
+ * \return port id, 0 for USART0, ...
+ */
+ int getId() const { return portId; }
+
+ /**
+ * Destructor
+ */
+ ~EFM32Serial();
+
+private:
+ /**
+ * Wait until all characters have been written to the serial port
+ */
+ void waitSerialTxFifoEmpty();
+
+ FastMutex txMutex; ///< Mutex locked during transmission
+ FastMutex rxMutex; ///< Mutex locked during reception
+
+ DynUnsyncQueue rxQueue; ///< Receiving queue
+ static const unsigned int rxQueueMin=1; ///< Minimum queue size
+ Thread *rxWaiting; ///< Thread waiting for rx, or 0
+
+ USART_TypeDef *port; ///< Pointer to USART peripheral
+
+ const unsigned char portId; ///< 0 for USART0, ...
+ int baudrate; ///< Baudrate
+};
+
+} //namespace miosix
+
+#endif //SERIAL_EFM32_H
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/serial_lpc2000.cpp b/lib/miosix-kernel/miosix/arch/common/drivers/serial_lpc2000.cpp
new file mode 100644
index 00000000..cb0f3732
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/serial_lpc2000.cpp
@@ -0,0 +1,304 @@
+/***************************************************************************
+ * Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 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 *
+ ***************************************************************************/
+
+#include
+#include
+#include
+#include "serial_lpc2000.h"
+#include "kernel/sync.h"
+#include "kernel/scheduler/scheduler.h"
+#include "interfaces/portability.h"
+#include "filesystem/ioctl.h"
+#include "LPC213x.h"
+
+namespace miosix {
+
+/// Pointer to serial port classes to let interrupts access the classes
+static LPC2000Serial *ports[2]={0};
+
+/**
+ * \internal interrupt routine for usart0 actual implementation
+ */
+void __attribute__((noinline)) usart0irqImpl()
+{
+ if(ports[0]) ports[0]->IRQhandleInterrupt();
+ VICVectAddr=0xff;//Restart VIC
+}
+
+/**
+ * \internal interrupt routine for usart0
+ */
+void __attribute__((interrupt("IRQ"),naked)) usart0irq()
+{
+ saveContextFromIrq();
+ asm volatile("bl _ZN6miosix13usart0irqImplEv");
+ restoreContext();
+}
+
+/**
+ * \internal interrupt routine for usart1 actual implementation
+ */
+void __attribute__((noinline)) usart1irqImpl()
+{
+ if(ports[1]) ports[1]->IRQhandleInterrupt();
+ VICVectAddr=0xff;//Restart VIC
+}
+
+/**
+ * \internal interrupt routine for usart1
+ */
+void __attribute__ ((interrupt("IRQ"),naked)) usart1irq()
+{
+ saveContextFromIrq();
+ asm volatile("bl _ZN6miosix13usart1irqImplEv");
+ restoreContext();
+}
+
+//
+// class LPC2000Serial
+//
+
+// A note on the baudrate/500: the buffer is selected so as to withstand
+// 20ms of full data rate. In the 8N1 format one char is made of 10 bits.
+// So (baudrate/10)*0.02=baudrate/500
+LPC2000Serial::LPC2000Serial(int id, int baudrate) : Device(Device::TTY),
+ rxQueue(hwRxQueueLen+baudrate/500), rxWaiting(0), idle(true)
+{
+ InterruptDisableLock dLock;
+ if(id<0 || id>1 || ports[id]!=0) errorHandler(UNEXPECTED);
+ ports[id]=this;
+ if(id==0)
+ {
+ serial=reinterpret_cast(0xe000c000);
+ PCONP|=(1<<3);//Enable UART0 peripheral
+ PINSEL0&=~(0xf);//Clear bits 0 to 3 of PINSEL0
+ PINSEL0|=0x5;//Set p0.0 as TXD and p0.1 as RXD
+ //Init VIC
+ VICSoftIntClr=(1<<6);//Clear uart0 interrupt flag (if previously set)
+ VICIntSelect&=~(1<<6);//uart0=IRQ
+ VICIntEnable=(1<<6);//uart0 interrupt ON
+ VICVectCntl2=0x20 | 0x6;//Slot 2 of VIC used by uart0
+ VICVectAddr2=reinterpret_cast(&usart0irq);
+ } else {
+ serial=reinterpret_cast(0xe0010000);
+ PCONP|=(1<<4);//Enable UART1 peripheral
+ PINSEL0&=~(0xf0000);//Clear bits 16 to 19 of PINSEL0
+ PINSEL0|=0x50000;//Set p0.8 as TXD and p0.9 as RXD
+ //Init VIC
+ VICSoftIntClr=(1<<7);//Clear uart1 interrupt flag (if previously set)
+ VICIntSelect&=~(1<<7);//uart1=IRQ
+ VICIntEnable=(1<<7);//uart1 interrupt ON
+ VICVectCntl3=0x20 | 0x7;//Slot 3 of VIC used by uart1
+ VICVectAddr3=reinterpret_cast(&usart1irq);
+ }
+ serial->LCR=0x3;//DLAB disabled
+ //0x07= fifo enabled, reset tx and rx hardware fifos
+ //0x80= uart rx fifo trigger level 8 characters
+ serial->FCR=0x07 | 0x80;
+ serial->LCR=0x83;//8data bit, 1stop, no parity, DLAB enabled
+ int div=TIMER_CLOCK/16/baudrate;
+ serial->DLL=div & 0xff;
+ serial->DLM=(div>>8) & 0xff;
+ serial->LCR=0x3;//DLAB disabled
+ serial->IER=0x7;//Enable RLS, RDA, CTI and THRE interrupts
+}
+
+ssize_t LPC2000Serial::readBlock(void *buffer, size_t size, off_t where)
+{
+ Lock l(rxMutex);
+ char *buf=reinterpret_cast(buffer);
+ size_t result=0;
+ FastInterruptDisableLock dLock;
+ for(;;)
+ {
+ //Try to get data from the queue
+ for(;result0) break;
+ if(result==size) break;
+ //Wait for data in the queue
+ do {
+ rxWaiting=Thread::IRQgetCurrentThread();
+ Thread::IRQwait();
+ {
+ FastInterruptEnableLock eLock(dLock);
+ Thread::yield();
+ }
+ } while(rxWaiting);
+ }
+ return result;
+}
+
+ssize_t LPC2000Serial::writeBlock(const void *buffer, size_t size, off_t where)
+{
+ Lock l(txMutex);
+ FastInterruptDisableLock dLock;
+ size_t len=size;
+ const char *buf=reinterpret_cast(buffer);
+ while(len>0)
+ {
+ //If no data in software and hardware queue
+ if((serial->LSR & (1<<5)) && (txQueue.isEmpty()))
+ {
+ //Fill hardware queue first
+ for(int i=0;iTHR=*buf++;
+ len--;
+ if(len==0) break;
+ }
+ } else {
+ if(txQueue.IRQput(*buf)==true)
+ {
+ buf++;
+ len--;
+ } else {
+ FastInterruptEnableLock eLock(dLock);
+ txQueue.waitUntilNotFull();
+ }
+ }
+ }
+ return size;
+}
+
+void LPC2000Serial::IRQwrite(const char *str)
+{
+ while((*str)!='\0')
+ {
+ //Wait until the hardware fifo is ready to accept one char
+ while(!(serial->LSR & (1<<5))) ; //wait
+ serial->THR=*str++;
+ }
+ waitSerialTxFifoEmpty();
+}
+
+int LPC2000Serial::ioctl(int cmd, void* arg)
+{
+ if(reinterpret_cast(arg) & 0b11) return -EFAULT; //Unaligned
+ termios *t=reinterpret_cast(arg);
+ switch(cmd)
+ {
+ case IOCTL_SYNC:
+ waitSerialTxFifoEmpty();
+ return 0;
+ case IOCTL_TCGETATTR:
+ t->c_iflag=IGNBRK | IGNPAR;
+ t->c_oflag=0;
+ t->c_cflag=CS8;
+ t->c_lflag=0;
+ return 0;
+ case IOCTL_TCSETATTR_NOW:
+ case IOCTL_TCSETATTR_DRAIN:
+ case IOCTL_TCSETATTR_FLUSH:
+ //Changing things at runtime unsupported, so do nothing, but don't
+ //return error as console_device.h implements some attribute changes
+ return 0;
+ default:
+ return -ENOTTY; //Means the operation does not apply to this descriptor
+ }
+}
+
+void LPC2000Serial::IRQhandleInterrupt()
+{
+ char c;
+ bool hppw=false;
+ bool wakeup=false;
+ switch(serial->IIR & 0xf)
+ {
+ case 0x6: //RLS
+ c=serial->LSR;//Read LSR to clear interrupt
+ c=serial->RBR;//Read RBR to discard char that caused error
+ break;
+ case 0x4: //RDA
+ //Note: read one less char than HARDWARE_RX_QUEUE_LENGTH as the
+ //CTI interrupt only occurs if there's at least one character in
+ //the buffer, and we always want the CTI interrupt
+ for(int i=0;iRBR)==false) /*fifo overflow*/;
+ wakeup=true;
+ idle=false;
+ break;
+ case 0xc: //CTI
+ while(serial->LSR & (1<<0))
+ if(rxQueue.tryPut(serial->RBR)==false) /*fifo overflow*/;
+ wakeup=true;
+ idle=true;
+ break;
+ case 0x2: //THRE
+ for(int i=0;iTHR=c;
+ }
+ break;
+ }
+ if(wakeup && rxWaiting)
+ {
+ rxWaiting->IRQwakeup();
+ if(rxWaiting->IRQgetPriority()>
+ Thread::IRQgetCurrentThread()->IRQgetPriority()) hppw=true;
+ rxWaiting=0;
+ }
+ if(hppw) Scheduler::IRQfindNextThread();
+}
+
+LPC2000Serial::~LPC2000Serial()
+{
+ waitSerialTxFifoEmpty();
+
+ InterruptDisableLock dLock;
+ //Disable UART0
+ serial->LCR=0;//DLAB disabled
+ serial->FCR=0;
+
+ int id=0;
+ if(ports[0]==this) id=0;
+ else if(ports[1]==this) id=1;
+ else errorHandler(UNEXPECTED);
+ ports[id]=0;
+
+ if(id==0)
+ {
+ //Disable VIC
+ VICIntEnClr=(1<<6);
+ //Disable PIN
+ PINSEL0&=~(0xf);//Clear bits 0 to 3 of PINSEL0
+ } else {
+ //Disable VIC
+ VICIntEnClr=(1<<7);
+ //Disable PIN
+ PINSEL0&=~(0xf0000);//Clear bits 16 to 19 of PINSEL0
+ }
+}
+
+} //namespace miosix
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/serial_lpc2000.h b/lib/miosix-kernel/miosix/arch/common/drivers/serial_lpc2000.h
new file mode 100644
index 00000000..67a41cf0
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/serial_lpc2000.h
@@ -0,0 +1,184 @@
+/***************************************************************************
+ * Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 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 *
+ ***************************************************************************/
+
+#ifndef SERIAL_LPC2000_H
+#define SERIAL_LPC2000_H
+
+#include "filesystem/console/console_device.h"
+#include "kernel/sync.h"
+#include "kernel/queue.h"
+#include "interfaces/delays.h"
+
+namespace miosix {
+
+/**
+ * Serial port class for LPC2000 microcontrollers.
+ *
+ * Classes of this type are reference counted, must be allocated on the heap
+ * and managed through intrusive_ref_ptr
+ */
+class LPC2000Serial : public Device
+{
+public:
+ /**
+ * Constructor, initializes the serial port.
+ * Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
+ * attempting to construct multiple objects with the same id. That is,
+ * it is possible to instantiate only one instance of this class for each
+ * hardware USART.
+ * \param id 0=USART0, 1=USART1
+ * \param baudrate serial port baudrate.
+ */
+ LPC2000Serial(int id, int baudrate);
+
+ /**
+ * Read a block of data
+ * \param buffer buffer where read data will be stored
+ * \param size buffer size
+ * \param where where to read from
+ * \return number of bytes read or a negative number on failure
+ */
+ ssize_t readBlock(void *buffer, size_t size, off_t where);
+
+ /**
+ * Write a block of data
+ * \param buffer buffer where take data to write
+ * \param size buffer size
+ * \param where where to write to
+ * \return number of bytes written or a negative number on failure
+ */
+ ssize_t writeBlock(const void *buffer, size_t size, off_t where);
+
+ /**
+ * Write a string.
+ * An extension to the Device interface that adds a new member function,
+ * which is used by the kernel on console devices to write debug information
+ * before the kernel is started or in case of serious errors, right before
+ * rebooting.
+ * Can ONLY be called when the kernel is not yet started, paused or within
+ * an interrupt. This default implementation ignores writes.
+ * \param str the string to write. The string must be NUL terminated.
+ */
+ void IRQwrite(const char *str);
+
+ /**
+ * Performs device-specific operations
+ * \param cmd specifies the operation to perform
+ * \param arg optional argument that some operation require
+ * \return the exact return value depends on CMD, -1 is returned on error
+ */
+ int ioctl(int cmd, void *arg);
+
+ /**
+ * \internal the serial port interrupts call this member function.
+ * Never call this from user code.
+ */
+ void IRQhandleInterrupt();
+
+ /**
+ * Destructor
+ */
+ ~LPC2000Serial();
+
+private:
+ /**
+ * Wait until all characters have been written to the serial port
+ */
+ void waitSerialTxFifoEmpty()
+ {
+ while((serial->LSR & (1<<6))==0) ;
+
+ //This delay has been added to fix a quirk on the Miosix board. When
+ //writing a message to the console and rebooting, if the reboot happens
+ //too fast with respect to the last character sent out of the serial
+ //port, the FT232 gets confused and the last charcters are lost,
+ //probably from the FT232 buffer. Using delayMs() to be callable from IRQ
+ delayMs(2);
+ }
+
+ /**
+ * The registers of the USART in struct form, to make a generic driver
+ */
+ struct Usart16550
+ {
+ //Offset 0x00
+ union {
+ volatile unsigned char RBR;
+ volatile unsigned char THR;
+ volatile unsigned char DLL;
+ };
+ char padding0[3];
+ //Offset 0x04
+ union {
+ volatile unsigned char IER;
+ volatile unsigned char DLM;
+ };
+ char padding1[3];
+ //Offset 0x08
+ union {
+ volatile unsigned char IIR;
+ volatile unsigned char FCR;
+ };
+ char padding2[3];
+ //Offset 0x0c
+ volatile unsigned char LCR;
+ char padding3[3];
+ //Offset 0x10
+ volatile unsigned char MCR; //Only USART1 has this
+ char padding4[3];
+ //Offset 0x14
+ volatile unsigned char LSR;
+ char padding5[7];
+ //Offset 0x1c
+ volatile unsigned char SCR;
+ char padding6[19];
+ //Offset 0x30
+ volatile unsigned char TER;
+ };
+
+ //Configure the software queue here
+ static const int swTxQueue=32;///< Size of tx software queue
+
+ //The hardware queues cannot be modified, since their length is hardware-specific
+ static const int hwTxQueueLen=16;
+ static const int hwRxQueueLen=8;
+
+ FastMutex txMutex;///< Mutex used to guard the tx queue
+ FastMutex rxMutex;///< Mutex used to guard the rx queue
+
+ Queue txQueue;///< Tx software queue
+ DynUnsyncQueue rxQueue;///< Rx software queue
+ Thread *rxWaiting; ///< Thread waiting on rx queue
+ bool idle; ///< Receiver idle
+
+ Usart16550 *serial; ///< Serial port registers
+};
+
+} //namespace miosix
+
+#endif //SERIAL_LPC2000_H
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/serial_stm32.cpp b/lib/miosix-kernel/miosix/arch/common/drivers/serial_stm32.cpp
new file mode 100644
index 00000000..28b791c3
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/serial_stm32.cpp
@@ -0,0 +1,1246 @@
+/***************************************************************************
+ * Copyright (C) 2010-2018 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 *
+ ***************************************************************************/
+
+#include
+#include
+#include
+#include "serial_stm32.h"
+#include "kernel/sync.h"
+#include "kernel/scheduler/scheduler.h"
+#include "interfaces/portability.h"
+#include "filesystem/ioctl.h"
+#include "core/cache_cortexMx.h"
+
+using namespace std;
+using namespace miosix;
+
+static const int numPorts=3; //Supporting only USART1, USART2, USART3
+
+//A nice feature of the stm32 is that the USART are connected to the same
+//GPIOS in all families, stm32f1, f2, f4 and l1. Additionally, USART1 is
+//always connected to the APB2, while USART2 and USART3 are always on APB1
+//Unfortunately, this does not hold with DMA.
+typedef Gpio u1tx;
+typedef Gpio u1rx;
+typedef Gpio u1cts;
+typedef Gpio u1rts;
+
+typedef Gpio u2tx;
+typedef Gpio u2rx;
+typedef Gpio u2cts;
+typedef Gpio u2rts;
+
+typedef Gpio u3tx;
+typedef Gpio u3rx;
+typedef Gpio u3cts;
+typedef Gpio u3rts;
+
+/// Pointer to serial port classes to let interrupts access the classes
+static STM32Serial *ports[numPorts]={0};
+
+/**
+ * \internal interrupt routine for usart1 actual implementation
+ */
+void __attribute__((noinline)) usart1irqImpl()
+{
+ if(ports[0]) ports[0]->IRQhandleInterrupt();
+}
+
+/**
+ * \internal interrupt routine for usart1
+ */
+void __attribute__((naked)) USART1_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z13usart1irqImplv");
+ restoreContext();
+}
+
+#if !defined(STM32_NO_SERIAL_2_3)
+
+/**
+ * \internal interrupt routine for usart2 actual implementation
+ */
+void __attribute__((noinline)) usart2irqImpl()
+{
+ if(ports[1]) ports[1]->IRQhandleInterrupt();
+}
+
+/**
+ * \internal interrupt routine for usart2
+ */
+void __attribute__((naked)) USART2_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z13usart2irqImplv");
+ restoreContext();
+}
+
+#if !defined(STM32F411xE) && !defined(STM32F401xE) && !defined(STM32F401xC)
+/**
+ * \internal interrupt routine for usart3 actual implementation
+ */
+void __attribute__((noinline)) usart3irqImpl()
+{
+ if(ports[2]) ports[2]->IRQhandleInterrupt();
+}
+
+/**
+ * \internal interrupt routine for usart3
+ */
+#if !defined(STM32F072xB)
+void __attribute__((naked)) USART3_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z13usart3irqImplv");
+ restoreContext();
+}
+#else //!defined(STM32F072xB)
+void __attribute__((naked)) USART3_4_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z13usart3irqImplv");
+ restoreContext();
+}
+#endif //!defined(STM32F072xB)
+#endif //!defined(STM32F411xE) && !defined(STM32F401xE) && !defined(STM32F401xC)
+#endif //!defined(STM32_NO_SERIAL_2_3)
+
+#ifdef SERIAL_1_DMA
+
+/**
+ * \internal USART1 DMA tx actual implementation
+ */
+void __attribute__((noinline)) usart1txDmaImpl()
+{
+ #if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \
+ || defined(_ARCH_CORTEXM4_STM32L4)
+ DMA1->IFCR=DMA_IFCR_CGIF4;
+ DMA1_Channel4->CCR=0; //Disable DMA
+ #else //stm32f2 and f4
+ DMA2->HIFCR=DMA_HIFCR_CTCIF7
+ | DMA_HIFCR_CTEIF7
+ | DMA_HIFCR_CDMEIF7
+ | DMA_HIFCR_CFEIF7;
+ #endif
+ if(ports[0]) ports[0]->IRQhandleDMAtx();
+}
+
+/**
+ * \internal USART1 DMA rx actual implementation
+ */
+void __attribute__((noinline)) usart1rxDmaImpl()
+{
+ if(ports[0]) ports[0]->IRQhandleDMArx();
+}
+
+#if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \
+ || defined(_ARCH_CORTEXM4_STM32L4)
+/**
+ * \internal DMA1 Channel 4 IRQ (configured as USART1 TX)
+ */
+void __attribute__((naked)) DMA1_Channel4_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15usart1txDmaImplv");
+ restoreContext();
+}
+
+/**
+ * \internal DMA1 Channel 5 IRQ (configured as USART1 RX)
+ */
+void __attribute__((naked)) DMA1_Channel5_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15usart1rxDmaImplv");
+ restoreContext();
+}
+
+#else //stm32f2 and stm32f4
+
+/**
+ * \internal DMA2 stream 7 IRQ (configured as USART1 TX)
+ */
+void __attribute__((naked)) DMA2_Stream7_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15usart1txDmaImplv");
+ restoreContext();
+}
+
+/**
+ * \internal DMA2 stream 5 IRQ (configured as USART1 RX)
+ */
+void __attribute__((naked)) DMA2_Stream5_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15usart1rxDmaImplv");
+ restoreContext();
+}
+#endif
+#endif //SERIAL_1_DMA
+
+#if defined(SERIAL_2_DMA) && !defined(STM32_NO_SERIAL_2_3)
+
+/**
+ * \internal USART2 DMA tx actual implementation
+ */
+void __attribute__((noinline)) usart2txDmaImpl()
+{
+ #if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \
+ || defined(_ARCH_CORTEXM4_STM32L4)
+ DMA1->IFCR=DMA_IFCR_CGIF7;
+ DMA1_Channel7->CCR=0; //Disable DMA
+ #else //stm32f2 and f4
+ DMA1->HIFCR=DMA_HIFCR_CTCIF6
+ | DMA_HIFCR_CTEIF6
+ | DMA_HIFCR_CDMEIF6
+ | DMA_HIFCR_CFEIF6;
+ #endif
+ if(ports[1]) ports[1]->IRQhandleDMAtx();
+}
+
+/**
+ * \internal USART2 DMA rx actual implementation
+ */
+void __attribute__((noinline)) usart2rxDmaImpl()
+{
+ if(ports[1]) ports[1]->IRQhandleDMArx();
+}
+
+#if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \
+ || defined(_ARCH_CORTEXM4_STM32L4)
+/**
+ * \internal DMA1 Channel 7 IRQ (configured as USART2 TX)
+ */
+void __attribute__((naked)) DMA1_Channel7_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15usart2txDmaImplv");
+ restoreContext();
+}
+
+/**
+ * \internal DMA1 Channel 6 IRQ (configured as USART2 RX)
+ */
+void __attribute__((naked)) DMA1_Channel6_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15usart2rxDmaImplv");
+ restoreContext();
+}
+
+#else //stm32f2 and stm32f4
+
+/**
+ * \internal DMA1 stream 6 IRQ (configured as USART2 TX)
+ */
+void __attribute__((naked)) DMA1_Stream6_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15usart2txDmaImplv");
+ restoreContext();
+}
+
+/**
+ * \internal DMA1 stream 5 IRQ (configured as USART2 RX)
+ */
+void __attribute__((naked)) DMA1_Stream5_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15usart2rxDmaImplv");
+ restoreContext();
+}
+#endif
+#endif //SERIAL_2_DMA
+
+#if defined(SERIAL_3_DMA) && !defined(STM32_NO_SERIAL_2_3)
+
+/**
+ * \internal USART3 DMA tx actual implementation
+ */
+void __attribute__((noinline)) usart3txDmaImpl()
+{
+ #if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \
+ || defined(_ARCH_CORTEXM4_STM32L4)
+ DMA1->IFCR=DMA_IFCR_CGIF2;
+ DMA1_Channel2->CCR=0; //Disable DMA
+ #else //stm32f2 and f4
+ DMA1->LIFCR=DMA_LIFCR_CTCIF3
+ | DMA_LIFCR_CTEIF3
+ | DMA_LIFCR_CDMEIF3
+ | DMA_LIFCR_CFEIF3;
+ #endif
+ if(ports[2]) ports[2]->IRQhandleDMAtx();
+}
+
+/**
+ * \internal USART3 DMA rx actual implementation
+ */
+void __attribute__((noinline)) usart3rxDmaImpl()
+{
+ if(ports[2]) ports[2]->IRQhandleDMArx();
+}
+
+#if defined(_ARCH_CORTEXM3_STM32) || defined (_ARCH_CORTEXM4_STM32F3) \
+ || defined(_ARCH_CORTEXM4_STM32L4)
+/**
+ * \internal DMA1 Channel 2 IRQ (configured as USART3 TX)
+ */
+void __attribute__((naked)) DMA1_Channel2_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15usart3txDmaImplv");
+ restoreContext();
+}
+
+/**
+ * \internal DMA1 Channel 3 IRQ (configured as USART3 RX)
+ */
+void __attribute__((naked)) DMA1_Channel3_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15usart3rxDmaImplv");
+ restoreContext();
+}
+
+#else //stm32f2 and stm32f4
+
+/**
+ * \internal DMA1 stream 3 IRQ (configured as USART3 TX)
+ */
+void __attribute__((naked)) DMA1_Stream3_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15usart3txDmaImplv");
+ restoreContext();
+}
+
+/**
+ * \internal DMA1 stream 1 IRQ (configured as USART3 RX)
+ */
+void __attribute__((naked)) DMA1_Stream1_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15usart3rxDmaImplv");
+ restoreContext();
+}
+#endif
+#endif //SERIAL_3_DMA
+
+namespace miosix {
+
+#ifdef SERIAL_DMA
+#if defined(_ARCH_CORTEXM4_STM32F4) || defined(_ARCH_CORTEXM4_STM32F3) \
+ || defined(_ARCH_CORTEXM4_STM32L4)
+/**
+ * The STM3F3, STM32F4 and STM32L4 have an ugly quirk of having 64KB RAM area
+ * called CCM that can only be accessed by the processor and not be the DMA.
+ * \param x pointer to check
+ * \return true if the pointer is inside the CCM, and thus it isn't possible
+ * to use it for DMA transfers
+ */
+static bool isInCCMarea(const void *x)
+{
+ unsigned int ptr=reinterpret_cast(x);
+ return (ptr>=0x10000000) && (ptr<(0x10000000+64*1024));
+}
+#else //_ARCH_CORTEXM4_STM32F4 and _ARCH_CORTEXM4_STM32F3
+static inline bool isInCCMarea(const void *x) { return false; }
+#endif // _ARCH_CORTEXM4_STM32F4 and _ARCH_CORTEXM4_STM32F3
+#endif //SERIAL_DMA
+
+//
+// class STM32Serial
+//
+
+// A note on the baudrate/500: the buffer is selected so as to withstand
+// 20ms of full data rate. In the 8N1 format one char is made of 10 bits.
+// So (baudrate/10)*0.02=baudrate/500
+STM32Serial::STM32Serial(int id, int baudrate, FlowCtrl flowControl)
+ : Device(Device::TTY), rxQueue(rxQueueMin+baudrate/500),
+ flowControl(flowControl==RTSCTS), portId(id)
+{
+ #if !defined(_ARCH_CORTEXM3_STM32)
+ //stm32f2, f4, l4, l1, f7, h7 require alternate function mapping
+ //stm32f0 family has different alternate function mapping
+ //with respect to the other families
+ switch(id)
+ {
+ case 1:
+ #if !defined(_ARCH_CORTEXM0_STM32)
+ u1tx::alternateFunction(7);
+ u1rx::alternateFunction(7);
+ if(flowControl)
+ {
+ u1rts::alternateFunction(7);
+ u1cts::alternateFunction(7);
+ }
+ #else //!defined(_ARCH_CORTEXM0_STM32)
+ u1tx::alternateFunction(1);
+ u1rx::alternateFunction(1);
+ if(flowControl)
+ {
+ u1rts::alternateFunction(1);
+ u1cts::alternateFunction(1);
+ }
+ #endif //!defined(_ARCH_CORTEXM0_STM32)
+ break;
+ case 2:
+ #if !defined(_ARCH_CORTEXM0_STM32)
+ u2tx::alternateFunction(7);
+ u2rx::alternateFunction(7);
+ if(flowControl)
+ {
+ u2rts::alternateFunction(7);
+ u2cts::alternateFunction(7);
+ }
+ #else //!defined(_ARCH_CORTEXM0_STM32)
+ u2tx::alternateFunction(1);
+ u2rx::alternateFunction(1);
+ if(flowControl)
+ {
+ u2rts::alternateFunction(1);
+ u2cts::alternateFunction(1);
+ }
+ #endif //!defined(_ARCH_CORTEXM0_STM32)
+ break;
+ case 3:
+ #if !defined(_ARCH_CORTEXM0_STM32)
+ u3tx::alternateFunction(7);
+ u3rx::alternateFunction(7);
+ if(flowControl)
+ {
+ u3rts::alternateFunction(7);
+ u3cts::alternateFunction(7);
+ }
+ #else //!defined(_ARCH_CORTEXM0_STM32)
+ u3tx::alternateFunction(4);
+ u3rx::alternateFunction(4);
+ if(flowControl)
+ {
+ u3rts::alternateFunction(4);
+ u3cts::alternateFunction(4);
+ }
+ #endif //!defined(_ARCH_CORTEXM0_STM32)
+ break;
+ }
+ #endif //_ARCH_CORTEXM3_STM32
+
+ switch(id)
+ {
+ case 1:
+ commonInit(id,baudrate,u1tx::getPin(),u1rx::getPin(),
+ u1rts::getPin(),u1cts::getPin());
+ break;
+ case 2:
+ commonInit(id,baudrate,u2tx::getPin(),u2rx::getPin(),
+ u2rts::getPin(),u2cts::getPin());
+ break;
+ case 3:
+ commonInit(id,baudrate,u3tx::getPin(),u3rx::getPin(),
+ u3rts::getPin(),u3cts::getPin());
+ break;
+ }
+}
+
+STM32Serial::STM32Serial(int id, int baudrate, GpioPin tx, GpioPin rx)
+ : Device(Device::TTY), rxQueue(rxQueueMin+baudrate/500),
+ flowControl(false), portId(id)
+{
+ commonInit(id,baudrate,tx,rx,tx,rx); //The last two args will be ignored
+}
+
+STM32Serial::STM32Serial(int id, int baudrate, GpioPin tx, GpioPin rx,
+ miosix::GpioPin rts, miosix::GpioPin cts)
+ : Device(Device::TTY), rxQueue(rxQueueMin+baudrate/500),
+ flowControl(true), portId(id)
+{
+ commonInit(id,baudrate,tx,rx,rts,cts);
+}
+
+void STM32Serial::commonInit(int id, int baudrate, GpioPin tx, GpioPin rx,
+ GpioPin rts, GpioPin cts)
+{
+ #ifdef SERIAL_DMA
+ dmaTx=0;
+ dmaRx=0;
+ txWaiting=0;
+ dmaTxInProgress=false;
+ #endif //SERIAL_DMA
+ InterruptDisableLock dLock;
+ if(id<1|| id>numPorts || ports[id-1]!=0) errorHandler(UNEXPECTED);
+ ports[id-1]=this;
+ unsigned int freq=SystemCoreClock;
+ //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
+ //defined in order to avoid "unused variable" warning
+ #if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM3_STM32L1) \
+ || defined(_ARCH_CORTEXM4_STM32F3) || defined(_ARCH_CORTEXM4_STM32L4)
+ const unsigned int ppre1=8;
+ const unsigned int ppre2=11;
+ #elif !defined(_ARCH_CORTEXM7_STM32H7) && !defined(_ARCH_CORTEXM0_STM32)
+ const unsigned int ppre1=10;
+ const unsigned int ppre2=13;
+ #endif
+ switch(id)
+ {
+ case 1:
+ port=USART1;
+ RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
+ RCC_SYNC();
+ #ifdef SERIAL_1_DMA
+ #if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
+ || defined(_ARCH_CORTEXM4_STM32L4)
+ #ifdef _ARCH_CORTEXM4_STM32L4
+ RCC->AHB1ENR |= RCC_AHBENR_DMA1EN;
+ DMA1_CSELR->CSELR |= (2 << DMA_CSELR_C4S_Pos) // Assign DMA1_CH4 to USART1_TX
+ | (2 << DMA_CSELR_C5S_Pos);// Assign DMA1_CH5 to USART1_RX
+ #else
+ RCC->AHBENR |= RCC_AHBENR_DMA1EN;
+ #endif
+ RCC_SYNC();
+ NVIC_SetPriority(DMA1_Channel4_IRQn,15);//Lowest priority for serial
+ NVIC_EnableIRQ(DMA1_Channel4_IRQn);
+ dmaTx=DMA1_Channel4;
+ //Higher priority to ensure IRQhandleDMArx() is called before
+ //IRQhandleInterrupt(), so that idle is set correctly
+ NVIC_SetPriority(DMA1_Channel5_IRQn,14);
+ NVIC_EnableIRQ(DMA1_Channel5_IRQn);
+ dmaRx=DMA1_Channel5;
+ #else //stm32f2, stm32f4
+ RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
+ RCC_SYNC();
+ NVIC_SetPriority(DMA2_Stream7_IRQn,15);//Lowest priority for serial
+ NVIC_EnableIRQ(DMA2_Stream7_IRQn);
+ dmaTx=DMA2_Stream7;
+ //Higher priority to ensure IRQhandleDMArx() is called before
+ //IRQhandleInterrupt(), so that idle is set correctly
+ NVIC_SetPriority(DMA2_Stream5_IRQn,14);
+ NVIC_EnableIRQ(DMA2_Stream5_IRQn);
+ dmaRx=DMA2_Stream5;
+ #endif
+ port->CR3=USART_CR3_DMAT | USART_CR3_DMAR;
+ #endif //SERIAL_1_DMA
+ NVIC_SetPriority(USART1_IRQn,15);//Lowest priority for serial
+ NVIC_EnableIRQ(USART1_IRQn);
+ #if !defined(_ARCH_CORTEXM7_STM32H7) && !defined(_ARCH_CORTEXM0_STM32)
+ if(RCC->CFGR & RCC_CFGR_PPRE2_2) freq/=1<<(((RCC->CFGR>>ppre2) & 0x3)+1);
+ #elif defined(_ARCH_CORTEXM0_STM32)
+ // STM32F0 family has only PPRE2 register
+ if(RCC->CFGR & RCC_CFGR_PPRE_2) freq/=1<<(((RCC->CFGR>>8) & 0x3)+1);
+ #else
+ //rcc_hclk3 = SystemCoreClock / HPRE
+ //rcc_pclk2 = rcc_hclk1 / D2PPRE2
+ //NOTE: are rcc_hclk3 and rcc_hclk1 the same signal?
+ //usart1 clock is rcc_pclk2
+ if(RCC->D1CFGR & RCC_D1CFGR_HPRE_3)
+ freq/=1<<(((RCC->D1CFGR>>RCC_D1CFGR_HPRE_Pos) & 0x7)+1);
+ if(RCC->D2CFGR & RCC_D2CFGR_D2PPRE2_2)
+ freq/=1<<(((RCC->D2CFGR>>RCC_D2CFGR_D2PPRE2_Pos) & 0x3)+1);
+ #endif //_ARCH_CORTEXM7_STM32H7
+ break;
+
+ #if !defined(STM32_NO_SERIAL_2_3)
+ case 2:
+ port=USART2;
+ #ifndef _ARCH_CORTEXM7_STM32H7
+ #ifndef _ARCH_CORTEXM4_STM32L4
+ RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
+ #else //_ARCH_CORTEXM4_STM32L4
+ RCC->APB1ENR1 |= RCC_APB1ENR1_USART2EN;
+ #endif //_ARCH_CORTEXM4_STM32L4
+ #else //_ARCH_CORTEXM7_STM32H7
+ RCC->APB1LENR |= RCC_APB1LENR_USART2EN;
+ #endif //_ARCH_CORTEXM7_STM32H7
+ RCC_SYNC();
+ #ifdef SERIAL_2_DMA
+ #if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
+ || defined(_ARCH_CORTEXM4_STM32L4)
+ #ifdef _ARCH_CORTEXM4_STM32L4
+ RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
+ DMA1_CSELR->CSELR |= (2 << DMA_CSELR_C7S_Pos) // Assign DMA1_CH7 to USART2_TX
+ | (2 << DMA_CSELR_C6S_Pos);// Assign DMA1_CH6 to USART2_RX
+ #else
+ RCC->AHBENR |= RCC_AHBENR_DMA1EN;
+ #endif
+ RCC_SYNC();
+ NVIC_SetPriority(DMA1_Channel7_IRQn,15);//Lowest priority for serial
+ NVIC_EnableIRQ(DMA1_Channel7_IRQn);
+ dmaTx=DMA1_Channel7;
+ //Higher priority to ensure IRQhandleDMArx() is called before
+ //IRQhandleInterrupt(), so that idle is set correctly
+ NVIC_SetPriority(DMA1_Channel6_IRQn,14);
+ NVIC_EnableIRQ(DMA1_Channel6_IRQn);
+ dmaRx=DMA1_Channel6;
+ #else //stm32f2, stm32f4
+ RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
+ RCC_SYNC();
+ NVIC_SetPriority(DMA1_Stream6_IRQn,15);//Lowest priority for serial
+ NVIC_EnableIRQ(DMA1_Stream6_IRQn);
+ dmaTx=DMA1_Stream6;
+ //Higher priority to ensure IRQhandleDMArx() is called before
+ //IRQhandleInterrupt(), so that idle is set correctly
+ NVIC_SetPriority(DMA1_Stream5_IRQn,14);
+ NVIC_EnableIRQ(DMA1_Stream5_IRQn);
+ dmaRx=DMA1_Stream5;
+ #endif
+ port->CR3=USART_CR3_DMAT | USART_CR3_DMAR;
+ #endif //SERIAL_2_DMA
+ NVIC_SetPriority(USART2_IRQn,15);//Lowest priority for serial
+ NVIC_EnableIRQ(USART2_IRQn);
+ #if !defined(_ARCH_CORTEXM7_STM32H7) && !defined(_ARCH_CORTEXM0_STM32)
+ if(RCC->CFGR & RCC_CFGR_PPRE1_2) freq/=1<<(((RCC->CFGR>>ppre1) & 0x3)+1);
+ #elif defined(_ARCH_CORTEXM0_STM32)
+ // STM32F0 family has only PPRE2 register
+ if(RCC->CFGR & RCC_CFGR_PPRE_2) freq/=1<<(((RCC->CFGR>>8) & 0x3)+1);
+ #else //_ARCH_CORTEXM7_STM32H7
+ //rcc_hclk3 = SystemCoreClock / HPRE
+ //rcc_pclk1 = rcc_hclk1 / D2PPRE1
+ //NOTE: are rcc_hclk3 and rcc_hclk1 the same signal?
+ //usart2 clock is rcc_pclk1
+ if(RCC->D1CFGR & RCC_D1CFGR_HPRE_3)
+ freq/=1<<(((RCC->D1CFGR>>RCC_D1CFGR_HPRE_Pos) & 0x7)+1);
+ if(RCC->D2CFGR & RCC_D2CFGR_D2PPRE1_2)
+ freq/=1<<(((RCC->D2CFGR>>RCC_D2CFGR_D2PPRE1_Pos) & 0x3)+1);
+ #endif //_ARCH_CORTEXM7_STM32H7
+ break;
+ #if !defined(STM32F411xE) && !defined(STM32F401xE) && !defined(STM32F401xC)
+ case 3:
+ port=USART3;
+ #ifndef _ARCH_CORTEXM7_STM32H7
+ #ifndef _ARCH_CORTEXM4_STM32L4
+ RCC->APB1ENR |= RCC_APB1ENR_USART3EN;
+ #else //_ARCH_CORTEXM4_STM32L4
+ RCC->APB1ENR1 |= RCC_APB1ENR1_USART3EN;
+ #endif //_ARCH_CORTEXM4_STM32L4
+ #else
+ RCC->APB1LENR |= RCC_APB1LENR_USART3EN;
+ #endif //_ARCH_CORTEXM7_STM32H7
+ RCC_SYNC();
+ #ifdef SERIAL_3_DMA
+ #if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
+ || defined(_ARCH_CORTEXM4_STM32L4)
+ #ifdef _ARCH_CORTEXM4_STM32L4
+ RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
+ DMA1_CSELR->CSELR |= (2 << DMA_CSELR_C2S_Pos) // Assign DMA1_CH2 to USART2_TX
+ | (2 << DMA_CSELR_C3S_Pos);// Assign DMA1_CH3 to USART2_RX
+ #else
+ RCC->AHBENR |= RCC_AHBENR_DMA1EN;
+ #endif
+ RCC_SYNC();
+ NVIC_SetPriority(DMA1_Channel2_IRQn,15);//Lowest priority for serial
+ NVIC_EnableIRQ(DMA1_Channel2_IRQn);
+ dmaTx=DMA1_Channel2;
+ //Higher priority to ensure IRQhandleDMArx() is called before
+ //IRQhandleInterrupt(), so that idle is set correctly
+ NVIC_SetPriority(DMA1_Channel3_IRQn,14);
+ NVIC_EnableIRQ(DMA1_Channel3_IRQn);
+ dmaRx=DMA1_Channel3;
+ #else //stm32f2, stm32f4
+ RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
+ RCC_SYNC();
+ NVIC_SetPriority(DMA1_Stream3_IRQn,15);//Lowest priority for serial
+ NVIC_EnableIRQ(DMA1_Stream3_IRQn);
+ dmaTx=DMA1_Stream3;
+ //Higher priority to ensure IRQhandleDMArx() is called before
+ //IRQhandleInterrupt(), so that idle is set correctly
+ NVIC_SetPriority(DMA1_Stream1_IRQn,14);
+ NVIC_EnableIRQ(DMA1_Stream1_IRQn);
+ dmaRx=DMA1_Stream1;
+ #endif
+ port->CR3=USART_CR3_DMAT | USART_CR3_DMAR;
+ #endif //SERIAL_3_DMA
+ #if !defined(STM32F072xB)
+ NVIC_SetPriority(USART3_IRQn,15);//Lowest priority for serial
+ NVIC_EnableIRQ(USART3_IRQn);
+ #else //STM32F072xB
+ NVIC_SetPriority(USART3_4_IRQn,15);
+ NVIC_EnableIRQ(USART3_4_IRQn);
+ #endif //STM32F072xB
+ #if !defined(_ARCH_CORTEXM7_STM32H7) && !defined(_ARCH_CORTEXM0_STM32)
+ if(RCC->CFGR & RCC_CFGR_PPRE1_2) freq/=1<<(((RCC->CFGR>>ppre1) & 0x3)+1);
+ #elif defined(_ARCH_CORTEXM0_STM32)
+ // STM32F0 family has only PPRE2 register
+ if(RCC->CFGR & RCC_CFGR_PPRE_2) freq/=1<<(((RCC->CFGR>>8) & 0x3)+1);
+ #else //_ARCH_CORTEXM7_STM32H7
+ //rcc_hclk3 = SystemCoreClock / HPRE
+ //rcc_pclk1 = rcc_hclk1 / D2PPRE1
+ //NOTE: are rcc_hclk3 and rcc_hclk1 the same signal?
+ //usart2 clock is rcc_pclk1
+ if(RCC->D1CFGR & RCC_D1CFGR_HPRE_3)
+ freq/=1<<(((RCC->D1CFGR>>RCC_D1CFGR_HPRE_Pos) & 0x7)+1);
+ if(RCC->D2CFGR & RCC_D2CFGR_D2PPRE1_2)
+ freq/=1<<(((RCC->D2CFGR>>RCC_D2CFGR_D2PPRE1_Pos) & 0x3)+1);
+ #endif //_ARCH_CORTEXM7_STM32H7
+ break;
+ #endif //!defined(STM32F411xE) && !defined(STM32F401xE) && !defined(STM32F401xC)
+ #endif //!defined(STM32_NO_SERIAL_2_3)
+ }
+ //Quirk: stm32f1 rx pin has to be in input mode, while stm32f2 and up want
+ //it in ALTERNATE mode. Go figure...
+ #ifdef _ARCH_CORTEXM3_STM32
+ Mode::Mode_ rxPinMode=Mode::INPUT;
+ #else //_ARCH_CORTEXM3_STM32
+ Mode::Mode_ rxPinMode=Mode::ALTERNATE;
+ #endif //_ARCH_CORTEXM3_STM32
+ tx.mode(Mode::ALTERNATE);
+ rx.mode(rxPinMode);
+ if(flowControl)
+ {
+ rts.mode(Mode::ALTERNATE);
+ cts.mode(rxPinMode);
+ }
+ const unsigned int quot=2*freq/baudrate; //2*freq for round to nearest
+ port->BRR=quot/2 + (quot & 1); //Round to nearest
+ if(flowControl==false) port->CR3 |= USART_CR3_ONEBIT;
+ else port->CR3 |= USART_CR3_ONEBIT | USART_CR3_RTSE | USART_CR3_CTSE;
+ //Enabled, 8 data bit, no parity, interrupt on character rx
+ #ifdef SERIAL_DMA
+ if(dmaTx)
+ {
+ port->CR1 = USART_CR1_UE //Enable port
+ | USART_CR1_IDLEIE //Interrupt on idle line
+ | USART_CR1_TE //Transmission enbled
+ | USART_CR1_RE; //Reception enabled
+ IRQdmaReadStart();
+ return;
+ }
+ #endif //SERIAL_DMA
+ port->CR1 = USART_CR1_UE //Enable port
+ | USART_CR1_RXNEIE //Interrupt on data received
+ | USART_CR1_IDLEIE //Interrupt on idle line
+ | USART_CR1_TE //Transmission enbled
+ | USART_CR1_RE; //Reception enabled
+}
+
+ssize_t STM32Serial::readBlock(void *buffer, size_t size, off_t where)
+{
+ Lock l(rxMutex);
+ char *buf=reinterpret_cast(buffer);
+ size_t result=0;
+ FastInterruptDisableLock dLock;
+ for(;;)
+ {
+ //Try to get data from the queue
+ for(;result0) break;
+ if(result==size) break;
+ //Wait for data in the queue
+ do {
+ rxWaiting=Thread::IRQgetCurrentThread();
+ Thread::IRQwait();
+ {
+ FastInterruptEnableLock eLock(dLock);
+ Thread::yield();
+ }
+ } while(rxWaiting);
+ }
+ return result;
+}
+
+ssize_t STM32Serial::writeBlock(const void *buffer, size_t size, off_t where)
+{
+ Lock l(txMutex);
+ const char *buf=reinterpret_cast(buffer);
+ #ifdef SERIAL_DMA
+ if(dmaTx)
+ {
+ size_t remaining=size;
+ if(isInCCMarea(buf)==false)
+ {
+ //Use zero copy for all but the last txBufferSize bytes, if possible
+ while(remaining>txBufferSize)
+ {
+ //DMA is limited to 64K
+ size_t transferSize=min(remaining-txBufferSize,65535);
+ waitDmaTxCompletion();
+ writeDma(buf,transferSize);
+ buf+=transferSize;
+ remaining-=transferSize;
+ }
+ }
+ while(remaining>0)
+ {
+ size_t transferSize=min(remaining,static_cast(txBufferSize));
+ waitDmaTxCompletion();
+ //Copy to txBuffer only after DMA xfer completed, as the previous
+ //xfer may be using the same buffer
+ memcpy(txBuffer,buf,transferSize);
+ writeDma(txBuffer,transferSize);
+ buf+=transferSize;
+ remaining-=transferSize;
+ }
+ return size;
+ }
+ #endif //SERIAL_DMA
+ for(size_t i=0;iSR & USART_SR_TXE)==0) ;
+ port->DR=*buf++;
+ #else //_ARCH_CORTEXM7_STM32F7/H7
+ while((port->ISR & USART_ISR_TXE)==0) ;
+ port->TDR=*buf++;
+ #endif //_ARCH_CORTEXM7_STM32F7/H7
+ }
+ return size;
+}
+
+void STM32Serial::IRQwrite(const char *str)
+{
+ // We can reach here also with only kernel paused, so make sure
+ // interrupts are disabled. This is important for the DMA case
+ bool interrupts=areInterruptsEnabled();
+ if(interrupts) fastDisableInterrupts();
+ #ifdef SERIAL_DMA
+ if(dmaTx)
+ {
+ #if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
+ || defined(_ARCH_CORTEXM4_STM32L4)
+ //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
+ static const unsigned int irqMask[]=
+ {
+ (DMA_ISR_TCIF4 | DMA_ISR_TEIF4),
+ (DMA_ISR_TCIF7 | DMA_ISR_TEIF7),
+ (DMA_ISR_TCIF2 | DMA_ISR_TEIF2)
+ };
+ #if defined(_ARCH_CORTEXM4_STM32F3) || defined(_ARCH_CORTEXM4_STM32L4)
+ // Workaround for ST messing up with flag definitions...
+ constexpr unsigned int DMA_CCR4_EN = DMA_CCR_EN;
+ #endif
+ while((dmaTx->CCR & DMA_CCR4_EN) && !(DMA1->ISR & irqMask[getId()-1])) ;
+ #else //_ARCH_CORTEXM3_STM32
+ //Wait until DMA xfer ends. EN bit is cleared by hardware on transfer end
+ while(dmaTx->CR & DMA_SxCR_EN) ;
+ #endif //_ARCH_CORTEXM3_STM32
+ }
+ #endif //SERIAL_DMA
+ while(*str)
+ {
+ #if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
+ && !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \
+ && !defined(_ARCH_CORTEXM4_STM32L4)
+ while((port->SR & USART_SR_TXE)==0) ;
+ port->DR=*str++;
+ #else //_ARCH_CORTEXM7_STM32F7/H7
+ while((port->ISR & USART_ISR_TXE)==0) ;
+ port->TDR=*str++;
+ #endif //_ARCH_CORTEXM7_STM32F7/H7
+ }
+ waitSerialTxFifoEmpty();
+ if(interrupts) fastEnableInterrupts();
+}
+
+int STM32Serial::ioctl(int cmd, void* arg)
+{
+ if(reinterpret_cast(arg) & 0b11) return -EFAULT; //Unaligned
+ termios *t=reinterpret_cast(arg);
+ switch(cmd)
+ {
+ case IOCTL_SYNC:
+ waitSerialTxFifoEmpty();
+ return 0;
+ case IOCTL_TCGETATTR:
+ t->c_iflag=IGNBRK | IGNPAR;
+ t->c_oflag=0;
+ t->c_cflag=CS8 | (flowControl ? CRTSCTS : 0);
+ t->c_lflag=0;
+ return 0;
+ case IOCTL_TCSETATTR_NOW:
+ case IOCTL_TCSETATTR_DRAIN:
+ case IOCTL_TCSETATTR_FLUSH:
+ //Changing things at runtime unsupported, so do nothing, but don't
+ //return error as console_device.h implements some attribute changes
+ return 0;
+ default:
+ return -ENOTTY; //Means the operation does not apply to this descriptor
+ }
+}
+
+void STM32Serial::IRQhandleInterrupt()
+{
+ #if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
+ && !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \
+ && !defined(_ARCH_CORTEXM4_STM32L4)
+ unsigned int status=port->SR;
+ #else //_ARCH_CORTEXM7_STM32F7/H7
+ unsigned int status=port->ISR;
+ constexpr unsigned int USART_SR_RXNE=USART_ISR_RXNE;
+ constexpr unsigned int USART_SR_IDLE=USART_ISR_IDLE;
+ constexpr unsigned int USART_SR_FE =USART_ISR_FE;
+ #endif //_ARCH_CORTEXM7_STM32F7/H7
+ char c;
+ #ifdef SERIAL_DMA
+ if(dmaRx==0 && (status & USART_SR_RXNE))
+ #else //SERIAL_DMA
+ if(status & USART_SR_RXNE)
+ #endif //SERIAL_DMA
+ {
+ //Always read data, since this clears interrupt flags
+ #if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
+ && !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \
+ && !defined(_ARCH_CORTEXM4_STM32L4)
+ c=port->DR;
+ #else //_ARCH_CORTEXM7_STM32F7/H7
+ c=port->RDR;
+ #endif //_ARCH_CORTEXM7_STM32F7/H7
+ //If no error put data in buffer
+ if((status & USART_SR_FE)==0)
+ if(rxQueue.tryPut(c)==false) /*fifo overflow*/;
+ idle=false;
+ }
+ if(status & USART_SR_IDLE)
+ {
+ #if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
+ && !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \
+ && !defined(_ARCH_CORTEXM4_STM32L4)
+ c=port->DR; //clears interrupt flags
+ #else //_ARCH_CORTEXM7_STM32F7/H7
+ port->ICR=USART_ICR_IDLECF; //clears interrupt flags
+ #endif //_ARCH_CORTEXM7_STM32F7/H7
+ #ifdef SERIAL_DMA
+ if(dmaRx) IRQreadDma();
+ #endif //SERIAL_DMA
+ idle=true;
+ }
+ if((status & USART_SR_IDLE) || rxQueue.size()>=rxQueueMin)
+ {
+ //Enough data in buffer or idle line, awake thread
+ if(rxWaiting)
+ {
+ rxWaiting->IRQwakeup();
+ if(rxWaiting->IRQgetPriority()>
+ Thread::IRQgetCurrentThread()->IRQgetPriority())
+ Scheduler::IRQfindNextThread();
+ rxWaiting=0;
+ }
+ }
+}
+
+#ifdef SERIAL_DMA
+void STM32Serial::IRQhandleDMAtx()
+{
+ dmaTxInProgress=false;
+ if(txWaiting==0) return;
+ txWaiting->IRQwakeup();
+ if(txWaiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+ Scheduler::IRQfindNextThread();
+ txWaiting=0;
+}
+
+void STM32Serial::IRQhandleDMArx()
+{
+ IRQreadDma();
+ idle=false;
+ if(rxWaiting==0) return;
+ rxWaiting->IRQwakeup();
+ if(rxWaiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+ Scheduler::IRQfindNextThread();
+ rxWaiting=0;
+}
+#endif //SERIAL_DMA
+
+STM32Serial::~STM32Serial()
+{
+ waitSerialTxFifoEmpty();
+ {
+ InterruptDisableLock dLock;
+ port->CR1=0;
+ int id=getId();
+ ports[id-1]=0;
+ switch(id)
+ {
+ case 1:
+ #ifdef SERIAL_1_DMA
+ IRQdmaReadStop();
+ #if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
+ || defined(_ARCH_CORTEXM4_STM32L4)
+ NVIC_DisableIRQ(DMA1_Channel4_IRQn);
+ NVIC_ClearPendingIRQ(DMA1_Channel4_IRQn);
+ NVIC_DisableIRQ(DMA1_Channel5_IRQn);
+ NVIC_ClearPendingIRQ(DMA1_Channel5_IRQn);
+ #else //stm32f2, stm32f4
+ NVIC_DisableIRQ(DMA2_Stream7_IRQn);
+ NVIC_ClearPendingIRQ(DMA2_Stream7_IRQn);
+ NVIC_DisableIRQ(DMA2_Stream5_IRQn);
+ NVIC_ClearPendingIRQ(DMA2_Stream5_IRQn);
+ #endif
+ #endif //SERIAL_1_DMA
+ NVIC_DisableIRQ(USART1_IRQn);
+ NVIC_ClearPendingIRQ(USART1_IRQn);
+ RCC->APB2ENR &= ~RCC_APB2ENR_USART1EN;
+ break;
+
+ #if !defined(STM32_NO_SERIAL_2_3)
+ case 2:
+ #ifdef SERIAL_2_DMA
+ IRQdmaReadStop();
+ #if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
+ || defined(_ARCH_CORTEXM4_STM32L4)
+ NVIC_DisableIRQ(DMA1_Channel7_IRQn);
+ NVIC_ClearPendingIRQ(DMA1_Channel7_IRQn);
+ NVIC_DisableIRQ(DMA1_Channel6_IRQn);
+ NVIC_ClearPendingIRQ(DMA1_Channel6_IRQn);
+ #else //stm32f2, stm32f4
+ NVIC_DisableIRQ(DMA1_Stream6_IRQn);
+ NVIC_ClearPendingIRQ(DMA1_Stream6_IRQn);
+ NVIC_DisableIRQ(DMA1_Stream5_IRQn);
+ NVIC_ClearPendingIRQ(DMA1_Stream5_IRQn);
+ #endif
+ #endif //SERIAL_2_DMA
+ NVIC_DisableIRQ(USART2_IRQn);
+ NVIC_ClearPendingIRQ(USART2_IRQn);
+ #ifndef _ARCH_CORTEXM7_STM32H7
+ #ifdef _ARCH_CORTEXM4_STM32L4
+ RCC->APB1ENR1 &= ~RCC_APB1ENR1_USART2EN;
+ #else
+ RCC->APB1ENR &= ~RCC_APB1ENR_USART2EN;
+ #endif
+ #else //_ARCH_CORTEXM7_STM32H7
+ RCC->APB1LENR &= ~RCC_APB1LENR_USART2EN;
+ #endif //_ARCH_CORTEXM7_STM32H7
+ break;
+ #if !defined(STM32F411xE) && !defined(STM32F401xE) && !defined(STM32F401xC)
+ case 3:
+ #ifdef SERIAL_3_DMA
+ IRQdmaReadStop();
+ #if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
+ || defined(_ARCH_CORTEXM4_STM32L4)
+ NVIC_DisableIRQ(DMA1_Channel2_IRQn);
+ NVIC_ClearPendingIRQ(DMA1_Channel2_IRQn);
+ NVIC_DisableIRQ(DMA1_Channel3_IRQn);
+ NVIC_ClearPendingIRQ(DMA1_Channel3_IRQn);
+ #else //stm32f2, stm32f4
+ NVIC_DisableIRQ(DMA1_Stream3_IRQn);
+ NVIC_ClearPendingIRQ(DMA1_Stream3_IRQn);
+ NVIC_DisableIRQ(DMA1_Stream1_IRQn);
+ NVIC_ClearPendingIRQ(DMA1_Stream1_IRQn);
+ #endif
+ #endif //SERIAL_3_DMA
+ #if !defined(STM32F072xB)
+ NVIC_SetPriority(USART3_IRQn,15);//Lowest priority for serial
+ NVIC_EnableIRQ(USART3_IRQn);
+ #else //STM32F072xB
+ NVIC_SetPriority(USART3_4_IRQn,15);
+ NVIC_EnableIRQ(USART3_4_IRQn);
+ #endif //STM32F072xB
+ #ifndef _ARCH_CORTEXM7_STM32H7
+ #ifdef _ARCH_CORTEXM4_STM32L4
+ RCC->APB1ENR1 &= ~RCC_APB1ENR1_USART3EN;
+ #else
+ RCC->APB1ENR &= ~RCC_APB1ENR_USART3EN;
+ #endif
+ #else //_ARCH_CORTEXM7_STM32H7
+ RCC->APB1LENR &= ~RCC_APB1LENR_USART3EN;
+ #endif //_ARCH_CORTEXM7_STM32H7
+ break;
+ #endif //!defined(STM32F411xE) && !defined(STM32F401xE) && !defined(STM32F401xC)
+ #endif //!defined(STM32_NO_SERIAL_2_3)
+ }
+ }
+}
+
+#ifdef SERIAL_DMA
+void STM32Serial::waitDmaTxCompletion()
+{
+ FastInterruptDisableLock dLock;
+ // If a previous DMA xfer is in progress, wait
+ if(dmaTxInProgress)
+ {
+ txWaiting=Thread::IRQgetCurrentThread();
+ do {
+ Thread::IRQwait();
+ {
+ FastInterruptEnableLock eLock(dLock);
+ Thread::yield();
+ }
+ } while(txWaiting);
+ }
+}
+
+void STM32Serial::writeDma(const char *buffer, size_t size)
+{
+ markBufferBeforeDmaWrite(buffer,size);
+ //Quirk: DMA messes up the TC bit, and causes waitSerialTxFifoEmpty() to
+ //return prematurely, causing characters to be missed when rebooting
+ //immediatley a write. You can just clear the bit manually, but doing that
+ //is dangerous, as if you clear the bit but for any reason the serial
+ //write doesn't start (think an invalid buffer, or another thread crashing),
+ //then TC will never be set and waitSerialTxFifoEmpty() deadlocks!
+ //The only way to clear it safely is to first read SR and then write to
+ //DR (thus the bit is cleared at the same time a transmission is started,
+ //and the race condition is eliminated). This is the purpose of this
+ //instruction, it reads SR. When we start the DMA, the DMA controller
+ //writes to DR and completes the TC clear sequence.
+ #if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
+ && !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \
+ && !defined(_ARCH_CORTEXM4_STM32L4)
+ while((port->SR & USART_SR_TXE)==0) ;
+ #else //_ARCH_CORTEXM7_STM32F7/H7
+ while((port->ISR & USART_ISR_TXE)==0) ;
+ #endif //_ARCH_CORTEXM7_STM32F7/H7
+
+ dmaTxInProgress=true;
+ #if defined(_ARCH_CORTEXM3_STM32)
+ dmaTx->CPAR=reinterpret_cast(&port->DR);
+ dmaTx->CMAR=reinterpret_cast(buffer);
+ dmaTx->CNDTR=size;
+ dmaTx->CCR=DMA_CCR4_MINC //Increment RAM pointer
+ | DMA_CCR4_DIR //Memory to peripheral
+ | DMA_CCR4_TEIE //Interrupt on transfer error
+ | DMA_CCR4_TCIE //Interrupt on transfer complete
+ | DMA_CCR4_EN; //Start DMA
+ #else
+ #if defined(_ARCH_CORTEXM4_STM32F3) || defined(_ARCH_CORTEXM4_STM32L4)
+ dmaTx->CPAR=reinterpret_cast(&port->TDR);
+ dmaTx->CMAR=reinterpret_cast(buffer);
+ dmaTx->CNDTR=size;
+ dmaTx->CCR=DMA_CCR_MINC //Increment RAM pointer
+ | DMA_CCR_DIR //Memory to peripheral
+ | DMA_CCR_TEIE //Interrupt on transfer error
+ | DMA_CCR_TCIE //Interrupt on transfer complete
+ | DMA_CCR_EN; //Start DMA
+ #else //_ARCH_CORTEXM4_STM32F3
+ #if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
+ && !defined(_ARCH_CORTEXM0_STM32)
+ dmaTx->PAR=reinterpret_cast(&port->DR);
+ #else //_ARCH_CORTEXM7_STM32F7/H7
+ dmaTx->PAR=reinterpret_cast(&port->TDR);
+ #endif //_ARCH_CORTEXM7_STM32F7/H7
+ dmaTx->M0AR=reinterpret_cast(buffer);
+ dmaTx->NDTR=size;
+ //Quirk: not enabling DMA_SxFCR_FEIE because the USART seems to
+ //generate a spurious fifo error. The code was tested and the
+ //transfer completes successfully even in the presence of this fifo
+ //error
+ dmaTx->FCR=DMA_SxFCR_DMDIS;//Enable fifo
+ dmaTx->CR=DMA_SxCR_CHSEL_2 //Select channel 4 (USART_TX)
+ | DMA_SxCR_MINC //Increment RAM pointer
+ | DMA_SxCR_DIR_0 //Memory to peripheral
+ | DMA_SxCR_TCIE //Interrupt on completion
+ | DMA_SxCR_TEIE //Interrupt on transfer error
+ | DMA_SxCR_DMEIE //Interrupt on direct mode error
+ | DMA_SxCR_EN; //Start the DMA
+ #endif //_ARCH_CORTEXM4_STM32F3
+ #endif //_ARCH_CORTEXM3_STM32
+}
+
+void STM32Serial::IRQreadDma()
+{
+ int elem=IRQdmaReadStop();
+ markBufferAfterDmaRead(rxBuffer,rxQueueMin);
+ for(int i=0;iCPAR=reinterpret_cast(&port->DR);
+ dmaRx->CMAR=reinterpret_cast(rxBuffer);
+ dmaRx->CNDTR=rxQueueMin;
+ dmaRx->CCR=DMA_CCR4_MINC //Increment RAM pointer
+ | 0 //Peripheral to memory
+ | DMA_CCR4_TEIE //Interrupt on transfer error
+ | DMA_CCR4_TCIE //Interrupt on transfer complete
+ | DMA_CCR4_EN; //Start DMA
+ #else
+ #if defined(_ARCH_CORTEXM4_STM32F3) || defined(_ARCH_CORTEXM4_STM32L4)
+ dmaRx->CPAR=reinterpret_cast(&port->RDR);
+ dmaRx->CMAR=reinterpret_cast(rxBuffer);
+ dmaRx->CNDTR=rxQueueMin;
+ dmaRx->CCR=DMA_CCR_MINC //Increment RAM pointer
+ | 0 //Peripheral to memory
+ | DMA_CCR_TEIE //Interrupt on transfer error
+ | DMA_CCR_TCIE //Interrupt on transfer complete
+ | DMA_CCR_EN; //Start DMA
+ #else //_ARCH_CORTEXM4_STM32F3
+ #if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
+ && !defined(_ARCH_CORTEXM0_STM32)
+ dmaRx->PAR=reinterpret_cast(&port->DR);
+ #else //_ARCH_CORTEXM7_STM32F7/H7
+ dmaRx->PAR=reinterpret_cast(&port->RDR);
+ #endif //_ARCH_CORTEXM7_STM32F7/H7
+ dmaRx->M0AR=reinterpret_cast(rxBuffer);
+ dmaRx->NDTR=rxQueueMin;
+ dmaRx->CR=DMA_SxCR_CHSEL_2 //Select channel 4 (USART_RX)
+ | DMA_SxCR_MINC //Increment RAM pointer
+ | 0 //Peripheral to memory
+ | DMA_SxCR_HTIE //Interrupt on half transfer
+ | DMA_SxCR_TEIE //Interrupt on transfer error
+ | DMA_SxCR_DMEIE //Interrupt on direct mode error
+ | DMA_SxCR_EN; //Start the DMA
+ #endif //_ARCH_CORTEXM4_STM32F3
+ #endif //_ARCH_CORTEXM3_STM32
+}
+
+int STM32Serial::IRQdmaReadStop()
+{
+ #if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
+ || defined(_ARCH_CORTEXM4_STM32L4)
+ dmaRx->CCR=0;
+ static const unsigned int irqMask[]=
+ {
+ DMA_IFCR_CGIF5,
+ DMA_IFCR_CGIF6,
+ DMA_IFCR_CGIF3
+ };
+ DMA1->IFCR=irqMask[getId()-1];
+ return rxQueueMin-dmaRx->CNDTR;
+ #else //_ARCH_CORTEXM3_STM32
+ //Stop DMA and wait for it to actually stop
+ dmaRx->CR &= ~DMA_SxCR_EN;
+ while(dmaRx->CR & DMA_SxCR_EN) ;
+ static const unsigned int irqMask[]=
+ {
+ (DMA_HIFCR_CTCIF5 | DMA_HIFCR_CHTIF5 | DMA_HIFCR_CTEIF5 | DMA_HIFCR_CDMEIF5 | DMA_HIFCR_CFEIF5),
+ (DMA_HIFCR_CTCIF5 | DMA_HIFCR_CHTIF5 | DMA_HIFCR_CTEIF5 | DMA_HIFCR_CDMEIF5 | DMA_HIFCR_CFEIF5),
+ (DMA_LIFCR_CTCIF1 | DMA_LIFCR_CHTIF1 | DMA_LIFCR_CTEIF1 | DMA_LIFCR_CDMEIF1 | DMA_LIFCR_CFEIF1)
+ };
+ static volatile unsigned long * const irqRegs[]=
+ {
+ &DMA2->HIFCR,
+ &DMA1->HIFCR,
+ &DMA1->LIFCR
+ };
+ *irqRegs[getId()-1]=irqMask[getId()-1];
+ return rxQueueMin-dmaRx->NDTR;
+ #endif //_ARCH_CORTEXM3_STM32
+}
+#endif //SERIAL_DMA
+
+} //namespace miosix
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/serial_stm32.h b/lib/miosix-kernel/miosix/arch/common/drivers/serial_stm32.h
new file mode 100644
index 00000000..95ad8d04
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/serial_stm32.h
@@ -0,0 +1,295 @@
+/***************************************************************************
+ * Copyright (C) 2010-2018 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 *
+ ***************************************************************************/
+
+#ifndef SERIAL_STM32_H
+#define SERIAL_STM32_H
+
+#include "filesystem/console/console_device.h"
+#include "kernel/sync.h"
+#include "kernel/queue.h"
+#include "interfaces/gpio.h"
+#include "board_settings.h"
+
+#if defined(_ARCH_CORTEXM3_STM32) && defined(__ENABLE_XRAM)
+//Quirk: concurrent access to the FSMC from both core and DMA is broken in
+//the stm32f1, so disable DMA mode if XRAM is enabled.
+#undef SERIAL_1_DMA
+#undef SERIAL_2_DMA
+#undef SERIAL_3_DMA
+#endif
+
+#if defined(SERIAL_1_DMA) || defined(SERIAL_2_DMA) || defined(SERIAL_3_DMA)
+#define SERIAL_DMA
+#endif
+
+#if defined(SERIAL_DMA) && defined(_ARCH_CORTEXM0_STM32)
+#undef SERIAL_1_DMA
+#undef SERIAL_2_DMA
+#undef SERIAL_3_DMA
+#undef SERIAL_DMA
+#warning "DMA not yet implemented for STM32F0 family"
+#endif
+
+namespace miosix {
+
+/**
+ * Serial port class for stm32 microcontrollers.
+ * Only supports USART1, USART2 and USART3
+ * Additionally, USARTx can use DMA if SERIAL_x_DMA is defined in
+ * board_settings.h, while the other serial use polling for transmission,
+ * and interrupt for reception.
+ *
+ * Classes of this type are reference counted, must be allocated on the heap
+ * and managed through intrusive_ref_ptr
+ */
+class STM32Serial : public Device
+{
+public:
+ enum FlowCtrl
+ {
+ NOFLOWCTRL, ///< No hardware flow control
+ RTSCTS ///< RTS/CTS hardware flow control
+ };
+
+ /**
+ * Constructor, initializes the serial port using the default pins, which
+ * are:
+ * USART1: tx=PA9 rx=PA10 cts=PA11 rts=PA12
+ * USART2: tx=PA2 rx=PA3 cts=PA0 rts=PA1
+ * USART3: tx=PB10 rx=PB11 cts=PB13 rts=PB14
+ * If you board has a different mapping, use one of the other constructors.
+ *
+ * Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
+ * attempting to construct multiple objects with the same id. That is,
+ * it is possible to instantiate only one instance of this class for each
+ * hardware USART.
+ * \param id a number 1 to 3 to select which USART
+ * \param baudrate serial port baudrate
+ * \param flowControl to enable hardware flow control on this port
+ */
+ STM32Serial(int id, int baudrate, FlowCtrl flowControl=NOFLOWCTRL);
+
+ /**
+ * Constructor, initializes the serial port using remapped pins and disables
+ * flow control.
+ *
+ * NOTE: for stm32f2, f4, f7 and h7 you have to set the correct alternate
+ * function to the pins in order to connect then to the USART peripheral
+ * before passing them to this class.
+ *
+ * Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
+ * attempting to construct multiple objects with the same id. That is,
+ * it is possible to instantiate only one instance of this class for each
+ * hardware USART.
+ * \param id a number 1 to 3 to select which USART
+ * \param baudrate serial port baudrate
+ * \param tx tx pin
+ * \param rx rx pin
+ */
+ STM32Serial(int id, int baudrate, miosix::GpioPin tx, miosix::GpioPin rx);
+
+ /**
+ * Constructor, initializes the serial port using remapped pins and enables
+ * flow control.
+ *
+ * NOTE: for stm32f2, f4, f7 and h7 you have to set the correct alternate
+ * function to the pins in order to connect then to the USART peripheral
+ * before passing them to this class.
+ *
+ * Calls errorHandler(UNEXPECTED) if id is not in the correct range, or when
+ * attempting to construct multiple objects with the same id. That is,
+ * it is possible to instantiate only one instance of this class for each
+ * hardware USART.
+ * \param id a number 1 to 3 to select which USART
+ * \param tx tx pin
+ * \param rx rx pin
+ * \param rts rts pin
+ * \param cts cts pin
+ */
+ STM32Serial(int id, int baudrate, miosix::GpioPin tx, miosix::GpioPin rx,
+ miosix::GpioPin rts, miosix::GpioPin cts);
+
+ /**
+ * Read a block of data
+ * \param buffer buffer where read data will be stored
+ * \param size buffer size
+ * \param where where to read from
+ * \return number of bytes read or a negative number on failure. Note that
+ * it is normal for this function to return less character than the amount
+ * asked
+ */
+ ssize_t readBlock(void *buffer, size_t size, off_t where);
+
+ /**
+ * Write a block of data
+ * \param buffer buffer where take data to write
+ * \param size buffer size
+ * \param where where to write to
+ * \return number of bytes written or a negative number on failure
+ */
+ ssize_t writeBlock(const void *buffer, size_t size, off_t where);
+
+ /**
+ * Write a string.
+ * An extension to the Device interface that adds a new member function,
+ * which is used by the kernel on console devices to write debug information
+ * before the kernel is started or in case of serious errors, right before
+ * rebooting.
+ * Can ONLY be called when the kernel is not yet started, paused or within
+ * an interrupt. This default implementation ignores writes.
+ * \param str the string to write. The string must be NUL terminated.
+ */
+ void IRQwrite(const char *str);
+
+ /**
+ * Performs device-specific operations
+ * \param cmd specifies the operation to perform
+ * \param arg optional argument that some operation require
+ * \return the exact return value depends on CMD, -1 is returned on error
+ */
+ int ioctl(int cmd, void *arg);
+
+ /**
+ * \internal the serial port interrupts call this member function.
+ * Never call this from user code.
+ */
+ void IRQhandleInterrupt();
+
+ #ifdef SERIAL_DMA
+ /**
+ * \internal the serial port DMA tx interrupts call this member function.
+ * Never call this from user code.
+ */
+ void IRQhandleDMAtx();
+
+ /**
+ * \internal the serial port DMA rx interrupts call this member function.
+ * Never call this from user code.
+ */
+ void IRQhandleDMArx();
+ #endif //SERIAL_DMA
+
+ /**
+ * \return port id, 1 for USART1, 2 for USART2, ...
+ */
+ int getId() const { return portId; }
+
+ /**
+ * Destructor
+ */
+ ~STM32Serial();
+
+private:
+ /**
+ * Code common for all constructors
+ */
+ void commonInit(int id, int baudrate, miosix::GpioPin tx, miosix::GpioPin rx,
+ miosix::GpioPin rts, miosix::GpioPin cts);
+
+ #ifdef SERIAL_DMA
+ /**
+ * Wait until a pending DMA TX completes, if any
+ */
+ void waitDmaTxCompletion();
+
+ /**
+ * Write to the serial port using DMA. When the function returns, the DMA
+ * transfer is still in progress.
+ * \param buffer buffer to write
+ * \param size size of buffer to write
+ */
+ void writeDma(const char *buffer, size_t size);
+
+ /**
+ * Read from DMA buffer and write data to queue
+ */
+ void IRQreadDma();
+
+ /**
+ * Start DMA read
+ */
+ void IRQdmaReadStart();
+
+ /**
+ * Stop DMA read
+ * \return the number of characters in rxBuffer
+ */
+ int IRQdmaReadStop();
+ #endif //SERIAL_DMA
+
+ /**
+ * Wait until all characters have been written to the serial port.
+ * Needs to be callable from interrupts disabled (it is used in IRQwrite)
+ */
+ void waitSerialTxFifoEmpty()
+ {
+ #if !defined(_ARCH_CORTEXM7_STM32F7) && !defined(_ARCH_CORTEXM7_STM32H7) \
+ && !defined(_ARCH_CORTEXM0_STM32) && !defined(_ARCH_CORTEXM4_STM32F3) \
+ && !defined(_ARCH_CORTEXM4_STM32L4)
+ while((port->SR & USART_SR_TC)==0) ;
+ #else //_ARCH_CORTEXM7_STM32F7/H7
+ while((port->ISR & USART_ISR_TC)==0) ;
+ #endif //_ARCH_CORTEXM7_STM32F7/H7
+ }
+
+ FastMutex txMutex; ///< Mutex locked during transmission
+ FastMutex rxMutex; ///< Mutex locked during reception
+
+ DynUnsyncQueue rxQueue; ///< Receiving queue
+ static const unsigned int rxQueueMin=16; ///< Minimum queue size
+ Thread *rxWaiting=0; ///< Thread waiting for rx, or 0
+
+ USART_TypeDef *port; ///< Pointer to USART peripheral
+ #ifdef SERIAL_DMA
+ #if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32F3) \
+ || defined(_ARCH_CORTEXM4_STM32L4)
+ DMA_Channel_TypeDef *dmaTx; ///< Pointer to DMA TX peripheral
+ DMA_Channel_TypeDef *dmaRx; ///< Pointer to DMA RX peripheral
+ #else //_ARCH_CORTEXM3_STM32 and _ARCH_CORTEXM4_STM32F3
+ DMA_Stream_TypeDef *dmaTx; ///< Pointer to DMA TX peripheral
+ DMA_Stream_TypeDef *dmaRx; ///< Pointer to DMA RX peripheral
+ #endif //_ARCH_CORTEXM3_STM32 and _ARCH_CORTEXM4_STM32F3
+ Thread *txWaiting; ///< Thread waiting for tx, or 0
+ 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
+ /// STM32F4, as it is used to perform DMA operations. This is guaranteed by
+ /// the fact that this class must be allocated on the heap as it derives
+ /// from Device, and the Miosix linker scripts never put the heap in CCM
+ char txBuffer[txBufferSize];
+ /// This buffer emulates the behaviour of a 16550. It is filled using DMA
+ /// and an interrupt is fired as soon as it is half full
+ char rxBuffer[rxQueueMin];
+ bool dmaTxInProgress; ///< True if a DMA tx is in progress
+ #endif //SERIAL_DMA
+ bool idle=true; ///< Receiver idle
+ const bool flowControl; ///< True if flow control GPIOs enabled
+ const unsigned char portId; ///< 1 for USART1, 2 for USART2, ...
+};
+
+} //namespace miosix
+
+#endif //SERIAL_STM32_H
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/servo_stm32.cpp b/lib/miosix-kernel/miosix/arch/common/drivers/servo_stm32.cpp
new file mode 100644
index 00000000..518d4a8b
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/servo_stm32.cpp
@@ -0,0 +1,356 @@
+ /***************************************************************************
+ * 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 *
+ ***************************************************************************/
+
+#include "servo_stm32.h"
+#include "kernel/scheduler/scheduler.h"
+#include
+#include
+#include
+
+using namespace std;
+using namespace miosix;
+
+typedef Gpio servo1out;
+typedef Gpio servo2out;
+typedef Gpio servo3out;
+typedef Gpio servo4out;
+static Thread *waiting=0;
+
+/**
+ * Timer 4 interrupt handler actual implementation
+ */
+void __attribute__((used)) tim4impl()
+{
+ TIM4->SR=0; //Clear interrupt flag
+ if(waiting==0) return;
+ waiting->IRQwakeup();
+ if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+ Scheduler::IRQfindNextThread();
+ waiting=0;
+}
+
+/**
+ * Timer 4 interrupt handler
+ */
+void __attribute__((naked)) TIM4_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z8tim4implv");
+ restoreContext();
+}
+
+namespace miosix {
+
+/* TODO: find a better place for this */
+unsigned int divideRoundToNearest(unsigned int a, unsigned int b)
+{
+ const unsigned int quot=2*a/b;
+ return quot/2 + (quot & 1);
+}
+
+//
+// class SynchronizedServo
+//
+
+SynchronizedServo& SynchronizedServo::instance()
+{
+ static SynchronizedServo singleton;
+ return singleton;
+}
+
+void SynchronizedServo::enable(int channel)
+{
+ Lock l(mutex);
+ if(status!=STOPPED) return; // If timer enabled ignore the call
+
+ {
+ FastInterruptDisableLock dLock;
+ // Calling the mode() function on a GPIO is subject to race conditions
+ // between threads on the STM32, so we disable interrupts
+ switch(channel)
+ {
+ case 0:
+ TIM4->CCMR1 |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE;
+ TIM4->CCER |= TIM_CCER_CC1E;
+ #ifndef _ARCH_CORTEXM3_STM32 //Only stm32f2 and stm32f4 have it
+ servo1out::alternateFunction(2);
+ #endif //_ARCH_CORTEXM3_STM32
+ servo1out::mode(Mode::ALTERNATE);
+ break;
+ case 1:
+ TIM4->CCMR1 |= TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE;
+ TIM4->CCER |= TIM_CCER_CC2E;
+ #ifndef _ARCH_CORTEXM3_STM32 //Only stm32f2 and stm32f4 have it
+ servo2out::alternateFunction(2);
+ #endif //_ARCH_CORTEXM3_STM32
+ servo2out::mode(Mode::ALTERNATE);
+ break;
+ case 2:
+ TIM4->CCMR2 |= TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE;
+ TIM4->CCER |= TIM_CCER_CC3E;
+ #ifndef _ARCH_CORTEXM3_STM32 //Only stm32f2 and stm32f4 have it
+ servo3out::alternateFunction(2);
+ #endif //_ARCH_CORTEXM3_STM32
+ servo3out::mode(Mode::ALTERNATE);
+ break;
+ case 3:
+ TIM4->CCMR2 |= TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE;
+ TIM4->CCER |= TIM_CCER_CC4E;
+ #ifndef _ARCH_CORTEXM3_STM32 //Only stm32f2 and stm32f4 have it
+ servo4out::alternateFunction(2);
+ #endif //_ARCH_CORTEXM3_STM32
+ servo4out::mode(Mode::ALTERNATE);
+ break;
+ }
+ }
+}
+
+void SynchronizedServo::disable(int channel)
+{
+ Lock l(mutex);
+ if(status!=STOPPED) return; // If timer enabled ignore the call
+
+ {
+ FastInterruptDisableLock dLock;
+ // Calling the mode() function on a GPIO is subject to race conditions
+ // between threads on the STM32, so we disable interrupts
+ switch(channel)
+ {
+ case 0:
+ servo1out::mode(Mode::INPUT);
+ TIM4->CCER &= ~TIM_CCER_CC1E;
+ break;
+ case 1:
+ servo2out::mode(Mode::INPUT);
+ TIM4->CCER &= ~TIM_CCER_CC2E;
+ break;
+ case 2:
+ servo3out::mode(Mode::INPUT);
+ TIM4->CCER &= ~TIM_CCER_CC3E;
+ break;
+ case 3:
+ servo4out::mode(Mode::INPUT);
+ TIM4->CCER &= ~TIM_CCER_CC4E;
+ break;
+ }
+ }
+}
+
+void SynchronizedServo::setPosition(int channel, float value)
+{
+ Lock l(mutex);
+ if(status!=STARTED) return; // If timer disabled ignore the call
+
+ value=min(1.0f,max(0.0f,value));
+ switch(channel)
+ {
+ case 0:
+ TIM4->CCR1=a*value+b;
+ break;
+ case 1:
+ TIM4->CCR2=a*value+b;
+ break;
+ case 2:
+ TIM4->CCR3=a*value+b;
+ break;
+ case 3:
+ TIM4->CCR4=a*value+b;
+ break;
+ }
+}
+
+float SynchronizedServo::getPosition(int channel)
+{
+ switch(channel)
+ {
+ case 0:
+ return TIM4->CCR1==0 ? NAN : TIM4->CCR1/a-b;
+ case 1:
+ return TIM4->CCR2==0 ? NAN : TIM4->CCR2/a-b;
+ case 2:
+ return TIM4->CCR3==0 ? NAN : TIM4->CCR3/a-b;
+ case 3:
+ return TIM4->CCR4==0 ? NAN : TIM4->CCR4/a-b;
+ default:
+ return NAN;
+ }
+}
+
+void SynchronizedServo::start()
+{
+ Lock l(mutex);
+ if(status!=STOPPED) return; // If timer enabled ignore the call
+
+ // While status is starting neither memeber function callable with timer
+ // started nor stopped are allowed
+ status=STARTED;
+ TIM4->CNT=0;
+ TIM4->EGR=TIM_EGR_UG;
+ TIM4->CR1=TIM_CR1_CEN;
+}
+
+void SynchronizedServo::stop()
+{
+ Lock l(mutex);
+ if(status!=STARTED) return; // If timer disabled ignore the call
+
+ status=STOPPED;
+ // Stopping the timer is a bit difficult because we don't want to
+ // accidentally create glitches on the outputs which may turn the servos
+ // to random positions. So, we set all PWM outputs to 0, then wait until the
+ // end of the period, and then disable the timer
+ TIM4->CCR1=0;
+ TIM4->CCR2=0;
+ TIM4->CCR3=0;
+ TIM4->CCR4=0;
+ {
+ FastInterruptDisableLock dLock;
+ // Wakeup an eventual thread waiting on waitForCycleBegin()
+ if(waiting) waiting->IRQwakeup();
+ IRQwaitForTimerOverflow(dLock);
+ }
+ TIM4->CR1=0;
+}
+
+bool SynchronizedServo::waitForCycleBegin()
+{
+ // No need to lock the mutex because disabling interrupts is enough to avoid
+ // race conditions. Also, locking the mutex here would prevent other threads
+ // from calling other member functions of this class
+ FastInterruptDisableLock dLock;
+ if(status!=STARTED) return true;
+ IRQwaitForTimerOverflow(dLock);
+ return status!=STARTED;
+}
+
+void SynchronizedServo::setFrequency(unsigned int frequency)
+{
+ Lock l(mutex);
+ if(status!=STOPPED) return; // If timer enabled ignore the call
+
+ TIM4->PSC=divideRoundToNearest(getPrescalerInputFrequency(),frequency*65536)-1;
+ precomputeCoefficients();
+}
+
+float SynchronizedServo::getFrequency() const
+{
+ float prescalerFreq=getPrescalerInputFrequency();
+ return prescalerFreq/((TIM4->PSC+1)*65536);
+}
+
+void SynchronizedServo::setMinPulseWidth(float minPulse)
+{
+ Lock l(mutex);
+ if(status!=STOPPED) return; // If timer enabled ignore the call
+
+ minWidth=1e-6f*min(1300.0f,max(500.0f,minPulse));
+ precomputeCoefficients();
+}
+
+void SynchronizedServo::setMaxPulseWidth(float maxPulse)
+{
+ Lock l(mutex);
+ if(status!=STOPPED) return; // If timer enabled ignore the call
+
+ maxWidth=1e-6f*min(2500.0f,max(1700.0f,maxPulse));
+ precomputeCoefficients();
+}
+
+SynchronizedServo::SynchronizedServo() : status(STOPPED)
+{
+ {
+ FastInterruptDisableLock dLock;
+ // The RCC register should be written with interrupts disabled to
+ // prevent race conditions with other threads.
+ RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;
+ RCC_SYNC();
+ }
+
+ // Configure timer
+ TIM4->CR1=0;
+ TIM4->ARR=0xffff;
+ TIM4->CCR1=0;
+ TIM4->CCR2=0;
+ TIM4->CCR3=0;
+ TIM4->CCR4=0;
+ // Configure interrupt on timer overflow
+ TIM4->DIER=TIM_DIER_UIE;
+ NVIC_SetPriority(TIM4_IRQn,13); //Low priority for timer IRQ
+ NVIC_EnableIRQ(TIM4_IRQn);
+ // Set default parameters
+ setFrequency(50);
+ setMinPulseWidth(1000);
+ setMaxPulseWidth(2000);
+}
+
+void SynchronizedServo::precomputeCoefficients()
+{
+ float realPeriod=1.0f/getFrequency();
+ a=65536.0f*(maxWidth-minWidth)/realPeriod;
+ b=65536.0f*minWidth/realPeriod;
+}
+
+unsigned int SynchronizedServo::getPrescalerInputFrequency()
+{
+ // The global variable SystemCoreClock from ARM's CMSIS allows to know
+ // the CPU frequency.
+ unsigned int freq=SystemCoreClock;
+
+ //The position of the PPRE1 bit in RCC->CFGR is different in some stm32
+ #ifdef _ARCH_CORTEXM3_STM32
+ const unsigned int ppre1=8;
+ #else //stm32f2 and f4
+ const unsigned int ppre1=10;
+ #endif
+
+ // 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) freq/=1<<((RCC->CFGR>>ppre1) & 0x3);
+
+ return freq;
+}
+
+void SynchronizedServo::IRQwaitForTimerOverflow(FastInterruptDisableLock& dLock)
+{
+ waiting=Thread::IRQgetCurrentThread();
+ do {
+ Thread::IRQwait();
+ {
+ FastInterruptEnableLock eLock(dLock);
+ Thread::yield();
+ }
+ } while(waiting);
+}
+
+} //namespace miosix
+
+
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/servo_stm32.h b/lib/miosix-kernel/miosix/arch/common/drivers/servo_stm32.h
new file mode 100644
index 00000000..a3570d3c
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/servo_stm32.h
@@ -0,0 +1,196 @@
+/***************************************************************************
+ * 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 *
+ ***************************************************************************/
+
+#ifndef SERVO_STM32_H
+#define SERVO_STM32_H
+
+#include "miosix.h"
+
+namespace miosix {
+
+/**
+ * This class is designed to drive up to 4 servomotors. It generates
+ * four square waves that are synchronized with respect to each other,
+ * and allows the execution of code that is too synchronized with the
+ * waveform generation, to ease the development of closed loop control
+ * code using the servos as actuators.
+ * This class can be safely accessed by multiple threads, except the
+ * waitForCycleBegin() member function.
+ */
+class SynchronizedServo
+{
+public:
+ /**
+ * \return an instance of the SynchronizedServo class (singleton)
+ * When first returned, the servo waveform generation is stopped. You must
+ * enable at least one channel call start() and setPosition() before the
+ * servo driving waveforms will be generated.
+ */
+ static SynchronizedServo& instance();
+
+ /**
+ * Enable a channel. Can only be called with the outputs stopped. Even if
+ * the channel is enabled, when it is started it will not produce any
+ * waveform until the first call to setPosition()
+ * \param channel which channel to enable, must be between 0 and 3.
+ */
+ void enable(int channel);
+
+ /**
+ * Disable a channel. Can only be called with the outputs stopped.
+ * \param channel which channel to disable, must be between 0 and 3.
+ */
+ void disable(int channel);
+
+ /**
+ * Set the servo position. Can only be called with the outputs started.
+ * The written value takes effect at the next waveform generation cycle.
+ * \param channel channel whose output need to be changed, between 0 and 3
+ * \param value a float value from 0.0 to 1.0. Due to the limited timer
+ * resolution, the actual set value is approximated. With the default values
+ * of waveform frequency, min and max width the range between 0.0 and 1.0
+ * is covered by around 3200 points.
+ */
+ void setPosition(int channel, float value);
+
+ /**
+ * \param channel channel whose output need to be read, between 0 and 3
+ * \return the exact servo position, considering the approximations.
+ * For this reason, the returned value may differ from the last call to
+ * setPosition(). NAN is returned if no setValue() was called on the channel
+ * since the last call to start()
+ */
+ float getPosition(int channel);
+
+ /**
+ * Start producing the output waveforms.
+ */
+ void start();
+
+ /**
+ * Stop producing the output waveforms. If a thread is waiting in
+ * waitForCycleBegin() it will be forecefully woken up.
+ * As a side effect, the position of all the channels will be set to NAN.
+ * When you restart the timer, you must call setPosition() on each enabled
+ * channel before the channel will actually produce a waveform.
+ * This function waits until the end of a waveform generation cycle in order
+ * to not produce glitches. For this reason, it may take up to
+ * 1/getFrequency() to complete, which with the default value of 50Hz is 20ms
+ */
+ void stop();
+
+ /**
+ * Wait until the begin of a waveform generation cycle
+ * \return false if a new cycle of waveform generation has begun, or true
+ * if another thread called stop(). Only one thread at a time can call this
+ * member function. If more than one thread calls this deadlock will occur
+ * so don't do it!
+ */
+ bool waitForCycleBegin();
+
+ /**
+ * Set the frequency of the generated waveform. Can only be called
+ * with the outputs stopped. The default is 50Hz. Note that due to prescaler
+ * resolution, the actual frequency is set to the closest possible value.
+ * To know the actual frequency, call getFrequency()
+ * \param frequency desired servo update frequency in Hz
+ * Must be between 10 and 100Hz
+ */
+ void setFrequency(unsigned int frequency);
+
+ /**
+ * \return the actual servo update frequency in Hz. Note that the
+ * returned value is floating point as the returned frequency takes into
+ * account approximations due to the prescaler resolution
+ */
+ float getFrequency() const;
+
+ /**
+ * Set the minimum width of the generated pulses, that is, the pulse width
+ * generated when an output is set to zero with setPosition(x,0).
+ * The default is 1000us. Can only be called with the outputs stopped.
+ * \param minPulse minimum pulse width in microseconds.
+ * Must be between 500 and 1300.
+ */
+ void setMinPulseWidth(float minPulse);
+
+ /**
+ * \return minimum pulse width in microseconds
+ */
+ float getMinPulseWidth() const { return minWidth*1e6f; }
+
+ /**
+ * Set the maximum width of the generated pulses, that is, the pulse width
+ * generated when an output is set to one with setPosition(x,1).
+ * The default is 2000us. Can only be called with the outputs stopped.
+ * \param maxPulse maximum pulse width in microseconds.
+ * Must be between 1700 and 2500.
+ */
+ void setMaxPulseWidth(float maxPulse);
+
+ /**
+ * \return maximum pulse width in microseconds
+ */
+ float getMaxPulseWidth() const { return maxWidth*1e6f; }
+
+private:
+ SynchronizedServo(const SynchronizedServo&);
+ SynchronizedServo& operator= (const SynchronizedServo&);
+
+ /**
+ * Constructor
+ */
+ SynchronizedServo();
+
+ /**
+ * Precompute a and b coefficient to make setPosition() faster
+ */
+ void precomputeCoefficients();
+
+ /**
+ * \return the input frequency of the timer prescaler
+ */
+ static unsigned int getPrescalerInputFrequency();
+
+ /**
+ * Wait until the timer overflows from 0xffff to 0. Can only be called with
+ * interrupts disabled
+ */
+ static void IRQwaitForTimerOverflow(FastInterruptDisableLock& dLock);
+
+ float minWidth, maxWidth; ///< Minimum and maximum pulse widths
+ float a, b; ///< Precomputed coefficients
+ FastMutex mutex; ///< Mutex to protect from concurrent access
+ enum {
+ STOPPED, ///< Timer is stopped
+ STARTED ///< Timer is started
+ } status;
+};
+
+} //namespace miosix
+
+#endif //SERVO_STM32_H
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/stm32_hardware_rng.cpp b/lib/miosix-kernel/miosix/arch/common/drivers/stm32_hardware_rng.cpp
new file mode 100644
index 00000000..9595e24d
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/stm32_hardware_rng.cpp
@@ -0,0 +1,121 @@
+/***************************************************************************
+ * Copyright (C) 2013 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 *
+ ***************************************************************************/
+
+#include
+#include "interfaces/delays.h"
+#include "stm32_hardware_rng.h"
+
+using namespace std;
+
+namespace miosix {
+
+//
+// class HardwareRng
+//
+
+HardwareRng& HardwareRng::instance()
+{
+ static HardwareRng singleton;
+ return singleton;
+}
+
+unsigned int HardwareRng::get()
+{
+ miosix::Lock l(mutex);
+ PeripheralEnable pe;
+ return getImpl();
+}
+
+void HardwareRng::get(void* buf, unsigned int size)
+{
+ unsigned char *buffer=reinterpret_cast(buf);
+ Lock l(mutex);
+ PeripheralEnable pe;
+ union Cast
+ {
+ unsigned int theInt;
+ unsigned char theChar[4];
+ };
+
+ if(reinterpret_cast(buffer) & 0x3)
+ {
+ Cast cast;
+ cast.theInt=getImpl();
+ int i=0;
+ while(reinterpret_cast(buffer) & 0x3)
+ {
+ if(size--==0) return; //May happen if buffer is small and unaligned
+ *buffer++=cast.theChar[i++];
+ }
+ }
+
+ unsigned int *aligned=reinterpret_cast(buffer);
+ for(unsigned int i=0;i0) { sr=RNG->SR; if(sr & RNG_SR_DRDY) break; }
+ if((sr & RNG_SR_SECS) || (sr & RNG_SR_CECS) || timeout<=0)
+ {
+ RNG->CR=0;
+ delayUs(1);
+ RNG->CR=RNG_CR_RNGEN;
+ continue;
+ }
+ unsigned int result=RNG->DR;
+ if(result==old) continue;
+ old=result;
+ return result;
+ }
+ #ifndef __NO_EXCEPTIONS
+ throw runtime_error("RNG Fault detected");
+ #endif //__NO_EXCEPTIONS
+}
+
+} //namespace miosix
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/stm32_hardware_rng.h b/lib/miosix-kernel/miosix/arch/common/drivers/stm32_hardware_rng.h
new file mode 100644
index 00000000..ceab1df0
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/stm32_hardware_rng.h
@@ -0,0 +1,98 @@
+/***************************************************************************
+ * Copyright (C) 2013 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 *
+ ***************************************************************************/
+
+#ifndef RNG_H
+#define RNG_H
+
+#include "interfaces/arch_registers.h"
+#include "kernel/sync.h"
+
+namespace miosix {
+
+/**
+ * Class to access the hardware random number generator in Miosix
+ * Works with the hardware RNG in stm32f2 and stm32f4
+ */
+class HardwareRng
+{
+public:
+ /**
+ * \return an instance of this class (singleton)
+ */
+ static HardwareRng& instance();
+
+ /**
+ * \return a 32 bit random number
+ * \throws runtime_error if the self test is not passed
+ */
+ unsigned int get();
+
+ /**
+ * Fill a buffer with random data
+ * \param buf buffer to be filled
+ * \param size buffer size
+ * \throws runtime_error if the self test is not passed
+ */
+ void get(void *buf, unsigned int size);
+
+private:
+ HardwareRng(const HardwareRng&);
+ HardwareRng& operator=(const HardwareRng&);
+
+ /**
+ * Constructor
+ */
+ HardwareRng() : old(0)
+ {
+ miosix::FastInterruptDisableLock dLock;
+ RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN;
+ RCC_SYNC();
+ }
+
+ /**
+ * \return a 32 bit random number
+ * \throws runtime_error if the self test is not passed
+ */
+ unsigned int getImpl();
+
+ /**
+ * To save power, enable the peripheral ony when requested
+ */
+ class PeripheralEnable
+ {
+ public:
+ PeripheralEnable() { RNG->CR=RNG_CR_RNGEN; }
+ ~PeripheralEnable() { RNG->CR=0; }
+ };
+
+ miosix::FastMutex mutex; ///< To protect against concurrent access
+ unsigned int old; ///< Previously read value
+};
+
+} //namespace miosix
+
+#endif //RNG_H
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/stm32_rtc.cpp b/lib/miosix-kernel/miosix/arch/common/drivers/stm32_rtc.cpp
new file mode 100644
index 00000000..13dd332c
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/stm32_rtc.cpp
@@ -0,0 +1,322 @@
+/***************************************************************************
+ * Copyright (C) 2017 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 *
+ ***************************************************************************/
+
+#include "stm32_rtc.h"
+#include
+#include
+#include
+
+using namespace miosix;
+
+namespace {
+
+//
+// class ScopedCnf, RAII to allow writing to the RTC alarm and prescaler.
+// NOTE: datasheet says that after CNF bit has been cleared, no write is allowed
+// to *any* of the RTC registers, not just PRL,CNT,ALR until RTOFF goes to 1.
+// We do not wait until RTOFF is 1 in the destructor for performance reasons,
+// so the rest of the code must be careful.
+//
+class ScopedCnf
+{
+public:
+ ScopedCnf()
+ {
+ while((RTC->CRL & RTC_CRL_RTOFF)==0) ;
+ RTC->CRL=0b11111;
+ }
+
+ ~ScopedCnf()
+ {
+ RTC->CRL=0b01111;
+ }
+};
+
+long long swTime=0; //64bit software extension of current time in ticks
+long long irqTime=0; //64bit software extension of scheduled irq time in ticks
+Thread *waiting=nullptr; //waiting thread
+
+/**
+ * \return the hardware counter
+ */
+inline unsigned int IRQgetHwTick()
+{
+ unsigned int h1=RTC->CNTH;
+ unsigned int l1=RTC->CNTL;
+ unsigned int h2=RTC->CNTH;
+ if(h1==h2) return (h1<<16) | l1;
+ return (h2<<16) | RTC->CNTL;
+}
+
+/**
+ * \return the time in ticks (hardware part + software extension to 64bits)
+ */
+long long IRQgetTick()
+{
+ //Pending bit trick
+ unsigned int hwTime=IRQgetHwTick();
+ if((RTC->CRL & RTC_CRL_OWF) && IRQgetHwTick()>=hwTime)
+ return (swTime + static_cast(hwTime)) + (1LL<<32);
+ return swTime + static_cast(hwTime);
+}
+
+/**
+ * Sleep the current thread till the specified time
+ * \param tick absolute time in ticks
+ * \param dLock used to reenable interrupts while sleeping
+ * \return true if the wait time was in the past
+ */
+bool IRQabsoluteWaitTick(long long tick, FastInterruptDisableLock& dLock)
+{
+ irqTime=tick;
+ unsigned int hwAlarm=(tick & 0xffffffffULL) - (swTime & 0xffffffffULL);
+ {
+ ScopedCnf cnf;
+ RTC->ALRL=hwAlarm;
+ RTC->ALRH=hwAlarm>>16;
+ }
+ //NOTE: We do not wait for the alarm register to be written back to the low
+ //frequency domain for performance reasons. The datasheet says it takes
+ //at least 3 cycles of the 32KHz clock, but experiments show that it takes
+ //from 2 to 3, so perhaps they meant "at most 3". Because of this we
+ //consider the time in the past if we are more than 2 ticks of the 16KHz
+ //clock (4 ticks of the 32KHz one) in advance. Sleeps less than 122us are
+ //thus not supported.
+ if(IRQgetTick()>=tick-2) return true;
+ waiting=Thread::IRQgetCurrentThread();
+ do {
+ Thread::IRQwait();
+ {
+ FastInterruptEnableLock eLock(dLock);
+ Thread::yield();
+ }
+ } while(waiting);
+ return false;
+}
+
+} //anon namespace
+
+/**
+ * RTC interrupt
+ */
+void __attribute__((naked)) RTC_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z10RTCIrqImplv");
+ restoreContext();
+}
+
+/**
+ * RTC interrupt actual implementation
+ */
+void __attribute__((used)) RTCIrqImpl()
+{
+ unsigned int crl=RTC->CRL;
+ if(crl & RTC_CRL_OWF)
+ {
+ RTC->CRL=(RTC->CRL | 0xf) & ~RTC_CRL_OWF;
+ swTime+=1LL<<32;
+ } else if(crl & RTC_CRL_ALRF) {
+ RTC->CRL=(RTC->CRL | 0xf) & ~RTC_CRL_ALRF;
+ if(waiting && IRQgetTick()>=irqTime)
+ {
+ waiting->IRQwakeup();
+ if(waiting->IRQgetPriority()>
+ Thread::IRQgetCurrentThread()->IRQgetPriority())
+ Scheduler::IRQfindNextThread();
+ waiting=nullptr;
+ }
+ }
+}
+
+namespace miosix {
+
+void absoluteDeepSleep(long long int value)
+{
+ #ifdef RUN_WITH_HSI
+ const int wakeupAdvance=3; //waking up takes time
+ #else //RUN_WITH_HSI
+ const int wakeupAdvance=33; //HSE starup time is 2ms
+ #endif //RUN_WITH_HSI
+
+ Rtc& rtc=Rtc::instance();
+ ioctl(STDOUT_FILENO,IOCTL_SYNC,0);
+
+ FastInterruptDisableLock dLock;
+
+ //Set alarm and enable EXTI
+ long long wkupTick=rtc.tc.ns2tick(value)-wakeupAdvance;
+ unsigned int hwAlarm=(wkupTick & 0xffffffffULL) - (swTime & 0xffffffffULL);
+ {
+ ScopedCnf cnf;
+ RTC->ALRL=hwAlarm;
+ RTC->ALRH=hwAlarm>>16;
+ }
+ while((RTC->CRL & RTC_CRL_RTOFF)==0) ;
+
+
+ EXTI->RTSR |= EXTI_RTSR_TR17;
+ EXTI->EMR |= EXTI_EMR_MR17; //enable event for wakeup
+
+ long long tick=IRQgetTick();
+ while(tickCR |= PWR_CR_LPDS;
+ SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
+ // Using WFE instead of WFI because if while we are with interrupts
+ // disabled an interrupt (such as the tick interrupt) occurs, it
+ // remains pending and the WFI becomes a nop, and the device never goes
+ // in sleep mode. WFE events are latched in a separate pending register
+ // so interrupts do not interfere with them
+ __WFE();
+ SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
+ PWR->CR &= ~PWR_CR_LPDS;
+
+ #ifndef SYSCLK_FREQ_24MHz
+ #error TODO: support more PLL frequencies
+ #endif
+ //STOP mode resets the clock to the HSI 8MHz, so restore the 24MHz clock
+ #ifndef RUN_WITH_HSI
+ RCC->CR |= RCC_CR_HSEON;
+ while((RCC->CR & RCC_CR_HSERDY)==0) ;
+ //PLL = (HSE / 2) * 6 = 24 MHz
+ RCC->CFGR &= ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL);
+ RCC->CFGR |= RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL6;
+ #else //RUN_WITH_HSI
+ //PLL = (HSI / 2) * 6 = 24 MHz
+ RCC->CFGR &= ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL);
+ RCC->CFGR |= RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL6;
+ #endif //RUN_WITH_HSI
+ RCC->CR |= RCC_CR_PLLON;
+ while((RCC->CR & RCC_CR_PLLRDY)==0) ;
+ RCC->CFGR &= ~RCC_CFGR_SW;
+ RCC->CFGR |= RCC_CFGR_SW_PLL;
+ while((RCC->CFGR & RCC_CFGR_SWS)!=0x08) ;
+
+ //Wait RSF
+ RTC->CRL=(RTC->CRL | 0xf) & ~RTC_CRL_RSF;
+ while((RTC->CRL & RTC_CRL_RSF)==0) ;
+
+ //Clear IRQ
+ unsigned int crl=RTC->CRL;
+ //NOTE: ST docs say OWF, ALRF are not updated in deep sleep. So, to
+ //detect counter ovreflow we check for oldTick>newTick. The check for
+ //flags is still there in case overflow/alarm occurs before/after sleep
+ if(crl & RTC_CRL_OWF || tick>IRQgetTick())
+ {
+ RTC->CRL=(RTC->CRL | 0xf) & ~RTC_CRL_OWF;
+ swTime+=1LL<<32;
+ } else if(crl & RTC_CRL_ALRF) {
+ RTC->CRL=(RTC->CRL | 0xf) & ~RTC_CRL_ALRF;
+ }
+
+ //Update tick, done after checking IRQ flags to avoid race condition
+ tick=IRQgetTick();
+ }
+
+ EXTI->EMR &=~ EXTI_EMR_MR17; //disable event for wakeup
+
+ wkupTick+=wakeupAdvance;
+
+ //NOTE: if we use the HSE we may spin for a while, but adding a
+ //IRQabsoluteWaitTick here makes this function wakeup too late
+ while(IRQgetTick()APB1ENR |= RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN;
+ PWR->CR |= PWR_CR_DBP;
+ RCC->BDCR=RCC_BDCR_RTCEN //RTC enabled
+ | RCC_BDCR_LSEON //External 32KHz oscillator enabled
+ | RCC_BDCR_RTCSEL_0; //Select LSE as clock source for RTC
+ RCC_SYNC();
+ #ifdef RTC_CLKOUT_ENABLE
+ BKP->RTCCR=BKP_RTCCR_CCO; //Output RTC clock/64 on pin
+ #endif
+ }
+ while((RCC->BDCR & RCC_BDCR_LSERDY)==0) ; //Wait for LSE to start
+
+ RTC->CRH=RTC_CRH_OWIE | RTC_CRH_ALRIE;
+ {
+ ScopedCnf cnf;
+ RTC->PRLH=0;
+ RTC->PRLL=1;
+ }
+ NVIC_SetPriority(RTC_IRQn,5);
+ NVIC_EnableIRQ(RTC_IRQn);
+}
+
+} //namespace miosix
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/stm32_rtc.h b/lib/miosix-kernel/miosix/arch/common/drivers/stm32_rtc.h
new file mode 100644
index 00000000..de080010
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/stm32_rtc.h
@@ -0,0 +1,115 @@
+/***************************************************************************
+ * Copyright (C) 2017 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 *
+ ***************************************************************************/
+
+#ifndef RTC_H
+#define RTC_H
+
+#include
+
+namespace miosix {
+
+///
+/// Uncomment this directive to enable the RTC clock output functionality
+///
+
+//#define RTC_CLKOUT_ENABLE
+
+/**
+ * Puts the MCU in deep sleep until the specified absolute time.
+ * \param value absolute wait time in nanoseconds
+ * If value of absolute time is in the past no waiting will be set
+ * and function return immediately.
+ */
+void absoluteDeepSleep(long long value);
+
+/**
+ * Driver for the stm32 RTC.
+ * All the wait and deepSleep functions cannot be called concurrently by
+ * multiple threads.
+ */
+class Rtc
+{
+public:
+ /**
+ * \return an instance of this class
+ */
+ static Rtc& instance();
+
+ /**
+ * \return the timer counter value in nanoseconds
+ */
+ long long getValue() const;
+
+ /**
+ * \return the timer counter value in nanoseconds
+ *
+ * Can be called with interrupt disabled, or inside an interrupt
+ */
+ long long IRQgetValue() const;
+
+ /**
+ * Set the timer counter value
+ * \param value new timer value in nanoseconds
+ *
+ * NOTE: if alarm is set wakeup time is not updated
+ */
+// void setValue(long long value);
+
+ /**
+ * Put thread in wait for the specified relative time.
+ * This function wait for a relative time passed as parameter.
+ * \param value relative time to wait, expressed in nanoseconds
+ */
+ void wait(long long value);
+
+ /**
+ * Puts the thread in wait until the specified absolute time.
+ * \param value absolute wait time in nanoseconds
+ * If value of absolute time is in the past no waiting will be set
+ * and function return immediately.
+ * \return true if the wait time was in the past
+ */
+ bool absoluteWait(long long value);
+
+ /**
+ * \return the timer frequency in Hz
+ */
+ unsigned int getTickFrequency() const { return 16384; }
+
+private:
+ Rtc();
+ Rtc(const Rtc&)=delete;
+ Rtc& operator= (const Rtc&)=delete;
+
+ TimeConversion tc;
+
+ friend void absoluteDeepSleep(long long value);
+};
+
+} //namespace miosix
+
+#endif //RTC_H
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/stm32_sgm.cpp b/lib/miosix-kernel/miosix/arch/common/drivers/stm32_sgm.cpp
new file mode 100644
index 00000000..64849679
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/stm32_sgm.cpp
@@ -0,0 +1,130 @@
+/***************************************************************************
+ * Copyright (C) 2017 by Matteo Michele Piazzolla *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "board_settings.h"
+#include "stm32_sgm.h"
+#include
+#include "miosix.h"
+
+namespace miosix {
+
+extern unsigned char _preserve_start asm("_preserve_start");
+extern unsigned char _preserve_end asm("_preserve_end");
+
+static unsigned char *preserve_start=&_preserve_start;
+static unsigned char *preserve_end=&_preserve_end;
+
+SGM& SGM::instance()
+{
+ static SGM singleton;
+ return singleton;
+}
+
+SGM::SGM()
+{
+ /* Enable PWR clock */
+ RCC->APB1ENR |= RCC_APB1ENR_PWREN;
+
+ /* Enable backup SRAM Clock */
+ RCC->AHB1ENR |= RCC_AHB1ENR_BKPSRAMEN;
+
+ enableWrite();
+
+ /* Enable Backup regulator */
+ PWR->CSR |= PWR_CSR_BRE;
+
+ /* Enable the Backup SRAM low power Regulator */
+ PWR->CSR |= PWR_CSR_BRE;
+
+ /* Wait for backup regulator */
+ while (!(PWR->CSR & (PWR_CSR_BRR)));
+
+ /* Retrive last reset reason and clear the pending flag */
+ readResetRegister();
+
+ /*
+ * If the reset wasn't caused by software failure we cannot trust
+ * the content of the backup memory and we need to clear it.
+ */
+ if(lastReset != RST_SW)
+ {
+ memset(preserve_start, 0, preserve_end-preserve_start);
+ }
+}
+
+void SGM::disableWrite()
+{
+ /* Enable Backup Domain write protection */
+ PWR->CR &= ~PWR_CR_DBP;
+}
+
+void SGM::enableWrite()
+{
+ /* Disable Backup Domain write protection */
+ PWR->CR |= PWR_CR_DBP;
+}
+
+void SGM::clearResetFlag()
+{
+ RCC->CSR |= RCC_CSR_RMVF;
+}
+
+void SGM::readResetRegister()
+{
+ uint32_t resetReg = RCC->CSR;
+ clearResetFlag();
+ if(resetReg & RCC_CSR_LPWRRSTF)
+ {
+ lastReset = RST_LOW_PWR;
+ }
+ else if( resetReg & RCC_CSR_WWDGRSTF)
+ {
+ lastReset = RST_WINDOW_WDG;
+ }
+ else if( resetReg & RCC_CSR_WDGRSTF)
+ {
+ lastReset = RST_INDEPENDENT_WDG;
+ }
+ else if( resetReg & RCC_CSR_SFTRSTF)
+ {
+ lastReset = RST_SW;
+ }
+ else if( resetReg & RCC_CSR_PORRSTF)
+ {
+ lastReset = RST_POWER_ON;
+ }
+ else if( resetReg & RCC_CSR_PADRSTF)
+ {
+ lastReset = RST_PIN;
+ }
+ else
+ {
+ lastReset = RST_UNKNOWN;
+ }
+}
+
+} // namespace miosix
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/stm32_sgm.h b/lib/miosix-kernel/miosix/arch/common/drivers/stm32_sgm.h
new file mode 100644
index 00000000..10cc6dc9
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/stm32_sgm.h
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * Copyright (C) 2017 by Matteo Michele Piazzolla *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "board_settings.h"
+
+#define PRESERVE __attribute__((section(".preserve")))
+
+namespace miosix {
+
+/**
+ * Possible causes for an STM32 reset
+ */
+enum ResetReason
+{
+ RST_LOW_PWR=0,
+ RST_WINDOW_WDG=1,
+ RST_INDEPENDENT_WDG=2,
+ RST_SW=3,
+ RST_POWER_ON=4,
+ RST_PIN=5,
+ RST_UNKNOWN=6,
+};
+
+/**
+ * Driver for the STM32F2 and STM32F4 backup SRAM, here used as
+ * SafeGuard Memory, that is, a memory whose value is preseved across resets.
+ */
+class SGM
+{
+public:
+ /**
+ * \return an instance of this class (singleton)
+ */
+ static SGM& instance();
+
+ /**
+ * Temporarily disable writing to the safeguard memory.
+ * By deafult, from reset to when the contrsuctor of this class is called
+ * the safeguard memory is not writable. After the constructor is called,
+ * the safeguard memory is writable.
+ */
+ void disableWrite();
+
+ /**
+ * Make the safeguard memory writable again, after a call to disableWrite()
+ */
+ void enableWrite();
+
+ /**
+ * Return the cause of the last reset of the microcontroller
+ */
+ ResetReason lastResetReason() { return lastReset; }
+
+private:
+ ResetReason lastReset;
+
+ SGM(const SGM&)=delete;
+ SGM& operator=(const SGM&)=delete;
+
+ SGM();
+ void readResetRegister();
+ void clearResetFlag();
+
+};
+
+
+}
\ No newline at end of file
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/stm32_wd.cpp b/lib/miosix-kernel/miosix/arch/common/drivers/stm32_wd.cpp
new file mode 100644
index 00000000..8e323c72
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/stm32_wd.cpp
@@ -0,0 +1,62 @@
+/***************************************************************************
+ * Copyright (C) 2017 by Matteo Michele Piazzolla *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "stm32_wd.h"
+#include "miosix.h"
+#include
+
+using namespace std;
+
+namespace miosix {
+
+IWatchDog& IWatchDog::instance()
+{
+ static IWatchDog singleton;
+ return singleton;
+}
+
+void IWatchDog::enable(int ms)
+{
+ /* disable WD configuration protection */
+ IWDG->KR = 0x5555;
+
+ /* set prescaler divider to /32 */
+ IWDG->PR = 0x3;
+
+ /* set reload register */
+ IWDG->RLR = max(1,min(4096,ms))-1;
+
+ /* start the watchdog */
+ IWDG->KR = 0xCCCC;
+}
+
+void IWatchDog::refresh()
+{
+ IWDG->KR=0xAAAA;
+}
+
+} //namespace miosix
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/stm32_wd.h b/lib/miosix-kernel/miosix/arch/common/drivers/stm32_wd.h
new file mode 100644
index 00000000..199bf54f
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/stm32_wd.h
@@ -0,0 +1,62 @@
+/***************************************************************************
+ * Copyright (C) 2017 by Matteo Michele Piazzolla *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+namespace miosix {
+
+/**
+ * Driver for the STM32 independent watchdog
+ */
+class IWatchDog
+{
+public:
+ /**
+ * \return an instance of this class (singleton)
+ */
+ static IWatchDog& instance();
+
+ /**
+ * Enable the watchdog
+ * \param ms reload period, from 1 (1 millisecond) to 4096 (4.096 seconds)
+ */
+ void enable(int ms);
+
+ /**
+ * If the watchdog is not periodically reloaded at least within the
+ * period selected by enable, the microcontroller is reset.
+ * The datsheet says that the oscillator clocking the watchdog can be
+ * up to twice as fast, so it is recomended to reload the watchdog three
+ * to four times faster to prevent spurious resets.
+ */
+ void refresh();
+
+private:
+ IWatchDog(const IWatchDog&)=delete;
+ IWatchDog& operator=(const IWatchDog&)=delete;
+ IWatchDog() {}
+};
+
+} //namespace miosix
diff --git a/lib/miosix-kernel/miosix/arch/common/drivers/stm32f2_f4_i2c.cpp b/lib/miosix-kernel/miosix/arch/common/drivers/stm32f2_f4_i2c.cpp
new file mode 100644
index 00000000..69067f9a
--- /dev/null
+++ b/lib/miosix-kernel/miosix/arch/common/drivers/stm32f2_f4_i2c.cpp
@@ -0,0 +1,476 @@
+/***************************************************************************
+ * Copyright (C) 2013 by Terraneo Federico and Silvano Seva *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "stm32f2_f4_i2c.h"
+#include
+#include
+
+using namespace miosix;
+
+static volatile bool error; ///< Set to true by IRQ on error
+static Thread *waiting=0; ///< Thread waiting for an operation to complete
+
+/* In non-DMA mode the variables below are used to
+ * handle the reception of 2 or more bytes through
+ * an interrupt, avoiding the thread that calls recv
+ * to be locked in polling
+ */
+
+#ifndef I2C_WITH_DMA
+static uint8_t *rxBuf = 0;
+static unsigned int rxBufCnt = 0;
+static unsigned int rxBufSize = 0;
+#endif
+
+
+#ifdef I2C_WITH_DMA
+/**
+ * DMA I2C rx end of transfer
+ */
+void __attribute__((naked)) DMA1_Stream0_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z20I2C1rxDmaHandlerImplv");
+ restoreContext();
+}
+
+/**
+ * DMA I2C rx end of transfer actual implementation
+ */
+void __attribute__((used)) I2C1rxDmaHandlerImpl()
+{
+ DMA1->LIFCR=DMA_LIFCR_CTCIF0
+ | DMA_LIFCR_CTEIF0
+ | DMA_LIFCR_CDMEIF0
+ | DMA_LIFCR_CFEIF0;
+ I2C1->CR1 |= I2C_CR1_STOP;
+ if(waiting==0) return;
+ waiting->IRQwakeup();
+ if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+ Scheduler::IRQfindNextThread();
+ waiting=0;
+}
+
+/**
+ * DMA I2C tx end of transfer
+ */
+void DMA1_Stream7_IRQHandler()
+{
+ DMA1->HIFCR=DMA_HIFCR_CTCIF7
+ | DMA_HIFCR_CTEIF7
+ | DMA_HIFCR_CDMEIF7
+ | DMA_HIFCR_CFEIF7;
+
+ //We can't just wake the thread because the I2C is double buffered, and this
+ //interrupt is fired at the same time as the second last byte is starting
+ //to be sent out of the bus. If we return now, the main code would send a
+ //stop condiotion too soon, and the last byte would never be sent. Instead,
+ //we change from DMA mode to IRQ mode, so when the second last byte is sent,
+ //that interrupt is fired and the last byte is sent out.
+ //Note that since no thread is awakened from this IRQ, there's no need for
+ //the saveContext(), restoreContext() and __attribute__((naked))
+ I2C1->CR2 &= ~I2C_CR2_DMAEN;
+ I2C1->CR2 |= I2C_CR2_ITBUFEN | I2C_CR2_ITEVTEN;
+}
+
+#endif
+
+/**
+ * I2C address sent interrupt
+ */
+void __attribute__((naked)) I2C1_EV_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z15I2C1HandlerImplv");
+ restoreContext();
+}
+
+/**
+ * I2C address sent interrupt actual implementation
+ */
+void __attribute__((used)) I2C1HandlerImpl()
+{
+ #ifdef I2C_WITH_DMA
+ //When called to resolve the last byte not sent issue, clearing
+ //I2C_CR2_ITBUFEN prevents this interrupt being re-entered forever, as
+ //it does not send another byte to the I2C, so the interrupt would remain
+ //pending. When called after the start bit has been sent, clearing
+ //I2C_CR2_ITEVTEN prevents the same infinite re-enter as this interrupt
+ //does not start an address transmission, which is necessary to stop
+ //this interrupt from being pending
+ I2C1->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN);
+ if(waiting==0) return;
+ #else
+
+ bool rxFinished = false;
+
+ /* If rxBuf is equal to zero means that we are sending the slave
+ address and this ISR is used to manage the address sent interrupt */
+
+ if(rxBuf == 0)
+ {
+ I2C1->CR2 &= ~I2C_CR2_ITEVTEN;
+ rxFinished = true;
+ }
+
+ if(I2C1->SR1 & I2C_SR1_RXNE)
+ {
+ rxBuf[rxBufCnt++] = I2C1->DR;
+ if(rxBufCnt >= rxBufSize)
+ {
+ I2C1->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITBUFEN);
+ rxFinished = true;
+ }
+ }
+
+ if(waiting==0 || !rxFinished) return;
+ #endif
+ waiting->IRQwakeup();
+ if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+ Scheduler::IRQfindNextThread();
+ waiting=0;
+}
+
+/**
+ * I2C error interrupt
+ */
+void __attribute__((naked)) I2C1_ER_IRQHandler()
+{
+ saveContext();
+ asm volatile("bl _Z18I2C1errHandlerImplv");
+ restoreContext();
+}
+
+/**
+ * I2C error interrupt actual implementation
+ */
+void __attribute__((used)) I2C1errHandlerImpl()
+{
+ I2C1->SR1=0; //Clear error flags
+ error=true;
+ if(waiting==0) return;
+ waiting->IRQwakeup();
+ if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+ Scheduler::IRQfindNextThread();
+ waiting=0;
+}
+
+namespace miosix {
+
+//
+// class I2C
+//
+
+I2C1Driver& I2C1Driver::instance()
+{
+ static I2C1Driver singleton;
+ return singleton;
+}
+
+void I2C1Driver::init()
+{
+ //I2C devices are connected to APB1, whose frequency is the system clock
+ //divided by a value set in the PPRE1 bits of RCC->CFGR
+ const int ppre1=(RCC->CFGR & RCC_CFGR_PPRE1)>>10;
+ const int divFactor= (ppre1 & 1<<2) ? (2<<(ppre1 & 0x3)) : 1;
+ const int fpclk1=SystemCoreClock/divFactor;
+ //iprintf("fpclk1=%d\n",fpclk1);
+
+ {
+ FastInterruptDisableLock dLock;
+
+ #ifdef I2C_WITH_DMA
+ RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
+ RCC_SYNC();
+ #endif
+ RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; //Enable clock gating
+ RCC_SYNC();
+ }
+
+ #ifdef I2C_WITH_DMA
+ NVIC_SetPriority(DMA1_Stream7_IRQn,10);//Low priority for DMA
+ NVIC_ClearPendingIRQ(DMA1_Stream7_IRQn);//DMA1 stream 7 channel 1 = I2C1 TX
+ NVIC_EnableIRQ(DMA1_Stream7_IRQn);
+
+ NVIC_SetPriority(DMA1_Stream0_IRQn,10);//Low priority for DMA
+ NVIC_ClearPendingIRQ(DMA1_Stream0_IRQn);//DMA1 stream 0 channel 1 = I2C1 RX
+ NVIC_EnableIRQ(DMA1_Stream0_IRQn);
+ #endif
+
+ NVIC_SetPriority(I2C1_EV_IRQn,10);//Low priority for I2C
+ NVIC_ClearPendingIRQ(I2C1_EV_IRQn);
+ NVIC_EnableIRQ(I2C1_EV_IRQn);
+
+ NVIC_SetPriority(I2C1_ER_IRQn,10);
+ NVIC_ClearPendingIRQ(I2C1_ER_IRQn);
+ NVIC_EnableIRQ(I2C1_ER_IRQn);
+
+ I2C1->CR1=I2C_CR1_SWRST;
+ I2C1->CR1=0;
+ I2C1->CR2=fpclk1/1000000; //Set pclk frequency in MHz
+ //This sets the duration of both Thigh and Tlow (master mode))
+ const int i2cSpeed=100000; //100KHz
+ I2C1->CCR=std::max(4,fpclk1/(2*i2cSpeed)); //Duty=2, standard mode (100KHz)
+ //Datasheet says with I2C @ 100KHz, maximum SCL rise time is 1000ns
+ //Need to change formula if I2C needs to run @ 400kHz
+ I2C1->TRISE=fpclk1/1000000+1;
+ I2C1->CR1=I2C_CR1_PE; //Enable peripheral
+}
+
+bool I2C1Driver::send(unsigned char address,
+ const void *data, int len, bool sendStop)
+{
+ address &= 0xfe; //Mask bit 0, as we are writing
+ if(start(address)==false || (I2C1->SR2 & I2C_SR2_TRA)==0)
+ {
+ I2C1->CR1 |= I2C_CR1_STOP;
+ return false;
+ }
+
+ error=false;
+
+ #ifdef I2C_WITH_DMA
+ waiting=Thread::getCurrentThread();
+ DMA1_Stream7->CR=0;
+ DMA1_Stream7->PAR=reinterpret_cast(&I2C1->DR);
+ DMA1_Stream7->M0AR=reinterpret_cast(data);
+ DMA1_Stream7->NDTR=len;
+ DMA1_Stream7->FCR=DMA_SxFCR_FEIE
+ | DMA_SxFCR_DMDIS;
+ DMA1_Stream7->CR=DMA_SxCR_CHSEL_0 //Channel 1
+ | DMA_SxCR_MINC //Increment memory pointer
+ | DMA_SxCR_DIR_0 //Memory to peripheral
+ | 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 DMA
+
+ //Enable DMA in the I2C peripheral *after* having configured the DMA
+ //peripheral, or a spurious interrupt is triggered
+ I2C1->CR2 |= I2C_CR2_DMAEN | I2C_CR2_ITERREN;
+
+ {
+ FastInterruptDisableLock dLock;
+ while(waiting)
+ {
+ waiting->IRQwait();
+ {
+ FastInterruptEnableLock eLock(dLock);
+ Thread::yield();
+ }
+ }
+ }
+
+ DMA1_Stream7->CR=0;
+
+ //The DMA interrupt routine changes the interrupt flags!
+ I2C1->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITERREN);
+ #else
+
+ I2C1->CR2 |= I2C_CR2_ITERREN;
+
+ const uint8_t *txData = reinterpret_cast(data);
+ for(int i=0; iDR = txData[i];
+ while(!(I2C1->SR1 & I2C_SR1_TXE)) ;
+ }
+
+ I2C1->CR2 &= ~I2C_CR2_ITERREN;
+ #endif
+
+ /*
+ * The main idea of this driver is to avoid having the processor spinning
+ * waiting on some status flag. Why? Because I2C is slow compared to a
+ * modern processor. A 120MHz core does 1200 clock cycles in the time it
+ * takes to transfer a single bit through an I2C clocked at 100KHz.
+ * This time could be better spent doing a context switch and letting
+ * another thread do useful work, or (and Miosix does it automatically if
+ * there are no ready threads) sleeping the processor core. However,
+ * I'm quite disappointed by the STM32 I2C peripheral, as it seems overly
+ * complicated to use. To come close to achieving this goal I had to
+ * orchestrate among *four* interrupt handlers, two of the DMA, and two
+ * of the I2C itself. And in the end, what's even more disappointing, is
+ * that I haven't found a way to completely avoid spinning. Why?
+ * There's no interrupt that's fired when the stop bit is sent!
+ * And what's worse, the documentation says that after you set the stop
+ * bit in the CR2 register you can't write to it again (for example, to send
+ * a start bit because two i2c api calls are made back to back) until the
+ * MSL bit is cleared. But there's no interrupt tied to that event!
+ * What's worse, is that the closest interrupt flag I've found when doing
+ * an I2C send is fired when the last byte is *beginning* to be sent.
+ * Maybe I haven't searched well enough, but the fact is I found nothing,
+ * so this code below spins for 8 data bits of the last byte plus the ack
+ * bit, plus the stop bit. That's 12000 wasted CPU cycles. Thanks, ST...
+ */
+
+ if(sendStop)
+ {
+ I2C1->CR1 |= I2C_CR1_STOP;
+ while(I2C1->SR2 & I2C_SR2_MSL) ; //Wait for stop bit sent
+ } else {
+ // Dummy write, is the only way to clear
+ // the TxE flag if stop bit is not sent...
+ I2C1->DR = 0x00;
+ }
+ return !error;
+}
+
+bool I2C1Driver::recv(unsigned char address, void *data, int len)
+{
+ address |= 0x01;
+ if(start(address,len==1)==false || I2C1->SR2 & I2C_SR2_TRA)
+ {
+ I2C1->CR1 |= I2C_CR1_STOP;
+ return false;
+ }
+
+ error=false;
+ waiting=Thread::getCurrentThread();
+
+ #ifdef I2C_WITH_DMA
+ I2C1->CR2 |= I2C_CR2_DMAEN | I2C_CR2_LAST | I2C_CR2_ITERREN;
+
+ DMA1_Stream0->CR=0;
+ DMA1_Stream0->PAR=reinterpret_cast