Stm32串口搭配DMA实现自定义printf、scanf

前言:本文仅供学习参考使用,主要目的是让大家快速使用串口调试,文章所提及的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函数会导致输入字符串时无法正常读取空格.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/12088.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

柯桥法语学习-5大法语听写网站:全力助攻你的dictée!

提到法语dicte&#xff0c;绝对可是法语学生们的老大难&#xff0c;简直就是心痛得不能自已啊&#xff01;所以今天&#xff0c;法语君整理了5个听写网站助攻大家的dicte哦&#xff01; Projet Voltaire 01 一个很容易让你对dicte上瘾的APP 写邮件、实习报告或者动机信时&…

Nat Plants | 植物抽核单细胞!多组学探究大豆根瘤成熟过程

发表时间&#xff1a;2023-04 发表期刊&#xff1a;Nature Plants 影响因子&#xff1a;17.352 DOI&#xff1a;10.1038/s41477-023-01387-z 研究背景 根瘤菌是亲和互作寄主植物&#xff0c;感染宿主并在根部形成共生器官根瘤&#xff0c;具有固氮…

jmeter中HttpClient4发送失败,java方法请求成功

jmeter中HttpClient4请求失败 上传文件时&#xff1a;Httpclient4: 请求体 请求头 响应结果 ,后端服务都总是提示存在非法标签。 jmeter中使用java请求成功 修改使用java方式&#xff0c;访问正常&#xff1b; 根据分析可能因为HC4对一些特殊字符会进行转义&#xff0c;转义后…

idea配置MySQL提示

点击sql语句&#xff0c;然后再选择show context actions 然后再选择Inject language or reference 然后再选择MySQL 然后我们会发现sql语句变颜色了 如果表是红色 那么需要我们连接mysql的对于的数据库

做私域不止是积累流量,生态也很重要!

如今&#xff0c;私域流水占比已经逼近整个零售市场的30%&#xff0c;达到4万亿规模&#xff0c;百度、阿里、腾讯等头部玩家也都在加速布局&#xff0c;私域运营&#xff0c;已不再是一个单一的商业模式或者运营手段&#xff0c;而是逐渐构成一种可持续的行业生态。 一、什么…

全球量子计算已开始商业化!应用最多的行业你一定想不到

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨卉可 排版丨沛贤 深度好文&#xff1a;2600字丨5 分钟阅读 01 量子计算是一场高风险的游戏 近日&#xff0c;PsiQuantum从澳大利亚联邦政府和地方政府获得了10亿澳元资金&#xff0c;这…

大屏UI:建筑可视化应用越来越广泛,根本挡不住。

建筑可视化在可视化大屏中有许多应用场景&#xff0c;以下是其中一些常见的应用场景&#xff1a; 建筑项目展示&#xff1a;可以使用建筑可视化技术展示正在进行或已完成的建筑项目。通过可视化大屏&#xff0c;可以展示建筑的外观、内部布局、材料选择等信息&#xff0c;帮助…

鬼畜作品创作必备素材,鬼畜自学语音包合集

一、素材描述 鬼畜是什么&#xff1f;鬼畜是一种网络流行语&#xff0c;也是网络文化的一种表现形式。它指的是将原本无关的两个或多个视频、音频、图片或文字进行剪辑、混合、重组等处理后&#xff0c;形成一种新的有趣、诙谐或恶搞的作品。鬼畜的制作过程通常需要一定的技术…

图像归一化处理

归一化 归一化是一种简化计算的方式&#xff0c;即将有量纲的表达式&#xff0c;经过变换&#xff0c;化为无量纲的表达式&#xff0c;成为标量。 在多种计算中都经常用到这种方法。 简单介绍 归一化是一种无量纲处理手段&#xff0c;使物理系统数值的绝对值变成某种相对值关…

C++哈希(个人笔记)

C哈希 1.unordered_mapd1.1unordered_map的构造函数1.2unorder_map的容量1.3unordered_map的迭代器1.4unordered_map的元素访问1.5unorder_map的查找1.6unordered_map的修改操作1.7unordered_map的桶操作 2.unordered_set3.unordered_set和unordered_set的笔试题4.哈希4.1哈希概…

应急救灾北斗终端手机应用方案

在应对自然灾害和紧急救援的严峻挑战中&#xff0c;技术的力量从未如此重要。为了保障救援人员能够迅速、准确地响应灾情&#xff0c;提供及时有效的救助&#xff0c;顶坚应急救灾北斗终端手机应用应运而生。这款应用依托北斗卫星导航系统的高精度定位与通信功能&#xff0c;不…

图扑智慧农业——生态鱼塘数字孪生监控

智慧农业园作为新型农业经营模式&#xff0c;正在以其高效、环保、可持续的特点受到广泛关注。智慧鱼塘作为智慧农业中一项关键技术&#xff0c;结合物联网、人工智能、云计算等技术&#xff0c;实现对新型养殖模式的实时监控、优化与管理。 效果展示 图扑软件应用自研 HT for…

巩固学习7

正则表达式 就是用来找到符合模式的字符串&#xff0c;这些模式包括&#xff1a;是什么字符&#xff0c;重复多少次&#xff0c;在什么位置&#xff0c;有哪些额外的约束 找某个字符串 import re text身高:178 体重:168 学号:123456 密码:9527 #在Python中&#xff0c;r前缀用…

BFD双向转发检测

BFD概述 Bidirectional Forwarding Detection &#xff1a;双向转发检测 BFD技术背景 现网中存在的问题 不能快速有效的发现网络设备或链路中出现的故障不能以毫秒级的速度发现网络中的问题协议自身的报文检测机制一般都大于1秒 解决方案&#xff1a; 需要一种专门用于快…

【脚本】使用脚本备份docker中部署的mysql数据库

v1版本明文密码方式&#xff1a; #!/bin/bash# 定义 MySQL 容器名称和数据库信息 container_name"mysql_container" db_user"root" db_password"your_password"# 定义要备份的数据库列表 databases("database1" "database2"…

Outlook的IMAP服务器怎么填写?填写步骤?

Outlook的IMAP服务器如何使用&#xff1f;服务器地址怎么查找&#xff1f; 当我们在Outlook中设置新的电子邮件账户时&#xff0c;经常会遇到一个问题&#xff1a;Outlook的IMAP服务器怎么填写呢&#xff1f;接下来&#xff0c;AokSend将详细解答这个问题&#xff0c;并帮助大…

400元已到账,成交从认真开始

昨天发了一个值班的需求&#xff0c;收到了很多好友的响应&#xff0c;这里非常感谢关注创业程序员卡酷的老朋友、新朋友。今天分享一下&#xff1a;拓展、合作、成交 现在不管是IT行业还是其他行业&#xff0c;大环境可谓一片惨淡&#xff0c;35乃至30找不到工作的失业人员一抓…

深度论证-高速走线控制100欧姆阻抗一定是最好的选择吗?

高速先生成员--黄刚 对于高速差分信号到底需要控制多少欧姆的阻抗&#xff0c;高速先生相信大部分工程师首先都会看下例如信号的协议文档或者芯片的文档&#xff0c;看看里面有没有推荐的控制阻抗值。例如像PCIE信号&#xff0c;在4.0之后的阻抗会明确要求按照85欧姆来控制&…

4.Jmeter阶梯加压Stepping Thread Group

1. 先去Jmeter下载地址下载PluginsManager&#xff0c;放置在Jmeter的lib/ext 目录下 &#xff0c;重启Jmeter 2. 在插件管理器查找并安装jpgc - Standard Set,重启Jmeter 3.右键测试计划->添加->Threads(Users)->jpgc - Stepping Thread Group 然后设置阶梯加压参数…

贷款没有逾期,征信没问题,为什么大数据信用评分低呢?

大数据信用在金融贷前风控越来越重要&#xff0c;这让不少人开始关心自己的大数据信用了&#xff0c;其中就有不少人有疑问&#xff0c;那就是自己网贷没有逾期&#xff0c;征信记录也还可以&#xff0c;为什么大数据信用评分低呢?这个问题也是不少人都想知道的&#xff0c;小…