stm32f gnu c++ 工程模板
由于工作上的原因,最近需要使用C++与编写stm32f030的程序,在此记录一下,以便后续查找。 背景: MCU:STM32F030 环境:ubuntu-20.04 编译器:gcc-arm-none-eabi-6-2017-q2-update/bin/arm-none-eabi-g++ gcc-arm-none-eabi-6-2017-q2-update/bin/arm-none-eabi-gcc 问题: 1 printf 问题 非半主机模式下的调试输出主要靠打印,所以选择使用printf的情况居多。在编写C代码时,可以直接使用gcc编译,不过需要以下支持才可正常打印。 可以将这些代码直接存为syscall.c
#pragma import(__use_no_semihosting)
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <sys/times.h>
extern int errno;
extern int __io_putchar(int ch) __attribute__((weak));
extern int __io_getchar(void) __attribute__((weak));
register char * stack_ptr asm("sp");
char *__env[1] = { 0 };
char **environ = __env;
void initialise_monitor_handles()
{
}
int _getpid(void)
{
return 1;
}
int _kill(int pid, int sig)
{
errno = EINVAL;
return -1;
}
void _exit (int status)
{
_kill(status, -1);
while (1) {}
}
__attribute__((weak)) int _read(int file, char *ptr, int len)
{
int DataIdx;
for (DataIdx = 0; DataIdx < len; DataIdx++)
{
*ptr++ = __io_getchar();
}
return len;
}
__attribute__((weak)) int _write(int file, char *ptr, int len)
{
int DataIdx;
for (DataIdx = 0; DataIdx < len; DataIdx++)
{
__io_putchar(*ptr++);
}
return len;
}
caddr_t _sbrk(int incr)
{
extern char end asm("end");
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)
{
errno = ENOMEM;
return (caddr_t) -1;
}
heap_end += incr;
return (caddr_t) prev_heap_end;
}
int _close(int file)
{
return -1;
}
int _fstat(int file, struct stat *st)
{
st->st_mode = S_IFCHR;
return 0;
}
int _isatty(int file)
{
return 1;
}
int _lseek(int file, int ptr, int dir)
{
return 0;
}
int _open(char *path, int flags, ...)
{
return -1;
}
int _wait(int *status)
{
errno = ECHILD;
return -1;
}
int _unlink(char *name)
{
errno = ENOENT;
return -1;
}
int _times(struct tms *buf)
{
return -1;
}
int _stat(char *file, struct stat *st)
{
st->st_mode = S_IFCHR;
return 0;
}
int _link(char *old, char *new)
{
errno = EMLINK;
return -1;
}
int _fork(void)
{
errno = EAGAIN;
return -1;
}
int _execve(char *name, char **argv, char **env)
{
errno = ENOMEM;
return -1;
}
连接芯片的串口输出有两种方式,任选其一都可以正常使用。 代码如下,可以放在任意位置:
方法一:
extern "C" int _write(int file, char *data, int len)
{
if ((file != STDOUT_FILENO) && (file != STDERR_FILENO))
{
errno = EBADF;
return -1;
}
int cntWait = 100000;
char *pdata=data;
pdata=data;
for(int i=0;i<len;i++)
{
USART_SendData(USART2,*pdata);
while((USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET) && (cntWait-- >0));
pdata++;
}
return len;
}
方法二:
extern "C" int __io_putchar(int ch)
{
int cntWait = 100000;
USART_SendData(USART2,ch);
while((USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET) && (cntWait-- >0));
return 0;
}
2 cout 输出问题 由于MCU的资源有限,直接使用标准库,会导致代码过大,所以为减小代码尺寸,需要自行实现cout代码,不再调用iostream库。相关原理不再细述,如需要的话,可以查看相关操作符重载章节,实现代码具体如下:
myoutstream.h
#ifndef _MYOUTSTREAM_H_
#define _MYOUTSTREAM_H_
class MyOutStream
{
public:
MyOutStream(){};
~MyOutStream(){};
const MyOutStream & operator<< (int value) const;
const MyOutStream & operator<< (char *str) const;
const MyOutStream & operator<< (void (*cf_newline)()) const;
private:
};
void endl(void);
#endif
函数实现代码如下:
myostream.cpp
#include "myOutStream.h"
#include "stdio.h"
const MyOutStream& MyOutStream::operator << (int value) const
{
printf("%d",value);
return *this;
}
const MyOutStream& MyOutStream::operator << (char *str) const
{
printf("%s",str);
return *this;
}
const MyOutStream & MyOutStream::operator<< (void (*cf_newline)()) const
{
cf_newline();
return *this;
}
void endl(void)
{
printf("\r\n");
}
3 工具链配置问题 由于使用 arm-none-eabi-g++/arm-none-eabi-gcc 工具链,需要对库目录路径,编译工具路径等等进行配置,否则调用相关工具及头文件较繁琐,具体实现为使用一个小的bash脚本处理,具体如下:
将以下代码存成 export.sh文件
#!/bin/bash
root_dir=$(pwd)
echo "current path = $root_dir"
gcc_path=/home/murphy/gcc-arm-none-eabi-6-2017-q2-update/bin
include_path=$root_dir/arch/stm32f0xx/STM32F0xx_HAL_Driver/Inc:$gcc_path
include_path=$root_dir/arch/stm32f0xx/STM32F0xx_HAL_Driver/Inc/Legacy:$include_path
include_path=$root_dir/arch/stm32f0xx/CMSIS:$include_path
include_path=$root_dir/arch/stm32f0xx/CMSIS/Include:$include_path
include_path=$root_dir/src:$include_path
echo $include_path
export PATH=:$include_path:$PATH
echo "export enviroment path success"
4 makefile 问题 由于使用make工具来进行处理,暂时未专门优化makefile文件,仅做到能用而已。如对输出目录,中间文件目录、工程目录有详细要求,需要对另外编写专用的makefile文件。本次使用的代码如下:
TARGET := main
ROOT_DIR := $(shell pwd)
OBJS_DIR := objects
SRCS := $(shell find ./ -name "*.c")
SRCS += $(shell find ./ -name "*.s")
SRCS += $(shell find ./ -name "*.cpp")
OBJS:=$(SRCS:.c=.o)
OBJS:=$(OBJS:.s=.o)
OBJS:=$(OBJS:.cpp=.o)
SSUA:=$(OBJS:.o=.su)
CC=arm-none-eabi-gcc
OC=arm-none-eabi-objcopy
CPP=arm-none-eabi-g++
OFLAGS := -mcpu=cortex-m0 -mthumb -O0 -ffunction-sections -fdata-sections -fstack-usage
WFLAGS := -Wall -Wswitch-enum -Wextra
CFLAGS += $(OFLAGS) $(WFLAGS)
CFLAGS += -std=gnu11 -specs=nano.specs
CFLAGS += -DSTM32F030x8 -DUSE_STDPERIPH_DRIVER -DSTM32F030
CFLAGS += -D__weak="__attribute__((weak))" -D__packed="__attribute__((__packed__))"
CFLAGS += -fno-builtin-printf
CPPFLAGS = $(OFLAGS) $(WFLAGS)
CPPFLAGS += -specs=nano.specs
CPPFLAGS += -DSTM32F030x8 -DUSE_STDPERIPH_DRIVER -DSTM32F030
CPPFLAGS += -D__weak="__attribute__((weak))" -D__packed="__attribute__((__packed__))"
LDFLAGS :=-T STM32F030R8_FLASH.ld -specs=nosys.specs -static -Wl,-Map=$(TARGET).map -Wl,--gc-sections -Wl,--defsym=malloc_getpagesize_P=0x80 -Wl,--start-group -lc -lm -Wl,--end-group -specs=nano.specs
INCLUDE_PATH := -I $(ROOT_DIR)/arch/stm32f0xx/CMSIS/
INCLUDE_PATH += -I $(ROOT_DIR)/arch/stm32f0xx/CMSIS/Include/
INCLUDE_PATH += -I $(ROOT_DIR)/arch/stm32f0xx/STM32F0xx_StdPeriph_Driver/inc/
INCLUDE_PATH += -I $(ROOT_DIR)/src/
all: $(TARGET).hex
%.o:%.s
$(CC) -c -x assembler-with-cpp $(OFLAGS) -o $@ $<
%.o:%.c
$(CC) -c $(CFLAGS) -o $@ $^ $(INCLUDE_PATH)
%.o:%.cpp
$(CPP) -c $(CPPFLAGS) -o $@ $^ $(INCLUDE_PATH)
%.hex:%.bin
$(OC) -I binary -O ihex --change-addresses 0x8000000 $< $@
%.bin:%.elf
$(OC) -O binary $< $@
$(TARGET).elf: $(OBJS)
@echo "Linking $@ ..."
$(CPP) -o $@ $^ $(OFLAGS) $(LDFLAGS)
clean:
rm -f $(OBJS) $(SSUA) $(TARGET).bin $(TARGET).hex $(TARGET).elf $(TARGET).map
print:
@echo "SRCs =" $(SRCS)
@echo "OBJs =" $(OBJS)
OK,以上为环境搭建时的一些主要问题。记录在此,以供查阅。 不知道如何上传附件,郁闷ing。。。
|