1. 项目概述
作为一名在嵌入式开发领域摸爬滚打多年的老手,我深知串口通信在硬件开发中的核心地位。今天要分享的是基于JL杰理AC696N系列芯片的UART开发实战经验,这个国产蓝牙音频SoC在消费电子领域应用广泛,但官方文档对串口应用的说明往往比较分散。本文将系统梳理UART在AC696N上的三大典型应用场景:调试信息打印、音频参数调节和设备间通信。
AC696N系列最大的特点是双核架构(DSP+MCU)和丰富的音频外设接口,其UART控制器支持最高3Mbps波特率,具有独立的128字节FIFO,在实际项目中既能满足基础调试需求,也能承担音频设备间的数据交互任务。我曾用这套方案成功开发过蓝牙音箱固件、车载音频处理器等产品,积累了不少实战技巧。
2. 硬件环境搭建
2.1 最小系统设计
AC696N的UART接口对应芯片的特定GPIO引脚,以AC6965D型号为例:
- UART0_TX: PB5
- UART0_RX: PB6
- UART1_TX: PB2
- UART1_RX: PB3
重要提示:PB5同时是I2S接口的BCK引脚,如果项目需要同时使用音频总线和串口,建议改用UART1或重新规划PCB走线。
典型外围电路设计要点:
- 上拉电阻:TX线建议接1KΩ上拉至VCC
- 电平转换:若对接3.3V系统可直接连接,对接5V设备需加电平转换芯片如TXS0108E
- 保护电路:ESD二极管选用SMF05C,特别在车载环境必须添加
2.2 开发板选择
推荐使用官方开发板AC696X_DEMO_V1.2,其板载CH340 USB转串口芯片,跳线设置如下:
- 短接J10的1-2脚启用UART0
- SW3拨码开关1置ON选择下载模式
- P3排针引出UART1测试点
3. 软件开发环境配置
3.1 SDK获取与工程创建
杰理提供AC696N的ADK开发包(需联系代理商获取),建议使用v1.6.3以上版本。新建工程时注意勾选以下配置:
makefile复制CONFIG_UART_ENABLE = y
CONFIG_UART0_ENABLE = y
CONFIG_DBG_PRINTF_UART0 = y # 将printf重定向到UART0
3.2 关键驱动函数解析
SDK中uart_drv.c提供了底层操作接口,重点函数包括:
c复制void uart_init(u32 uart_id, u32 baud_rate); // 初始化波特率
void uart_send_data(u32 uart_id, u8 *buf, u32 len);
u32 uart_recv_data(u32 uart_id, u8 *buf, u32 len);
典型初始化代码示例:
c复制// 设置UART0为115200波特率,8N1模式
uart_init(UART0, 115200);
// 启用接收中断
request_irq(IRQ_UART0_IDX, 0, uart0_isr, 0);
4. 三大核心应用实现
4.1 调试信息打印方案
在audio_debug.c中重写__wrap_printf函数:
c复制int __wrap_printf(const char *fmt, ...)
{
va_list args;
char buffer[256];
va_start(args, fmt);
int len = vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
uart_send_data(UART0, (u8 *)buffer, len);
return len;
}
优化技巧:
- 使用静态缓冲区减少堆栈消耗
- 添加互斥锁防止多任务打印错乱
- 通过CONFIG_LOG_LEVEL控制输出级别
4.2 音频参数动态调节
实现串口命令控制EQ参数的典型流程:
- 定义通信协议(示例):
code复制[HEAD][CMD][LEN][DATA][CRC] 0x55 0xA5 0x04 0x01 0x02 0x03 0x04 0xXX - 解析线程实现:
c复制void uart_parser_task(void *p)
{
while(1) {
if(uart_recv_data(UART0, rx_buf, 1) > 0) {
switch(state_machine) {
case WAIT_HEAD:
if(rx_buf[0] == 0x55) state_machine = WAIT_CMD;
break;
// 其他状态处理...
}
}
os_time_dly(1);
}
}
4.3 设备间通信Demo
与蓝牙模块通信的完整示例:
-
硬件连接:
- AC696N_UART1_TX → HC05_RX
- AC696N_UART1_RX → HC05_TX
- 共地连接
-
AT指令交互代码:
c复制void send_at_command(const char *cmd)
{
uart_send_data(UART1, (u8 *)"AT", 2);
uart_send_data(UART1, (u8 *)cmd, strlen(cmd));
uart_send_data(UART1, (u8 *)"\r\n", 2);
}
void bt_init_sequence()
{
send_at_command("+NAME=MySpeaker");
send_at_command("+PSWD=1234");
send_at_command("+UART=115200,0,0");
}
5. 性能优化与问题排查
5.1 波特率精度优化
AC696N的UART时钟源来自PLL_48M,实际波特率计算公式为:
code复制实际波特率 = 48000000 / (16 * DIV)
当需要115200波特率时:
code复制DIV = 48000000/(16*115200) = 26.041667
取整后实际波特率 = 48000000/(16*26) ≈ 115384.6 (误差0.16%)
实测发现当误差超过2%时会出现数据错误,建议使用以下常用波特率:
- 9600 (DIV=312.5 → 实际9615)
- 115200 (DIV=26 → 实际115384)
- 230400 (DIV=13 → 实际230769)
5.2 典型问题解决方案
问题1:接收数据丢帧
现象:高速传输时丢失部分字节
解决方法:
- 检查FIFO阈值设置:
c复制uart_set_rx_fifo_thres(UART0, 16); // 收到16字节触发中断
- 提升接收任务优先级
- 使用DMA模式(需修改驱动)
问题2:打印信息乱码
排查步骤:
- 确认双方波特率误差<2%
- 用逻辑分析仪抓取TX信号波形
- 检查PCB走线是否过长(建议<10cm)
6. 进阶开发技巧
6.1 多串口负载均衡方案
当系统需要同时处理调试打印和设备通信时:
c复制// 将调试信息分流到UART0
#define debug_printf(fmt...) do { \
if(debug_port == 0) uart_send_data(UART0, fmt); \
else uart_send_data(UART1, fmt); \
} while(0)
// 关键通信使用专用UART1
void comm_send(u8 *data, u16 len)
{
uart_send_data(UART1, data, len);
}
6.2 低功耗模式优化
在电池供电设备中:
- 动态关闭串口时钟
c复制void uart_suspend(u32 uart_id)
{
JL_UART->CON &= ~(1<<0); // 关闭UART
clk_disable(CLK_UART0); // 关闭时钟
}
- 唤醒后重新初始化
- 使用硬件流控(CTS/RTS)控制数据流
7. 实战案例:蓝牙音箱固件开发
7.1 生产测试接口设计
通过UART实现自动化测试:
- 测试协议设计:
code复制// PC → 设备 [0xAA][0x55][CMD][PARAM][CRC] // 设备 → PC [0x55][0xAA][STATUS][DATA...][CRC] - 典型测试项:
- 按键检测(发送0x01)
- LED测试(发送0x02 0x01开启)
- 音频环路测试(发送0x03启动)
7.2 固件升级方案
通过UART实现IAP升级:
- 设计Bootloader流程:
mermaid复制graph TD A[上电] --> B{按键按下?} B -->|是| C[进入升级模式] B -->|否| D[跳转主程序] C --> E[接收新固件] E --> F[校验并写入Flash] F --> G[重启] - 升级协议要点:
- 每包数据包含128字节+2字节CRC
- 使用YMODEM协议简化开发
- 关键代码保护区写保护
在真实项目中,我发现AC696N的UART在连续传输大数据量时(如固件升级),需要特别注意Flash擦写期间的时序控制。一个实用的技巧是在每包数据写入后添加10ms延时,这样可以避免Flash操作阻塞串口接收导致缓冲区溢出。