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

张开发
2026/4/4 1:03:57 15 分钟阅读
005.串口调试功能实现|千篇笔记实现嵌入式全栈/裸机篇
⚠️裸机仓库https://gitee.com/simonchina_carel_li/mini2440-bare-metal.git⚠️Tag:5-uart1. 方案解析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#编译:liDESKTOP-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接线, 烧录, 打开终端, 运行:

更多文章