前言:本文仅供学习参考使用,主要目的是让大家快速使用串口调试,文章所提及的GCC适用于Clion,Vscode等第三方编辑器的用户。作者有时间会继续更新^_^
一、GCC环境
1、标准库
(1)、使用方法
在主函数while(1)初始化中,添加Serial_Init();
int main(void) {
Serial_Init();
while (1) {
}
}
在代码目录下创建USART文件夹,新建syscalls.c
,sysmem.c
,usart.c
,usart.h
四个文件,工程目录结构如下:
在CMakeLists中添加如下代码:
# include_directories(Core/USART) 根据USART文件夹实际路径进行修改
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,-u_printf_float")
add_link_options( -specs=nosys.specs -specs=nano.specs)
使用u_scanf
,u_printf
来替代scanf
,printf
函数,用法一致。
(2)代码部分
syscalls.c
/**
******************************************************************************
* @file syscalls.c
* @author Auto-generated by STM32CubeIDE
* @brief STM32CubeIDE Minimal System calls file
*
* For more information about which c-functions
* need which of these lowlevel functions
* please consult the Newlib libc-manual
******************************************************************************
* @attention
*
* Copyright (c) 2020-2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Includes */
#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>
/* Variables */
extern int __io_putchar(int ch) __attribute__((weak));
extern int __io_getchar(void) __attribute__((weak));
char *__env[1] = { 0 };
char **environ = __env;
/* Functions */
void initialise_monitor_handles()
{
}
int _getpid(void)
{
return 1;
}
int _kill(int pid, int sig)
{
(void)pid;
(void)sig;
errno = EINVAL;
return -1;
}
void _exit (int status)
{
_kill(status, -1);
while (1) {} /* Make sure we hang here */
}
__attribute__((weak)) int _read(int file, char *ptr, int len)
{
(void)file;
int DataIdx;
for (DataIdx = 0; DataIdx < len; DataIdx++)
{
*ptr++ = __io_getchar();
}
return len;
}
__attribute__((weak)) int _write(int file, char *ptr, int len)
{
(void)file;
int DataIdx;
for (DataIdx = 0; DataIdx < len; DataIdx++)
{
__io_putchar(*ptr++);
}
return len;
}
int _close(int file)
{
(void)file;
return -1;
}
int _fstat(int file, struct stat *st)
{
(void)file;
st->st_mode = S_IFCHR;
return 0;
}
int _isatty(int file)
{
(void)file;
return 1;
}
int _lseek(int file, int ptr, int dir)
{
(void)file;
(void)ptr;
(void)dir;
return 0;
}
int _open(char *path, int flags, ...)
{
(void)path;
(void)flags;
/* Pretend like we always fail */
return -1;
}
int _wait(int *status)
{
(void)status;
errno = ECHILD;
return -1;
}
int _unlink(char *name)
{
(void)name;
errno = ENOENT;
return -1;
}
int _times(struct tms *buf)
{
(void)buf;
return -1;
}
int _stat(char *file, struct stat *st)
{
(void)file;
st->st_mode = S_IFCHR;
return 0;
}
int _link(char *old, char *new)
{
(void)old;
(void)new;
errno = EMLINK;
return -1;
}
int _fork(void)
{
errno = EAGAIN;
return -1;
}
int _execve(char *name, char **argv, char **env)
{
(void)name;
(void)argv;
(void)env;
errno = ENOMEM;
return -1;
}
sysmem.c
/**
******************************************************************************
* @file sysmem.c
* @author Generated by STM32CubeIDE
* @brief STM32CubeIDE System Memory calls file
*
* For more information about which C functions
* need which of these lowlevel functions
* please consult the newlib libc manual
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Includes */
#include <errno.h>
#include <stdint.h>
/**
* Pointer to the current high watermark of the heap usage
*/
static uint8_t *__sbrk_heap_end = NULL;
/**
* @brief _sbrk() allocates memory to the newlib heap and is used by malloc
* and others from the C library
*
* @verbatim
* ############################################################################
* # .data # .bss # newlib heap # MSP stack #
* # # # # Reserved by _Min_Stack_Size #
* ############################################################################
* ^-- RAM start ^-- _end _estack, RAM end --^
* @endverbatim
*
* This implementation starts allocating at the '_end' linker symbol
* The '_Min_Stack_Size' linker symbol reserves a memory for the MSP stack
* The implementation considers '_estack' linker symbol to be RAM end
* NOTE: If the MSP stack, at any point during execution, grows larger than the
* reserved size, please increase the '_Min_Stack_Size'.
*
* @param incr Memory size
* @return Pointer to allocated memory
*/
void *_sbrk(ptrdiff_t incr)
{
extern uint8_t _end; /* Symbol defined in the linker script */
extern uint8_t _estack; /* Symbol defined in the linker script */
extern uint32_t _Min_Stack_Size; /* Symbol defined in the linker script */
const uint32_t stack_limit = (uint32_t)&_estack - (uint32_t)&_Min_Stack_Size;
const uint8_t *max_heap = (uint8_t *)stack_limit;
uint8_t *prev_heap_end;
/* Initialize heap end at first call */
if (NULL == __sbrk_heap_end)
{
__sbrk_heap_end = &_end;
}
/* Protect heap from growing into the reserved MSP stack */
if (__sbrk_heap_end + incr > max_heap)
{
errno = ENOMEM;
return (void *)-1;
}
prev_heap_end = __sbrk_heap_end;
__sbrk_heap_end += incr;
return (void *)prev_heap_end;
}
usart.c
#include "usart.h"
uint8_t rx_buffer[BUFF_SIZE];
uint8_t tx_buffer[1];
volatile uint8_t Serial_RxFlag;
void GPIO_Init_Config(void) {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void USART_Init_Config(void) {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_HardwareFlowControl =
USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
}
void DMA_Init_Config(DMA_Channel_TypeDef *DMA_Channel, uint32_t buffer,
uint32_t direction, uint32_t bufferSize) {
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // Enable DMA1 clock
DMA_DeInit(DMA_Channel); // Reset DMA channel
DMA_InitStructure.DMA_PeripheralBaseAddr =
(u32)(&USART1->DR); // Peripheral base address
DMA_InitStructure.DMA_MemoryBaseAddr = buffer; // Memory base address
DMA_InitStructure.DMA_DIR = direction; // DMA transmit direction
DMA_InitStructure.DMA_BufferSize = bufferSize; // DMA Channel buffer size
DMA_InitStructure.DMA_PeripheralInc =
DMA_PeripheralInc_Disable; // Peripheral address incremented
DMA_InitStructure.DMA_MemoryInc =
DMA_MemoryInc_Enable; // Memory address incremented
DMA_InitStructure.DMA_PeripheralDataSize =
DMA_PeripheralDataSize_Byte; // Peripheral data width
DMA_InitStructure.DMA_MemoryDataSize =
DMA_MemoryDataSize_Byte; // Memory data width
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // DMA Channel mode
DMA_InitStructure.DMA_Priority = DMA_Priority_High; // DMA Channel priority
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // Memory-to-memory transfer
DMA_Init(DMA_Channel, &DMA_InitStructure); // DMA init
DMA_Cmd(DMA_Channel, ENABLE); // Enable DMA channel
}
void NVIC_Init_Config(void) {
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; // IRQ Channel
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =
3; // Preemption Priority
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // SubPriority Priority
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // Enable IRQ Channel
NVIC_Init(&NVIC_InitStructure); // Init NVIC
}
void Serial_Init(void) {
GPIO_Init_Config();
USART_Init_Config();
DMA_Init_Config(DMA1_Channel5, (u32)rx_buffer, DMA_DIR_PeripheralSRC,
BUFF_SIZE);
DMA_Init_Config(DMA1_Channel4, (u32)tx_buffer, DMA_DIR_PeripheralDST, 1);
NVIC_Init_Config();
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); // Enables Idle interrupt
USART_GetFlagStatus(USART1, USART_FLAG_IDLE); // Get Idle flag
USART_ReceiveData(USART1); // Get receive data
USART_Cmd(USART1, ENABLE); // Enable USART1
USART_DMACmd(USART1, USART_DMAReq_Rx | USART_DMAReq_Tx,
ENABLE); // Enable DMA receive/transmit request
}
void Usart_SendByte(uint8_t ch) {
tx_buffer[0] = ch;
DMA_Cmd(DMA1_Channel4, DISABLE); //关闭 USART1 TX DMA1 所指示的通道
DMA_SetCurrDataCounter(DMA1_Channel4, 1); //设置 DMA 缓存的大小
DMA_Cmd(DMA1_Channel4, ENABLE); //使能 USART1 TX DMA1 所指示的通道
//等待发送结束
while (!DMA_GetFlagStatus(DMA1_FLAG_TC4))
;
DMA_ClearFlag(DMA1_FLAG_TC4);
}
void USART_SendString(const char *str) {
unsigned int i = 0;
while (*(str + i) != '\0') {
Usart_SendByte(*(str + i));
i++;
}
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
;
}
void USART1_IRQHandler(void) {
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
USART_ClearFlag(USART1, USART_FLAG_RXNE);
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
if (USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) // Idle interrupt set
{
USART_GetITStatus(USART1, USART_IT_IDLE); // Get idle interrupt state
USART_ReceiveData(USART1); // Get USARTx received data
DMA_Cmd(DMA1_Channel5, DISABLE); // Disable DMA channel
DMA_SetCurrDataCounter(DMA1_Channel5, BUFF_SIZE); // Set data count
DMA_Cmd(DMA1_Channel5, ENABLE); // Enable DMA channel
Serial_RxFlag = 1;
}
}
char *find_next_space_or_end(char *str) {
while (*str != ' ' && *str != '\0') {
str++;
}
return str;
}
int str_to_num(char *start, char *end) {
char temp[50];
strncpy(temp, start, end - start);
temp[end - start] = '\0';
return atoi(temp);
}
double str_to_double(char *start, char *end) {
char temp[50];
strncpy(temp, start, end - start);
temp[end - start] = '\0';
return atof(temp);
}
void u_scanf(char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
char *start = rx_buffer;
char *end;
while (Serial_RxFlag == 0) {
}
Serial_RxFlag = 0;
while (*fmt != '\0') {
if (*fmt == '%') {
fmt++;
end = find_next_space_or_end(start);
switch (*fmt) {
case 'd': {
int *ip = va_arg(ap, int *);
*ip = str_to_num(start, end);
} break;
case 'l': {
++fmt;
if (*fmt == 'f') {
double *fp = va_arg(ap, double *);
*fp = str_to_double(start, end);
}
} break;
case 'f': {
float *fp = va_arg(ap, float *);
*fp = (float)str_to_double(start, end);
} break;
case 'c': {
int *cp = va_arg(ap, int *);
*cp = start[0];
} break;
case 's': {
char *sp = va_arg(ap, char *);
strcpy(sp, rx_buffer);
} break;
}
start = end + 1;
}
fmt++;
}
va_end(ap);
}
void u_printf(const char *format, ...) {
va_list args;
uint8_t buff[BUFF_SIZE];
va_start(args, format);
vsprintf(buff, format, args);
USART_SendString(buff);
va_end(args);
}
usart.h
#ifndef __USART_H
#define __USART_H
#include "stdlib.h"
#include "stm32f10x.h"
#include "stm32f10x_dma.h"
#include "string.h"
#include <math.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/types.h>
#define BUFF_SIZE 256
void Serial_Init(void);
void u_printf(const char *format, ...);
void u_scanf(char *fmt, ...);
#endif
(3)总结
可以看出在代码中u_scanf
比较繁琐,使用自定义函数来实现,并未使用自带的vsscanf来重构,如果你想要使用vsscanf的话,还需要添加-u_scanf_float
链接标志(同上),但是vsscanf函数会导致输入字符串时无法正常读取空格.
以上版本存在问题,请替换usart.c
,usart.h
为如下代码,使用方法直接使用printf以及scanf,注意在CMakeLists中添加-u_scanf_float
链接标志
usart.c
#include "usart.h"
/*根据需要取消注释对应宏定义*/
#define DBUG_1
//#define DBUG_2
#if defined(DBUG_1)
#define DBUG_USART USART1
#define DBUG_PORT GPIOA
#define RX_PIN GPIO_Pin_10
#define TX_PIN GPIO_Pin_9
#define RCC_APB2Periph_DBUG_PORT RCC_APB2Periph_GPIOA
#define DBUG_RX_CHANNEL DMA1_Channel5
#define DBUG_TX_CHANNEL DMA1_Channel4
#define DBUG_RX_FLAG DMA1_FLAG_TC5
#define DBUG_TX_FLAG DMA1_FLAG_TC4
#elif defined(DBUG_2)
#define DBUG_USART USART2
#define DBUG_PORT GPIOA
#define RX_PIN GPIO_Pin_3
#define TX_PIN GPIO_Pin_2
#define RCC_APB2Periph_DBUG_PORT RCC_APB2Periph_GPIOA
#define DBUG_RX_CHANNEL DMA1_Channel6
#define DBUG_TX_CHANNEL DMA1_Channel7
#define DBUG_RX_FLAG DMA1_FLAG_TC6
#define DBUG_TX_FLAG DMA1_FLAG_TC7
#endif
uint8_t rx_buffer[1];
uint8_t tx_buffer[1];
volatile uint8_t Serial_RxFlag;
void GPIO_Init_Config(void) {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_DBUG_PORT, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = TX_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DBUG_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = RX_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DBUG_PORT, &GPIO_InitStructure);
}
void USART_Init_Config(void) {
#if defined(DBUG_1)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
#elif defined(DBUG_2)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
#endif
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_HardwareFlowControl =
USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(DBUG_USART, &USART_InitStructure);
USART_Cmd(DBUG_USART, ENABLE);
}
void DMA_Init_Config(DMA_Channel_TypeDef *DMA_Channel, uint32_t buffer,
uint32_t direction, uint32_t bufferSize) {
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA_Channel);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&DBUG_USART->DR);
DMA_InitStructure.DMA_MemoryBaseAddr = buffer;
DMA_InitStructure.DMA_DIR = direction;
DMA_InitStructure.DMA_BufferSize = bufferSize;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA_Channel, &DMA_InitStructure);
DMA_Cmd(DMA_Channel, ENABLE);
}
void Serial_Init(void) {
GPIO_Init_Config();
USART_Init_Config();
DMA_Init_Config(DBUG_RX_CHANNEL, (u32)rx_buffer, DMA_DIR_PeripheralSRC, 1);
DMA_Init_Config(DBUG_TX_CHANNEL, (u32)tx_buffer, DMA_DIR_PeripheralDST, 1);
USART_Cmd(DBUG_USART, ENABLE);
USART_DMACmd(DBUG_USART, USART_DMAReq_Rx | USART_DMAReq_Tx, ENABLE);
setvbuf(stdin, NULL, _IONBF, 0);
}
#if (defined(__GNUC__) && !defined(__CC_ARM))
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#define GETCHAR_PROTOTYPE int __io_getchar(void)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#define GETCHAR_PROTOTYPE int fgetc(FILE *f)
#endif /* __GNUC__ */
PUTCHAR_PROTOTYPE {
*tx_buffer = ch;
while (DMA_GetCurrDataCounter(DBUG_TX_CHANNEL) != 0) {
};
DMA_Cmd(DBUG_TX_CHANNEL, DISABLE);
DMA_SetCurrDataCounter(DBUG_TX_CHANNEL, 1);
DMA_Cmd(DBUG_TX_CHANNEL, ENABLE);
while (!DMA_GetFlagStatus(DBUG_TX_FLAG))
;
DMA_ClearFlag(DBUG_TX_FLAG);
return ch;
}
GETCHAR_PROTOTYPE {
DMA_Cmd(DBUG_RX_CHANNEL, DISABLE);
DMA_SetCurrDataCounter(DBUG_RX_CHANNEL, 1);
DMA_Cmd(DBUG_RX_CHANNEL, ENABLE);
while (!DMA_GetFlagStatus(DBUG_RX_FLAG))
;
DMA_ClearFlag(DBUG_RX_FLAG);
return *rx_buffer;
}
usart.h
#ifndef __USART_H
#define __USART_H
#include "delay.h"
#include "stdlib.h"
#include "stm32f10x.h"
#include "stm32f10x_dma.h"
#include "string.h"
#include <math.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/types.h>
void Serial_Init(void);
#endif