前言:本文仅供学习参考使用,主要目的是让大家快速使用串口调试,文章所提及的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 clockDMA_DeInit(DMA_Channel); // Reset DMA channelDMA_InitStructure.DMA_PeripheralBaseAddr =(u32)(&USART1->DR); // Peripheral base addressDMA_InitStructure.DMA_MemoryBaseAddr = buffer; // Memory base addressDMA_InitStructure.DMA_DIR = direction; // DMA transmit directionDMA_InitStructure.DMA_BufferSize = bufferSize; // DMA Channel buffer sizeDMA_InitStructure.DMA_PeripheralInc =DMA_PeripheralInc_Disable; // Peripheral address incrementedDMA_InitStructure.DMA_MemoryInc =DMA_MemoryInc_Enable; // Memory address incrementedDMA_InitStructure.DMA_PeripheralDataSize =DMA_PeripheralDataSize_Byte; // Peripheral data widthDMA_InitStructure.DMA_MemoryDataSize =DMA_MemoryDataSize_Byte; // Memory data widthDMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // DMA Channel modeDMA_InitStructure.DMA_Priority = DMA_Priority_High; // DMA Channel priorityDMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // Memory-to-memory transferDMA_Init(DMA_Channel, &DMA_InitStructure); // DMA initDMA_Cmd(DMA_Channel, ENABLE); // Enable DMA channel
}void NVIC_Init_Config(void) {NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; // IRQ ChannelNVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =3; // Preemption PriorityNVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // SubPriority PriorityNVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // Enable IRQ ChannelNVIC_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 interruptUSART_GetFlagStatus(USART1, USART_FLAG_IDLE); // Get Idle flagUSART_ReceiveData(USART1); // Get receive dataUSART_Cmd(USART1, ENABLE); // Enable USART1USART_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 stateUSART_ReceiveData(USART1); // Get USARTx received dataDMA_Cmd(DMA1_Channel5, DISABLE); // Disable DMA channelDMA_SetCurrDataCounter(DMA1_Channel5, BUFF_SIZE); // Set data countDMA_Cmd(DMA1_Channel5, ENABLE); // Enable DMA channelSerial_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函数会导致输入字符串时无法正常读取空格.