当前位置: 首页 > news >正文

005.串口调试功能实现|千篇笔记实现嵌入式全栈/裸机篇

⚠️裸机仓库:https://gitee.com/simonchina_carel_li/mini2440-bare-metal.git
⚠️Tag:5-uart

1. 方案解析

1.1 看引脚

为了实现log功能,我们需要配置一路UART0串口使用

看原理图,

对应的IO口为GPH2, GPH3,

根据数据手册, 需要将CPHCON对应bits配置为0b10复用模式,

1.2 看配置

对于初始化, 我们需要配置三个寄存器,

  • 线控寄存器ULCON0: 配置8n1

  • 控制寄存器UCON0: 配置收发缓冲模式为polling(最简单的方式, 没有用fifo)

  • 分频寄存器UBRDIV0: 配置波特率为26

具体寄存器位定义参看数据手册,

1.3 看收发

在使用方面, 我们需要两个功能, 就是收和发,

对于收, 需要轮询UTRSTAT0状态寄存器, 监测到接收buffer有数据了, 然后从接收缓存寄存器URXH0中读一个字符出来,

对于发, 需要轮询UTRSTAT0状态寄存器, 监测到发送buffer空闲了, 然后从发送缓存寄存器UTXH0写一个字符, 就会自动发送出去,

2. 代码实现

在common目录下新建uart.c文件,

初始化,

void uart0_init() { /**GPIO配置 * TXD0: GPH2/RXD0: GPH3/上拉 */ GPIO_SET_MODE(GPIOHCON, 2, 0b10, 2); GPIO_SET_MODE(GPIOHCON, 3, 0b10, 2); GPIO_SET_PULLUP(GPIOHUP, 2, 0); GPIO_SET_PULLUP(GPIOHUP, 3, 0); /** * 配置UART线路控制 * 非红外/8n1 */ ULCON0 = 0b11 << 0; /** * 控制寄存器 * 时钟选择PCLK/Tx中断类型:脉冲/Rx中断类型:脉冲/不环回/Tx缓冲模式:polling/Rx缓冲模式:polling */ UCON0 = (0b01 << 2) | (0b01 << 0); /** * 配置波特率115200 * 公式:UBRDIVn = (int)( UART 时钟 / ( 波特率 × 16) ) –1 * UBRDIV0 = 26 */ UBRDIV0 = 26; }

基础的字符收发

void uart0_putc(char c) { /** * 等待发送缓冲区空闲 * @note 非FIFO模式下缓存寄存器只有1字节 */ while (!(UTRSTAT0 & (1 << 1))); /** * 发送数据 */ UTXH0 = c; } char uart0_getc() { /** * 等待接收缓冲区收到数据 */ while (!(UTRSTAT0 & (1 << 0))); return (char)URXH0; }

格式化输出函数封装,

///@brief 输出字符串 void uart0_puts(const char *s) { while (*s) { uart0_putc(*s++); } } ///@brief 自定义 printf 实现 void uart0_printf(const char *fmt, ...) { va_list args; va_start(args, fmt); char c; while ((c = *fmt++)) { if (c != '%') { if (c == '\n') { uart0_putc('\r'); // 添加回车(解决某些终端换行显示问题) } uart0_putc(c); continue; } c = *fmt++; if (c == '\0') break; switch (c) { case 'c': { char ch = (char)va_arg(args, int); uart0_putc(ch); break; } case 's': { const char *s = va_arg(args, const char *); if (s) uart0_puts(s); break; } case 'd': { int num = va_arg(args, int); if (num < 0) { uart0_putc('-'); num = -num; } char buf[10]; int i = 0; do { buf[i++] = (num % 10) + '0'; num /= 10; } while (num); while (i--) { uart0_putc(buf[i]); } break; } case 'x': { uint32_t num = va_arg(args, uint32_t); char buf[8]; int i = 0; do { int digit = num % 16; buf[i++] = digit < 10 ? digit + '0' : digit - 10 + 'a'; num /= 16; } while (num); while (i--) { uart0_putc(buf[i]); } break; } default: uart0_putc(c); // 未识别的格式,输出原字符 break; } } va_end(args); }

函数声明放到头文件s3c2440a.h,

#ifndef __S3C2440A_H__ #define __S3C2440A_H__ ...略... void uart0_init(); void uart0_putc(char c); char uart0_getc(); void uart0_puts(const char *s); void uart0_printf(const char *fmt, ...); #endif //!__S3C2440A_H__

3. 测试代码

Makefile中增加新的目标uart,

...略... # 目标定义 TARGETS := led key uart ...略...

新建目录和文件uart/main.c,

#include "s3c2440a.h" int main() { char buf[128]; int ptr = 0; for (;;) { // 提示符 if (!ptr) { uart0_printf("MINI2440# "); } // 接收 char ch = uart0_getc(); // 实时回显 uart0_putc(ch); // 缓存 if (ptr < 127) { buf[ptr++] = ch; } // 一次性打印 if (ch == '\r' || ch == '\n') { buf[ptr] = '\0'; uart0_printf("\nI Got %s\n", buf); ptr = 0; } } return 0; }

这个测试代码的目的, 是回环显示你的输入,

  • 输入字符时, 实时显示出来

  • 输入完毕, 按回车, 回显输入的完整行

  • 有提示符MINI2440#

编译:

li@DESKTOP-QQUOPJH:~/2026/bare-metal$ make uart /home/li/2026/toolchain/arm-s3c2440-linux-gnueabi/bin/arm-s3c2440-linux-gnueabi-gcc -Wall -Werror -O0 -Icommon -c uart/main.c -o uart/main.o /home/li/2026/toolchain/arm-s3c2440-linux-gnueabi/bin/arm-s3c2440-linux-gnueabi-gcc -nostdlib -Wl,--gc-sections -T /home/li/2026/bare-metal/mini2440_nor.lds -o uart.elf uart/main.o common/start.o common/clk_setup.o common/uart.o common/common.o /home/li/2026/toolchain/arm-s3c2440-linux-gnueabi/lib/gcc/arm-s3c2440-linux-gnueabi/8.5.0/../../../../arm-s3c2440-linux-gnueabi/bin/ld: warning: common/start.o: missing .note.GNU-stack section implies executable stack /home/li/2026/toolchain/arm-s3c2440-linux-gnueabi/lib/gcc/arm-s3c2440-linux-gnueabi/8.5.0/../../../../arm-s3c2440-linux-gnueabi/bin/ld: NOTE: This behaviour is deprecated and will be removed in a future version of the linker /home/li/2026/toolchain/arm-s3c2440-linux-gnueabi/bin/arm-s3c2440-linux-gnueabi-objcopy -O binary uart.elf uart.bin

接线, 烧录, 打开终端, 运行:

http://www.jsqmd.com/news/583898/

相关文章:

  • AI工具实战--Agent Skills入门:把重复流程封装成可复用的能力包
  • MAX30101心率血氧传感器驱动开发与嵌入式集成指南
  • 新手小白部署阿里云服务器
  • spring boot在普通方法中获取HttpServletRequest及其使用的方式
  • _C++精灵库算法可视化程序
  • 外企转华为的技术人转型观察与建议
  • Kubernetes网络策略深度解析
  • AI工具实战--VibeCoding开发流程:写代码前的9步准备
  • 本地LLM部署工具(写给小白的LLM工具选型系列:第一篇)
  • CFPS 数据清洗教程:带你轻松上手
  • AI工具实战-- 普通人自学AI必知的30个工具清单
  • 4类URPC2021水下目标检测数据集该数据集拥有7600张图片,拥有4个类别,分别是[‘holothurian‘, ‘echinus‘, ‘starfish‘, ‘scallop‘]数据集是VO
  • ### 3. 工业级鲁棒性:动态参数控制
  • OpenClaw长任务省token方案:Qwen3-32B私有镜像实测对比
  • 深耕公考七载,以师者匠心筑就学员上岸之路
  • STM32三区存储优化LoRa OTA升级速度
  • 10款高效答辩AI工具(含爱毕业aibiye)与模板使用体验分享
  • 基于YOLOV8的狗狗前端检测识别成品项目开箱即用,开箱即用,开箱即用该项目具有如下5个优势:① 包含狗狗检测数据集(mtdata_dog_cls11)共884张图片,11种类别,已按照7:2:
  • AI工具实战--MCP是什么:让AI真正连接外部世界的协议
  • 2025-2026年美国投资移民中介推荐:五大口碑服务评测评价领先 - 品牌推荐
  • WIZ820ioInterface:面向工业场景的W5500嵌入式以太网驱动HAL
  • Slope Trick 学习笔记
  • 深圳万象天地街区美陈:如何从空间装饰升级为体验引擎?肆墨设计
  • 基于51单片机的锅炉温控(PT100、热电偶)探秘
  • 针对双SMC控制的四轮转向轨迹跟踪模型优化与效果评估研究
  • 【图像加密】基于 AES算法的图像位平面加密解密算法附Matlab代码
  • 【算法日记】Day 4 一维动态规划基础(按字符DP)
  • 技术创业中的产品迭代:从内核开发到用户中心
  • 创意随笔:智能转录便携终端
  • 告别图层导出烦恼:智能高效的Photoshop批量处理工具如何提升设计效率