说起来,本来只是想尝个鲜,看看cubeIDE是怎么用的,结果随手做个实验freeRTOS+串口的实验就跌进了坑,花了我一个星期往外爬,在此做个记录。
cubeIDE使用起来。感觉很多初始化代码出比较快,一些资源使用也很直观,但是内部的SDK使用起来还是有些坑,并不是那么的完善。
看看这些吐槽,哈哈哈。
哈哈哈欢乐,SDK团队也不容易啊,一直受到全球人民的鞭策。
以前都是很信任官方SDK的,自从我转来做SDK之后,才发现bug也不少,毕竟也是人写的。只不过版本控制和代码review、merge要严格很多。
多一些宽容多一些理解多一些等待,SDK和客户一起成长 ~
想用cubeIDE在freeRTOS下printf浮点数,你需要按照下面这么几个步骤来做:
- 驱动串口(图形化引脚配置,cubeIDE的驱动代码生成)
- 使用printf(串口重定向)
- 使用printf输出浮点数 (菜单勾选)
- 在freeRTOS中使用printf (修改task默认堆栈大小,默认128 word小了,会进hardfault)
- 怎么使用printf在freeRTOS中输出浮点数 (本文内容,增加第三方提供的补丁)
其中,1、2、3、4都是很常规的操作,在网上能搜到不少教程和帖子。
实在不行可以看我的上一篇
一般来说,到这里就应该结束了,没想到让我爬了一个星期。
(至少截止目前,2020.7.16,cubeIDE v1.3.0版本)依然存在在freeRTOS下,线程中使用printf、USB库等接口的异常。
因为这些接口使用了malloc等接口,而不是freeRTOS提供的有线程保护的pvPortMalloc等接口,ST官方自己实现的_sbrk函数有些问题(sysmem.c里),导致线程中一些调用了系统自身malloc的函数接口出问题。
这里我们使用第三种方法。
具体步骤如下:
- 添加heap_useNewlib.c(可以在文末复制)
- 把sysmem.c 和 FreeRTOS里的heapX.c从编译内容中排除。点击文件,右键Resource configuration - exclude from build
- ioc文件中的freeRTOS USE_NEWLIB_REENTRANT 选择enable.
ST的bug是怎么形成的?
heap_useNewlib.c做了些什么?
另外附上heap_useNewlib.c全文,出自参考链接一。
/**
* \file heap_useNewlib.c
* \brief Wrappers required to use newlib malloc-family within FreeRTOS.
*
* \par Overview
* Route FreeRTOS memory management functions to newlib's malloc family.
* Thus newlib and FreeRTOS share memory-management routines and memory pool,
* and all newlib's internal memory-management requirements are supported.
*
* \author Dave Nadler
* \date 7-August-2019
* \version 23-Sep-2019 comments, check no malloc call inside ISR
*
* \see http://www.nadler.com/embedded/newlibAndFreeRTOS.html
* \see https://sourceware.org/newlib/libc.html#Reentrancy
* \see https://sourceware.org/newlib/libc.html#malloc
* \see https://sourceware.org/newlib/libc.html#index-_005f_005fenv_005flock
* \see https://sourceware.org/newlib/libc.html#index-_005f_005fmalloc_005flock
* \see https://sourceforge.net/p/freertos/feature-requests/72/
* \see http://www.billgatliff.com/newlib.html
* \see http://wiki.osdev.org/Porting_Newlib
* \see http://www.embecosm.com/appnotes/ean9/ean9-howto-newlib-1.0.html
*
*
* \copyright
* (c) Dave Nadler 2017-2019, All Rights Reserved.
* Web: http://www.nadler.com
* email: [email protected]
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Use or redistributions of source code must retain the above copyright notice,
* this list of conditions, ALL ORIGINAL COMMENTS, and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// ================================================================================================
// ======================================= Configuration ========================================
// These configuration symbols could be provided by from build...
#define STM_VERSION // use STM standard exported LD symbols
//#define SUPPORT_MALLOCS_INSIDE_ISRs // #define iff you have this crap code (ie unrepaired STM USB CDC)
#define SUPPORT_ISR_STACK_MONITOR // #define to enable ISR (MSP) stack diagnostics
#define ISR_STACK_LENGTH_BYTES 512 // #define bytes to reserve for ISR (MSP) stack
// ======================================= Configuration ========================================
// ================================================================================================
#include // maps to newlib...
#include // mallinfo...
#include // ENOMEM
#include
#include
#include "newlib.h"
#if (__NEWLIB__ != 3) || (__NEWLIB_MINOR__ != 0)
#warning "This wrapper was verified for newlib version 3.0.0; please ensure newlib's external requirements for malloc-family are unchanged!"
#endif
#include "freeRTOS.h" // defines public interface we're implementing here
#if !defined(configUSE_NEWLIB_REENTRANT) || (configUSE_NEWLIB_REENTRANT!=1)
#warning "#define configUSE_NEWLIB_REENTRANT 1 // Required for thread-safety of newlib sprintf, strtok, etc..."
// If you're *REALLY* sure you don't need FreeRTOS's newlib reentrancy support, remove this warning...
#endif
#include "task.h"
// ================================================================================================
// External routines required by newlib's malloc (sbrk/_sbrk, __malloc_lock/unlock)
// ================================================================================================
// Simplistic sbrk implementations assume stack grows downwards from top of memory,
// and heap grows upwards starting just after BSS.
// FreeRTOS normally allocates task stacks from a pool placed within BSS or DATA.
// Thus within a FreeRTOS task, stack pointer is always below end of BSS.
// When using this module, stacks are allocated from malloc pool, still always prior
// current unused heap area...
#if 0 // STM CubeMX 2018-2019 Incorrect Implementation (fails for FreeRTOS)
caddr_t _sbrk(int incr)
{
extern char end asm("end"); // lowest unused RAM address, just beyond end of BSS.
static char *heap_end;
char *prev_heap_end;
if (heap_end == 0) heap_end = &end;
prev_heap_end = heap_end;
if (heap_end + incr > stack_ptr) // of course, always true for FreeRTOS task stacks
{
errno = ENOMEM; // ...so first call inside a FreeRTOS task lands here
return (caddr_t) -1;
}
heap_end += incr;
return (caddr_t) prev_heap_end;
}
#endif
register char * stack_ptr asm("sp");
#ifdef STM_VERSION // Use STM CubeMX LD symbols for heap+stack area
// To avoid modifying STM LD file (and then having CubeMX trash it), use available STM symbols
// Unfortunately STM does not provide standardized markers for RAM suitable for heap!
// STM CubeMX-generated LD files provide the following symbols:
// end /* aligned first word beyond BSS */
// _estack /* one word beyond end of "RAM" Ram type memory, for STM32F429 0x20030000 */
// Kludge below uses CubeMX-generated symbols instead of sane LD definitions
#define __HeapBase end
#define __HeapLimit _estack // except in K64F this was already adjusted in LD for stack...
static int heapBytesRemaining;
// no DRN HEAP_SIZE symbol from LD... // that's (&__HeapLimit)-(&__HeapBase)
uint32_t TotalHeapSize; // publish for diagnostic routines; filled in first _sbrk call.
#else
// Note: DRN's K64F LD provided: __StackTop (byte beyond end of memory), __StackLimit, HEAP_SIZE, STACK_SIZE
// __HeapLimit was already adjusted to be below reserved stack area.
extern char HEAP_SIZE; // make sure to define this symbol in linker LD command file
static int heapBytesRemaining = (int)&HEAP_SIZE; // that's (&__HeapLimit)-(&__HeapBase)
#endif
#ifdef MALLOCS_INSIDE_ISRs // STM code to avoid malloc within ISR (USB CDC stack)
// We can't use vTaskSuspendAll() within an ISR.
// STM's stunningly bad coding malpractice calls malloc within ISRs (for example, on USB connect function USBD_CDC_Init)
// So, we must just suspend/resume interrupts, lengthening max interrupt response time, aarrggg...
#define DRN_ENTER_CRITICAL_SECTION(_usis) { _usis = taskENTER_CRITICAL_FROM_ISR(); } // Disables interrupts (after saving prior state)
#define DRN_EXIT_CRITICAL_SECTION(_usis) { taskEXIT_CRITICAL_FROM_ISR(_usis); } // Re-enables interrupts (unless already disabled prior taskENTER_CRITICAL)
#else
#define DRN_ENTER_CRITICAL_SECTION(_usis) vTaskSuspendAll(); // Note: safe to use before FreeRTOS scheduler started, but not in ISR
#define DRN_EXIT_CRITICAL_SECTION(_usis) xTaskResumeAll(); // Note: safe to use before FreeRTOS scheduler started, but not in ISR
#endif
#ifndef NDEBUG
static int totalBytesProvidedBySBRK = 0;
#endif
extern char __HeapBase, __HeapLimit; // make sure to define these symbols in linker LD command file
//! _sbrk_r version supporting reentrant newlib (depends upon above symbols defined by linker control file).
void * _sbrk_r(struct _reent *pReent, int incr) {
#ifdef MALLOCS_INSIDE_ISRs // block interrupts during free-storage use
UBaseType_t usis; // saved interrupt status
#endif
static char *currentHeapEnd = &__HeapBase;
#ifdef STM_VERSION // Use STM CubeMX LD symbols for heap
if(TotalHeapSize==0) {
TotalHeapSize = heapBytesRemaining = (int)((&__HeapLimit)-(&__HeapBase))-ISR_STACK_LENGTH_BYTES;
};
#endif
char* limit = (xTaskGetSchedulerState()==taskSCHEDULER_NOT_STARTED) ?
stack_ptr : // Before scheduler is started, limit is stack pointer (risky!)
&__HeapLimit-ISR_STACK_LENGTH_BYTES; // Once running, OK to reuse all remaining RAM except ISR stack (MSP) stack
DRN_ENTER_CRITICAL_SECTION(usis);
char *previousHeapEnd = currentHeapEnd;
if (currentHeapEnd + incr > limit) {
// Ooops, no more memory available...
#if( configUSE_MALLOC_FAILED_HOOK == 1 )
{
extern void vApplicationMallocFailedHook( void );
DRN_EXIT_CRITICAL_SECTION(usis);
vApplicationMallocFailedHook();
}
#elif defined(configHARD_STOP_ON_MALLOC_FAILURE)
// If you want to alert debugger or halt...
// WARNING: brkpt instruction may prevent watchdog operation...
while(1) { __asm("bkpt #0"); }; // Stop in GUI as if at a breakpoint (if debugging, otherwise loop forever)
#else
// Default, if you prefer to believe your application will gracefully trap out-of-memory...
pReent->_errno = ENOMEM; // newlib's thread-specific errno
DRN_EXIT_CRITICAL_SECTION(usis);
#endif
return (char *)-1; // the malloc-family routine that called sbrk will return 0
}
// 'incr' of memory is available: update accounting and return it.
currentHeapEnd += incr;
heapBytesRemaining -= incr;
#ifndef NDEBUG
totalBytesProvidedBySBRK += incr;
#endif
DRN_EXIT_CRITICAL_SECTION(usis);
return (char *) previousHeapEnd;
}
//! non-reentrant sbrk uses is actually reentrant by using current context
// ... because the current _reent structure is pointed to by global _impure_ptr
char * sbrk(int incr) { return _sbrk_r(_impure_ptr, incr); }
//! _sbrk is a synonym for sbrk.
char * _sbrk(int incr) { return sbrk(incr); };
#ifdef MALLOCS_INSIDE_ISRs // block interrupts during free-storage use
static UBaseType_t malLock_uxSavedInterruptStatus;
#endif
void __malloc_lock(struct _reent *r) {
#if defined(MALLOCS_INSIDE_ISRs)
DRN_ENTER_CRITICAL_SECTION(malLock_uxSavedInterruptStatus);
#else
bool insideAnISR = xPortIsInsideInterrupt();
configASSERT( !insideAnISR ); // Make damn sure no more mallocs inside ISRs!!
vTaskSuspendAll();
#endif
};
void __malloc_unlock(struct _reent *r) {
#if defined(MALLOCS_INSIDE_ISRs)
DRN_EXIT_CRITICAL_SECTION(malLock_uxSavedInterruptStatus);
#else
(void)xTaskResumeAll();
#endif
};
// newlib also requires implementing locks for the application's environment memory space,
// accessed by newlib's setenv() and getenv() functions.
// As these are trivial functions, momentarily suspend task switching (rather than semaphore).
// ToDo: Move __env_lock/unlock to a separate newlib helper file.
void __env_lock() { vTaskSuspendAll(); };
void __env_unlock() { (void)xTaskResumeAll(); };
#if 1 // Provide malloc debug and accounting wrappers
/// /brief Wrap malloc/malloc_r to help debug who requests memory and why.
/// To use these, add linker options: -Xlinker --wrap=malloc -Xlinker --wrap=_malloc_r
// Note: These functions are normally unused and stripped by linker.
int TotalMallocdBytes;
int MallocCallCnt;
static bool inside_malloc;
void *__wrap_malloc(size_t nbytes) {
extern void * __real_malloc(size_t nbytes);
MallocCallCnt++;
TotalMallocdBytes += nbytes;
inside_malloc = true;
void *p = __real_malloc(nbytes); // will call malloc_r...
inside_malloc = false;
return p;
};
void *__wrap__malloc_r(void *reent, size_t nbytes) {
extern void * __real__malloc_r(size_t nbytes);
if(!inside_malloc) {
MallocCallCnt++;
TotalMallocdBytes += nbytes;
};
void *p = __real__malloc_r(nbytes);
return p;
};
#endif
// ================================================================================================
// Implement FreeRTOS's memory API using newlib-provided malloc family.
// ================================================================================================
void *pvPortMalloc( size_t xSize ) PRIVILEGED_FUNCTION {
void *p = malloc(xSize);
return p;
}
void vPortFree( void *pv ) PRIVILEGED_FUNCTION {
free(pv);
};
size_t xPortGetFreeHeapSize( void ) PRIVILEGED_FUNCTION {
struct mallinfo mi = mallinfo(); // available space now managed by newlib
return mi.fordblks + heapBytesRemaining; // plus space not yet handed to newlib by sbrk
}
// GetMinimumEverFree is not available in newlib's malloc implementation.
// So, no implementation is provided: size_t xPortGetMinimumEverFreeHeapSize( void ) PRIVILEGED_FUNCTION;
//! No implementation needed, but stub provided in case application already calls vPortInitialiseBlocks
void vPortInitialiseBlocks( void ) PRIVILEGED_FUNCTION {};