UART / SPI / IIC 通信外设
AC82N(BR 系列)MCU SDK 中三大串行通信外设的统一参考:UART(通用异步收发)、SPI(串行外设接口)与 IIC(I²C 两线接口)的驱动 API、数据结构、工作模式与典型用法。
Purpose and Scope
本页面系统介绍 AC82N_GP-MCU_SDK 中 UART、SPI、IIC 三类通信外设的驱动层接口与使用方式,涵盖:
- UART:
uart_v2.h定义的设备句柄、配置结构体、DMA 接收、事件回调、硬件流控与单线半双工模式; - SPI:
spi.h定义的四种工作模式(1/2/4 位半双工与全双工)、主/从角色、阻塞/中断/DMA 三类传输接口; - IIC:
iic_api.h定义的软件 IIC(GPIO 模拟)与硬件 IIC 两套实现,以及基于_IIC_USE_HW宏的统一接口抽象。
以下内容属于兄弟页面范畴,不在本页展开:具体外设器件驱动(如 LCD 的 lcd_spi_st7735 等 SPI 屏驱动、EEPROM 的 iic_eeprom.c)、GPIO 基本输入输出、UART 日志系统(uart_log.c)与 UBOOT 阶段串口。本页聚焦于驱动 API 本身的设计与使用;如需查看器件级应用,可参考 sdk/cpu/demo/uart_demo.c、sdk/cpu/demo/spi_demo.c、sdk/cpu/demo/iic_demo.c 三个示例工程。
Overview
三类外设是嵌入式系统中最基础的数据通路:UART 用于异步点对点通信(调试、AT 指令、低速率传感),SPI 用于高速同步通信(Flash、LCD、SD 卡),IIC 用于低速多设备总线(EEPROM、传感器、PMIC)。AC82N SDK 对它们的抽象层级一致:
- API 头文件(
include_lib/driver/cpu/periph/与include_lib/driver/cpu/)暴露全部对外接口; - 实现文件(
sdk/cpu/cd09/、sdk/cpu/)完成寄存器级操作; - 设备框架(UART 通过
device/device.h与generic/ioctl.h集成到设备子系统)与 OS 抽象(信号量/互斥锁可切换为 RTOS 或裸机实现)。
三个外设的设计哲学各有侧重:
- UART 以"配置结构体 + 事件回调 + 环形缓冲"为中心,把 DMA、超时帧检测、校验错误等全部收敛到
uart_dma_config中,并提供UT_Semaphore/UT_mutex的 OS 双实现(见 uart_v2.h),使同一套 API 既可用于 RTOS 环境也可用于裸机。 - SPI 以
spi_platform_data描述引脚与时序(CPOL/CPHA),通过spi_open打开通道后,按阻塞(字节级)、DMA(块级)、中断(ISR 内逐字节/逐缓冲)三档接口递进,主从机共用中断接口。 - IIC 通过
_IIC_USE_HW编译宏在软件 IIC(任意 GPIO 模拟,频率不精确)与硬件 IIC(精确时序、可中断、支持从机)之间切换,上层统一使用i2c_master_read_nbytes_from_device_reg这类"寄存器地址 + 数据"的语义化接口。
Architecture
flowchart TD
subgraph sg_App["应用层"]
App["用户代码 / demo 工程<br/>uart_demo / spi_demo / iic_demo"]
end
subgraph sg_API["外设 API 层 (头文件)"]
UART_API["uart_v2.h<br/>uart_init / uart_dma_init"]
SPI_API["spi.h<br/>spi_open / spi_dma_send"]
IIC_API["iic_api.h<br/>i2c_master_read/write_nbytes"]
end
subgraph sg_IMPL["实现层 (cpu 目录)"]
UART_IMPL["cpu/cd09 串口实现<br/>+ uart_log.c"]
SPI_IMPL["cpu/cd09/spi_hw_v2.c"]
IIC_IMPL["cpu/iic_api.c + iic_soft.c<br/>(软 IIC 与硬 IIC 两套)"]
end
subgraph sg_HW["硬件外设"]
UART_HW["UART 控制器<br/>DMA / FIFO / 流控"]
SPI_HW["SPI 控制器<br/>SPI0 / SPI1 / SPI2"]
IIC_HW["IIC 控制器 或 GPIO 模拟<br/>SCL / SDA"]
end
App -->|"调用 API"| UART_API
App -->|"调用 API"| SPI_API
App -->|"调用 API"| IIC_API
UART_API --> UART_IMPL --> UART_HW
SPI_API --> SPI_IMPL --> SPI_HW
IIC_API --> IIC_IMPL --> IIC_HW
架构说明
- API 层与实现层分离:头文件位于
sdk/include_lib/driver/cpu/(对外发布),实现位于sdk/cpu/(内部编译)。这使驱动可独立升级、应用代码无需关心寄存器细节。 - IIC 的双实现是唯一"一 API 两实现"的外设:
iic_soft.c用 GPIO 位翻转模拟时序(任意引脚、无硬件限制、频率不准),iic_api.c+iic_hw_v2.h走硬件控制器(精确时序、可配置滤波与中断、支持从机)。宏_IIC_USE_HW决定iic_init等符号最终映射到哪一套。 - UART 的 OS 抽象:
CONFIG_ENABLE_UART_OS_SEM开启时信号量/互斥锁映射到os_sem_*/os_mutex_*;关闭时退化为带wdt_clear()与asm("idle")的自旋实现(见 uart_v2.h),保证裸机环境下同样可阻塞等待。
UART 深入解析
UART 驱动以"设备号 + 配置结构体"为核心。设备句柄类型为 typedef const int uart_dev,uart_num = -1 时由驱动自动分配串口号,返回值 >= 0 表示实际分配的串口号(见 uart_v2.h)。
配置结构体与设计意图
struct uart_config {
u32 baud_rate;
u16 tx_pin; //不使用时配0xffff
u16 rx_pin; //不使用时配0xffff,当rx_pin == tx_pin 的时候为单线半双工通讯模式
enum uart_parity parity;//uart_v2有效
u8 tx_wait_mutex;//1:不支持中断调用,互斥,0:支持中断,不互斥
};
struct uart_dma_config {
cbuffer_t cbuf;
u32 rx_timeout_thresh;//us, 需大于1byte时间
u32 frame_size;//一般配置=rx_cbuffer_size
u32 rx_cbuffer_size;
void *rx_cbuffer;
void (*irq_callback)(uart_dev uart_num, enum uart_event); //推送到调用者的线程执行
enum uart_event event_mask;
UT_Semaphore uart_rx;
u8 irq_priority;//中断优先级
};
struct uart_flow_ctrl {
u16 cts_pin; //不使用时配0xffff
u16 rts_pin; //不使用时配0xffff
u8 cts_idle_level; //0:cts空闲电平为低; 1:cts空闲电平为高
u8 rts_idle_level; //0:rts空闲电平为低; 1:rts空闲电平为高
u8 rx_thresh; // 0~100 %
};
来源:uart_v2.h
要点:
tx_pin/rx_pin用0xffff表示"不使用":可只配 TX(只发)或只配 RX(只收);当rx_pin == tx_pin时自动进入单线半双工模式,这是很多低引脚数传感与调试协议(如单线总线)的常用形态。tx_wait_mutex是并发安全开关:置 1 时发送前会取互斥锁(不支持在中断里调用发送),置 0 时不做互斥、允许中断上下文直接发送。设计意图是让用户按自己的调用场景在"数据完整性"与"可中断性"之间取舍。- DMA 配置把"数据搬运"与"事件通知"绑定在一起:
rx_cbuffer是接收环形缓冲,irq_callback由中断推送到调用者线程执行,event_mask决定哪些事件会上报,uart_rx信号量用于阻塞式等待接收。
事件与状态码
enum uart_event : u8 {
UART_EVENT_RX_DATA = 1, //接收缓冲区溢出
UART_EVENT_RX_FIFO_OVF = 2, //接收缓冲区溢出
UART_EVENT_RX_TIMEOUT = 4, //数据帧接收完成
UART_EVENT_PARITY_ERR = 8, //奇偶校验错误
UART_EVENT_TX_DONE = 16, //数据发送完成
};
来源:uart_v2.h
事件是位掩码(1/2/4/8/16),可组合赋给 uart_dma_config.event_mask。UART_EVENT_RX_TIMEOUT(帧接收完成)依赖 rx_timeout_thresh,用于把一帧不定长的数据包整体交付给上层——这是"接收超时判帧"的典型设计。
状态码 enum uart_state 从 UART_OK = 0 到 UART_ERROR_FLOWCTRL_IO = -13,依次覆盖:忙、设备已关闭、上下文错误、内存分配失败、缓冲未 4 字节对齐、超时、OS 对象创建失败、波特率非法、RX/TX 缓冲分配失败、硬件流控初始化失败/IO 错误等(见 uart_v2.h)。负值返回约定贯穿整个 SDK,调用方只需判断 < 0 即失败。
运行时接口
s32 uart_init(uart_dev uart_num, const struct uart_config *config); // >=0:串口号, <0:失败
s32 uart_deinit(uart_dev uart_num); // <0:error; 0:ok
s32 uart_dma_init(uart_dev uart_num, const struct uart_dma_config *dma_config);
void uart_dump(); // 打印uart配置信息
s32 uart_set_baudrate(uart_dev uart_num, u32 baud_rate); // 返回实际波特率
s32 uart_set_rx_timeout_thresh(uart_dev uart_num, u32 timeout_thresh);
来源:uart_v2.h
uart_set_baudrate 返回实际波特率而非 0/1,体现"请求值 → 分频器取整 → 回读实际值"的闭环设计——对需要精确波特率(如 BLE 配对、红外载波)的场景至关重要。
SPI 深入解析
SPI 驱动以 hw_spi_dev 句柄 + spi_platform_data 配置为中心,支持三个硬件通道(SPI0/1/2)。
模式、角色与时序配置
enum spi_mode {
SPI_MODE_BIDIR_1BIT, //支持SPIx(x=0,1,2),全双工,di接收,do发送
SPI_MODE_UNIDIR_1BIT, //支持SPIx(x=0,1,2),半双工,do分时发送/接收
SPI_MODE_UNIDIR_2BIT, //支持SPIx(x=0,1,2),半双工,di & do共2bit分时发送/接收
SPI_MODE_UNIDIR_4BIT, //支持SPIx(x=1),半双工,di & do & wp & hold 共4bit分时发送/接收
};
enum spi_role {
SPI_ROLE_MASTER,
SPI_ROLE_SLAVE,
};
typedef struct spi_platform_data {
u8 port[6]; //CLK, DO, DI, D2(wp), D3(hold), cs(只用于slave)
enum spi_role role; //master or slave
enum spi_mode mode; //模式,选项为enum spi_mode中的枚举常量
enum spi_bit_mode bit_mode;
u8 cpol: 1; //clk level in idle state:0:low, 1:high
u8 cpha: 1; //sampling edge:0:first, 1:second
u8 ie_en: 1; //ie enbale:0:disable, 1:enable
u8 irq_priority: 5; //中断优先级
void (*spi_isr_callback)(hw_spi_dev spi, enum hw_spi_isr_status sta); //spi isr callback
u32 clk; //波特率
} spi_hardware_info;
来源:spi.h
要点:
port[6]固定顺序:CLK、DO、DI、D2(wp)、D3(hold)、CS(CS 仅从机使用,主机 CS 由HW_SPI_MASTER_CS_EN宏控制开关,默认关闭,见 spi.h)。这与常见 MCU 的引脚复用表一一对应,配置时按此顺序填写 IO 编号。- 位域打包的时序参数:
cpol(空闲时钟电平)、cpha(采样沿)、ie_en(中断使能)、irq_priority(5 bit 中断优先级)共用一个字节,spi_isr_callback用于中断模式完成通知。 - 模式演进反映 Flash/屏类器件的需求:BIDIR_1BIT 是标准 4 线全双工;UNIDIR_1BIT/2BIT/4BIT 分别对应 SPI NOR Flash 的 1-1-1、1-1-2、1-1-4 以及 QSPI 的 4 线模式(wp/hold 复用为数据线)。注意 4BIT 模式仅 SPI1 支持。
传输接口分层
/*******************主机 阻塞接口*********************/
u8 spi_recv_byte(hw_spi_dev spi, int *err);
int spi_send_byte(hw_spi_dev spi, u8 byte);
u8 spi_send_recv_byte(hw_spi_dev spi, u8 byte, int *err);//全双工
int spi_dma_recv(hw_spi_dev spi, void *buf, u32 len);
int spi_dma_send(hw_spi_dev spi, const void *buf, u32 len);
来源:spi.h
阻塞接口按字节粒度工作:spi_recv_byte/spi_send_byte 用于单字节读写,spi_send_recv_byte 一次调用同时完成发送与接收(全双工),这对"读状态寄存器时需要先发命令字节"的器件操作很关键。spi_dma_recv/spi_dma_send 用于块传输,适合 LCD 帧缓冲、Flash 页编程等大数据量场景。
中断接口(spi_set_ie、hw_spix_irq_change_callback、spi_byte_transmit_for_isr、spi_buf_transmit_for_isr、spi_dma_transmit_for_isr,见 spi.h)统一为 rw 参数区分收发(rw:1-rx; 0-tx),主从机均可使用,不等待 pending、无 CS 控制——适用于在中断回调里推进的协议栈(如 DMA 乒乓、命令队列)。中断状态由 enum hw_spi_isr_status(WAITING_PND / TX_FINISH / RX_FINISH / PND_ERROR)与 hw_spix_get_isr_len(带符号长度:DMA 为负、字节为正)共同表达(见 spi.h)。
IIC 深入解析
IIC 是三者中抽象最特殊的一个:一份 API,两套实现,由宏 _IIC_USE_HW 决定编译期映射。
统一配置与状态
struct iic_master_config {
enum iic_role role; //软件只有IIC_MASTER
int scl_io;
int sda_io;
enum gpio_mode io_mode;//上拉或浮空
enum gpio_drive_strength hdrive; //enum GPIO_HDRIVE 0:2.4MA, 1:8MA, 2:26.4MA, 3:40MA
u32 master_frequency; //软件iic频率(hz 不准)
u8 ie_en; //中断使能
u8 irq_priority; //中断
u8 io_filter; //BR50: 0:close filter; 1:<1*Tiic_baud_clk, ...
};
来源:iic_api.h
- 软件 IIC 只支持
IIC_MASTER角色,master_frequency只是"请求值,实际不准"——因为 GPIO 位翻转受指令周期抖动影响,注释明确承认这一点; io_mode用于配置上拉/浮空(IIC 开漏总线通常需要上拉),hdrive选择驱动强度(2.4~40mA),io_filter按芯片型号不同语义:BR27/28/36 为 0/1 开关,BR50 为 0~3 级滤波深度。
语义化读写接口(核心用法)
//如果无reg_addr:reg_addr=NULL,reg_len=0
//return: <0:error, =read_len:ok
int soft_i2c_master_read_nbytes_from_device_reg(soft_iic_dev iic,
unsigned char dev_addr, //设备地址
unsigned char *reg_addr, unsigned char reg_len,//设备寄存器地址,长度
unsigned char *read_buf, int read_len);//缓存buf,读取长度
//如果无reg_addr:reg_addr=NULL,reg_len=0
//return: =write_len:ok, other:error
int soft_i2c_master_write_nbytes_to_device_reg(soft_iic_dev iic,
unsigned char dev_addr,
unsigned char *reg_addr, unsigned char reg_len,
unsigned char *write_buf, int write_len);
来源:iic_api.h
hw_i2c_master_read_nbytes_from_device_reg / hw_i2c_master_write_nbytes_to_device_reg 签名与软 IIC 完全一致(见 iic_api.h),只是句柄类型换成 hw_iic_dev。reg_len 支持多字节寄存器地址(如 2 字节寻址的 EEPROM),reg_addr == NULL && reg_len == 0 表示无寄存器寻址的裸读写。返回值约定:读成功返回 read_len,写成功返回 write_len,其余为错误码。
宏抽象层:一套代码切换软/硬 IIC
#ifdef _IIC_USE_HW
#define iic_init(iic, config) hw_iic_init(iic, config)
#define iic_start(iic) hw_iic_start(iic)
#define iic_stop(iic) hw_iic_stop(iic)
#define iic_tx_byte(iic, byte) hw_iic_tx_byte(iic, byte)
#define iic_rx_byte(iic, ack) hw_iic_rx_byte(iic, ack)
#define iic_read_buf(iic, buf, len) hw_iic_read_buf(iic, buf, len)
#define iic_write_buf(iic, buf, len) hw_iic_write_buf(iic, buf, len)
#define iic_suspend(iic) hw_iic_suspend(iic)
#define iic_resume(iic) hw_iic_resume(iic)
#else
#define iic_init(iic, config) soft_iic_init(iic, config)
#define iic_start(iic) soft_iic_start(iic)
#define iic_stop(iic) soft_iic_stop(iic)
#define iic_tx_byte(iic, byte) soft_iic_tx_byte(iic, byte)
#define iic_rx_byte(iic, ack) soft_iic_rx_byte(iic, ack)
#define iic_read_buf(iic, buf, len) soft_iic_read_buf(iic, buf, len)
#define iic_write_buf(iic, buf, len) soft_iic_write_buf(iic, buf, len)
#define iic_suspend(iic) soft_iic_suspend(iic)
#define iic_resume(iic) soft_iic_resume(iic)
#endif
来源:iic_api.h
这一层宏让器件驱动(如 iic_eeprom.c)只需写一次 iic_start/iic_tx_byte 等原语,编译时自动绑定到软或硬实现——这是"策略在编译期决定"的典型手法,避免运行期分支开销,也保证裸机与 RTOS 下行为一致。底层原语之外还有 hw_iic_slave_polling_rx / hw_iic_slave_polling_tx 两个硬件从机轮询接口(见 iic_api.h),软件 IIC 不提供从机能力。
Core Flow
UART DMA 接收流程
sequenceDiagram
participant App as 应用线程
participant U as uart_init + uart_dma_init
participant ISR as 串口中断
participant CB as 环形缓冲 cbuffer_t
App->>U: uart_init(num, config) 配置波特率/引脚
App->>U: uart_dma_init(num, dma_config) 注册回调与事件掩码
U->>CB: 建立 rx_cbuffer
loop 数据持续到达
ISR->>CB: 硬件中断把 RX 数据搬入环形缓冲
ISR->>App: 推送 irq_callback(uart, event)
end
App->>CB: 按 event_mask 消费数据 / 等待 uart_rx 信号量
App->>U: uart_deinit(num) 释放资源
设计意图:接收路径完全由中断驱动,环形缓冲解耦"硬件中断产生数据"与"应用线程消费数据";UART_EVENT_RX_TIMEOUT 事件让上层能按"帧"而非按"字节"处理不定长协议包;rx_timeout_thresh 需大于 1 字节传输时间,避免正常传输间隙被误判为帧结束。
SPI 主机一次完整事务
sequenceDiagram
participant M as SPI Master
participant D as 从设备 (Flash/LCD)
M->>M: spi_open(spi, &info) 配置 mode/cpol/cpha/clk
M->>D: spi_send_byte(命令字节)
M->>D: spi_send_recv_byte(数据, 全双工交换)
M->>D: spi_dma_send(buf, len) 大块数据 (DMA)
M->>M: hw_spix_get_isr_status 查询完成状态
M->>M: spi_deinit(spi) 释放通道
中断模式下 spi_byte_transmit_for_isr/spi_buf_transmit_for_isr/spi_dma_transmit_for_isr 可取代阻塞调用,在 spi_isr_callback 中推进状态机;hw_spix_clear_isr_len 必须在每包数据前调用,且注释警告"在通信中间调用会导致数据长不准"(见 spi.h)。
IIC 寄存器读流程(软/硬通用)
sequenceDiagram
participant M as IIC Master (soft/hw)
participant D as IIC 从设备 (EEPROM/传感器)
M->>M: iic_init(iic, &config) 或 i2c_master_*_nbytes 直连
M->>D: START + dev_addr(W)
M->>D: reg_addr[0..reg_len-1] (多字节寄存器地址)
M->>D: 重复 START + dev_addr(R)
D-->>M: read_len 字节数据 + ACK/NACK
M->>D: STOP
i2c_master_read_nbytes_from_device_reg 把上面的 START/地址/重复起始/STOP 时序全部封装在驱动内部,应用只提供设备地址、寄存器地址与长度即可,避免了手工维护总线时序的常见错误。
Usage Examples
示例一:UART 配置与 DMA 初始化
struct uart_config uart_cfg = {
.baud_rate = 115200,
.tx_pin = 0xffff, // 只收不发
.rx_pin = IO_PORTA_01,
.parity = UART_PARITY_DISABLE,
.tx_wait_mutex = 0, // 允许中断上下文发送
};
s32 uart_num = uart_init(-1, &uart_cfg); // -1: 自动分配串口号
struct uart_dma_config dma_cfg = {
.rx_timeout_thresh = 2000, // us, 需大于1byte时间
.frame_size = 256,
.rx_cbuffer_size = 256,
.rx_cbuffer = rx_buf,
.event_mask = UART_EVENT_RX_DATA | UART_EVENT_RX_TIMEOUT,
.irq_priority = 2,
};
uart_dma_init(uart_num, &dma_cfg);
结构体字段取自 uart_v2.h,调用方式依据
uart_init/uart_dma_init声明(uart_v2.h)。
示例二:SPI 通道打开与全双工收发
spi_hardware_info spi_info = {
.port = {SPI_CLK_IO, SPI_DO_IO, SPI_DI_IO, 0xff, 0xff, 0xff},
.role = SPI_ROLE_MASTER,
.mode = SPI_MODE_BIDIR_1BIT, // 标准全双工
.cpol = 0, // 空闲低电平
.cpha = 0, // 第一个沿采样
.ie_en = 0,
.clk = 10000000, // 10MHz
};
int ret = spi_open(SPI0, &spi_info);
u8 cmd = 0x9F; // 读 JEDEC ID 命令
u8 rsp = spi_send_recv_byte(SPI0, cmd, NULL); // 全双工交换
示例三:IIC 寄存器读写(软/硬件统一接口)
unsigned char dev_addr = 0xA0; // EEPROM 器件地址
unsigned char reg_addr[2] = {0x00, 0x10}; // 2字节寄存器地址
unsigned char wbuf[16], rbuf[16];
// 写:向寄存器写入 16 字节
int wr = i2c_master_write_nbytes_to_device_reg(0, dev_addr,
reg_addr, sizeof(reg_addr), wbuf, sizeof(wbuf));
// 读:从寄存器读出 16 字节
int rd = i2c_master_read_nbytes_from_device_reg(0, dev_addr,
reg_addr, sizeof(reg_addr), rbuf, sizeof(rbuf));
// 返回约定: wr == sizeof(wbuf) 成功; rd == sizeof(rbuf) 成功; 否则为负错误码
接口签名见 iic_api.h,宏映射见 iic_api.h。
iic参数为设备索引(软 IIC 为soft_iic_dev,硬 IIC 为hw_iic_dev)。
Configuration Options
UART 配置项(struct uart_config / struct uart_dma_config)
| 选项 | 类型 | 默认/典型值 | 说明 |
|---|---|---|---|
baud_rate | u32 | 115200 | 波特率;uart_set_baudrate 返回实际值 |
tx_pin / rx_pin | u16 | 0xffff | 0xffff 表示不使用;tx_pin == rx_pin 时为单线半双工 |
parity | enum uart_parity | DISABLE | DISABLE / all_0 / all_1 / EVEN / ODD |
tx_wait_mutex | u8 | — | 1:发送互斥(不可中断调用);0:不互斥(可中断调用) |
rx_timeout_thresh | u32 | — | 帧超时阈值(us),需大于 1 字节时间 |
frame_size / rx_cbuffer_size | u32 | 相等 | DMA 帧长与接收环形缓冲大小 |
event_mask | enum uart_event | — | RX_DATA(1)/RX_FIFO_OVF(2)/RX_TIMEOUT(4)/PARITY_ERR(8)/TX_DONE(16) 位或组合 |
irq_priority | u8 | — | 串口中断优先级 |
cts_pin/rts_pin | u16 | 0xffff | 硬件流控引脚,0xffff 关闭 |
rx_thresh | u8 | — | RTS 触发阈值(0~100%) |
SPI 配置项(spi_platform_data)
| 选项 | 类型 | 说明 |
|---|---|---|
port[6] | u8 数组 | 固定顺序:CLK、DO、DI、D2(wp)、D3(hold)、CS(CS 仅从机) |
role | enum spi_role | MASTER / SLAVE |
mode | enum spi_mode | BIDIR_1BIT(全双工)/ UNIDIR_1BIT / UNIDIR_2BIT / UNIDIR_4BIT(仅 SPI1) |
cpol / cpha | bit | 空闲电平(0 低/1 高)、采样沿(0 第一沿/1 第二沿) |
ie_en | bit | 中断使能 |
irq_priority | 5bit | 中断优先级 |
clk | u32 | 波特率(Hz) |
IIC 配置项(struct iic_master_config)
| 选项 | 类型 | 说明 |
|---|---|---|
role | enum iic_role | 软件 IIC 仅支持 IIC_MASTER |
scl_io / sda_io | int | 总线引脚(GPIO 号) |
io_mode | enum gpio_mode | 上拉或浮空 |
hdrive | enum gpio_drive_strength | 0:2.4MA / 1:8MA / 2:26.4MA / 3:40MA |
master_frequency | u32 | 请求频率;软件 IIC 实际不准 |
ie_en / irq_priority | u8 | 中断使能与优先级 |
io_filter | u8 | BR27/28/36:0/1 开关;BR50:0~3 级滤波 |
编译期开关
| 宏 | 作用 |
|---|---|
CONFIG_ENABLE_UART_OS_SEM | 1:UART 信号量/互斥锁映射到 RTOS(os_sem_*/os_mutex_*);0:退化为裸机自旋实现 |
_IIC_USE_HW | 1:iic_* 原语映射到硬件 IIC;0:映射到软件 IIC(GPIO 模拟) |
HW_SPI_MASTER_CS_EN | 主机片选使能开关(0 关 / 1 开) |
API Reference
UART
s32 uart_init(uart_dev uart_num, const struct uart_config *config)— 初始化串口;uart_num = -1自动分配,返回>=0串口号,<0失败。s32 uart_deinit(uart_dev uart_num)— 关闭串口;返回 0 成功。s32 uart_dma_init(uart_dev uart_num, const struct uart_dma_config *dma_config)— 配置 DMA 接收、回调与事件掩码。s32 uart_set_baudrate(uart_dev uart_num, u32 baud_rate)— 动态改波特率;返回实际波特率(<0错误)。s32 uart_set_rx_timeout_thresh(uart_dev uart_num, u32 timeout_thresh)— 动态调整接收超时阈值。void uart_dump(void)— 打印各串口配置信息(调试用)。
错误码区间:UART_OK(0),UART_ERROR_BUSY(-1) ~ UART_ERROR_FLOWCTRL_IO(-13),详见 uart_v2.h。
SPI
int spi_open(hw_spi_dev spi, spi_hardware_info *spi_info)— 按平台数据打开 SPI 通道(主/从)。void spi_deinit(hw_spi_dev spi)/spi_suspend/spi_resume— 释放/挂起/恢复。int spi_set_baud(hw_spi_dev spi, u32 baud)/u32 spi_get_baud(...)— 动态调整/读取波特率。void spi_set_bit_mode(hw_spi_dev spi, enum spi_mode mode)— 运行期切换 1/2/4 位模式。- 阻塞:
u8 spi_recv_byte(spi, int *err)、int spi_send_byte(spi, u8)、u8 spi_send_recv_byte(spi, u8, int *err)。 - DMA:
int spi_dma_recv(spi, void *buf, u32 len)、int spi_dma_send(spi, const void *buf, u32 len)。 - 中断:
spi_byte_transmit_for_isr/spi_buf_transmit_for_isr/spi_dma_transmit_for_isr(rw: 1-rx; 0-tx)、hw_spix_get_isr_status、hw_spix_get_isr_len、hw_spix_slave_get_dma_len。
IIC
int soft_i2c_master_read_nbytes_from_device_reg(soft_iic_dev, dev_addr, reg_addr, reg_len, read_buf, read_len)— 软 IIC 读;成功返回read_len。int soft_i2c_master_write_nbytes_to_device_reg(...)— 软 IIC 写;成功返回write_len。int hw_i2c_master_read_nbytes_from_device_reg(hw_iic_dev, ...)/hw_i2c_master_write_nbytes_to_device_reg(...)— 硬件 IIC 对应接口。- 原语(宏映射):
iic_init/iic_start/iic_stop/iic_tx_byte/iic_rx_byte/iic_read_buf/iic_write_buf/iic_suspend/iic_resume/iic_reset/iic_deinit。 - 从机:
int hw_iic_slave_polling_rx(hw_iic_dev, u8 *rx_buf)、int hw_iic_slave_polling_tx(hw_iic_dev, u8 *tx_buf)。
错误码区间:IIC_OK(0),IIC_ERROR_INIT_FAIL(-1) ~ IIC_ERROR_RESLOCK_BUSY(-11),覆盖初始化失败、未初始化、挂起/恢复失败、忙、参数错误、设备地址/寄存器地址 ACK 错误、索引/频率错误、资源锁忙等(见 iic_api.h)。
Failure Modes, Edge Cases & Concurrency
- 返回值约定:三个外设统一"负值即失败";UART/IIC 的多数接口返回
0表示成功或返回实际长度,判断时必须区分"成功返回的实际值"与"负数错误码",例如 IIC 读成功返回read_len而非 0。 - UART 缓冲区溢出:
UART_EVENT_RX_FIFO_OVF上报 FIFO 溢出;UART_ERROR_CLOSE注释为"接收缓冲区溢出"(历史命名,实际语义以枚举注释为准)。rx_cbuffer必须 4 字节对齐,否则返回UART_ERROR_BUF_ALIGN4。 - 中断上下文限制:
tx_wait_mutex = 1时 UART 发送不可在中断中调用(会死锁);= 0时允许。SPI 的中断接口明确"不等待 pnd、无 CS",调用者必须在 ISR 回调中自行管理包边界与片选。 - SPI 长度计数:
hw_spix_clear_isr_len只能在每包数据前调用,通信中途调用会导致长度统计错误;hw_spix_get_isr_len用符号区分 DMA(负)与字节(正)传输。 - 软件 IIC 时序:
master_frequency仅为请求值,实际时序受指令抖动影响不准;软件 IIC 无硬件滤波(io_filter仅硬件生效),长总线或高干扰环境应优先硬件 IIC 并开启滤波。 - 单线半双工:UART
rx_pin == tx_pin时收发共用一根线,需应用层自行保证半双工时序(发送时禁止同时接收解析)。 - 裸机并发:
CONFIG_ENABLE_UART_OS_SEM = 0时,信号量/互斥退化为volatile u8自旋 +wdt_clear()+asm("idle"),等待超时依赖 jiffies 换算(ut_msecs_to_jiffies10ms 取整),短超时精度有限(见 uart_v2.h)。
Performance & Operational Considerations
- DMA 优先:UART 大数据接收、SPI 大块传输都应走 DMA 路径(
uart_dma_init/spi_dma_send/spi_dma_recv),字节级轮询只用于低频命令交互。 - SPI 4 线模式带宽:
SPI_MODE_UNIDIR_4BIT仅 SPI1 支持,是读取高容量 Flash / 高分辨率屏的最高带宽选项;启用前需确认port[]中 D2/D3 引脚已分配。 - UART 帧超时调参:
rx_timeout_thresh必须大于 1 字节传输时间(如 115200 波特率下约 87us),过小会拆帧、过大增加协议响应延迟;协议报文越长可适当调大。 - IIC 总线负载:硬件 IIC 的
io_filter在 BR50 上提供 1~3 个Tiic_baud_clk的滤波窗口,抗干扰与速率之间需权衡;软件 IIC 因位翻转慢,总线上挂多设备时需降低master_frequency请求值。 - 调试手段:
uart_dump()可打印全部串口配置,用于核对初始化参数;SPI 中断状态通过hw_spix_get_isr_status的SPI_PND_ERROR排查异常。
Extension Points
- 新增 UART 用途:
uart_dma_config.irq_callback是唯一的应用扩展钩子——在同一回调内根据event_mask分发到帧处理、错误统计、协议解析等逻辑,避免改动驱动。 - SPI 从机协议栈:
spi_isr_callback+spi_buf_transmit_for_isr/spi_dma_transmit_for_isr可构建从机命令响应状态机;hw_spix_slave_get_dma_len用于查询从机已接收长度。 - IIC 器件驱动:基于
i2c_master_read/write_nbytes_from_device_reg写新器件驱动即可自动兼容软/硬 IIC(编译期宏切换),如iic_eeprom.c的做法;若需更细粒度控制(如页写、重复起始),可下钻到iic_start/iic_tx_byte原语层。 - OS 移植:UART 的
UT_Semaphore/UT_mutex封装是唯一需要随 OS 变化的部分,新增 RTOS 时只需替换 uart_v2.h 中CONFIG_ENABLE_UART_OS_SEM分支的内联实现。
Related Links
- UART 设备框架与类型:
sdk/include_lib/driver/cpu/cd09/asm/uart_types.h、sdk/include_lib/driver/cpu/cd09/asm/uart_log.h - UART 实现与示例:
sdk/cpu/cd09/uart_log.c、sdk/cpu/demo/uart_demo.c、sdk/UBOOT工程/cpu/cd09/user_uart.c - SPI 硬件层与示例:
sdk/include_lib/driver/cpu/cd09/asm/spi_hw.h、sdk/cpu/cd09/spi_hw_v2.c、sdk/cpu/demo/spi_demo.c - SPI 屏驱动示例(器件级):
sdk/apps/common/lcd/lcd_spi_st7735_128x128.c、lcd_spi_st7789_BOE1.54_update_240x240.c - IIC 实现与示例:
sdk/cpu/iic_api.c、sdk/cpu/iic_soft.c、sdk/cpu/demo/iic_demo.c、sdk/include_lib/driver/cpu/periph/iic_soft.h、iic_hw_v2.h - IIC 器件级应用:
sdk/apps/common/eeprom/iic_eeprom.c、sdk/apps/common/eeprom/iic_eeprom.h - 相关外设页面:GPIO 驱动(
io_mode/hdrive类型来源)、LCD 显示驱动、EEPROM 存储