DSP28335 SCI 串口回显功能案例解析
#include "DSP2833x_Device.h"
#include "DSP2833x_Examples.h"// Function prototypes (函数声明,统一英文命名风格)
void scia_echoback_init(void);
void scia_fifo_init(void);
void scia_xmit(int a);
void scia_send_str(char *str); // renamed from "scia_msg" for clarity// Global count variables (全局计数变量)
Uint16 LoopCount; // Counts total echoed characters (统计总回显字符数)
Uint16 ErrorCount; // Reserved for error monitoring (预留用于错误统计)void main(void)
{Uint16 ReceivedChar; // Stores single received character (存储单个接收字符)char *init_prompt1; // Initial welcome prompt (初始欢迎提示)char *init_prompt2; // Function explanation prompt (功能说明提示)char *input_prompt; // Loop input prompt (循环输入提示)// Step 1: Initialize system control (PLL, watchdog, peripheral clocks)// 步骤1:初始化系统控制(锁相环、看门狗、外设时钟)InitSysCtrl();// Step 2: Initialize SCI-A GPIO pins (TX: GPIO28, RX: GPIO29)// 步骤2:初始化SCI-A引脚(发送脚GPIO28,接收脚GPIO29)InitSciaGpio();// Step 3: Initialize interrupts (disable all to avoid interference)// 步骤3:初始化中断(关闭所有中断,避免干扰)DINT; // Disable global CPU interrupts (禁用全局CPU中断)InitPieCtrl(); // Reset PIE control registers to default (复位PIE控制寄存器)IER = 0x0000; // Disable all CPU interrupts (禁用所有CPU中断)IFR = 0x0000; // Clear all CPU interrupt flags (清空所有CPU中断标志)InitPieVectTable(); // Initialize PIE vector table (初始化PIE向量表)// Step 4: Initialize user variables (初始化用户变量)LoopCount = 0;ErrorCount = 0;// Step 5: Initialize SCI FIFO and core parameters// 步骤5:初始化SCI FIFO和核心参数scia_fifo_init();scia_echoback_init();// Step 6: Send initial welcome prompts (发送初始欢迎提示)init_prompt1 = "\r\n\n\nHello! SCI Echo Test (DSP28335)\0"; // 欢迎语+功能标识init_prompt2 = "\r\nSend any characters/strings - DSP will echo them back.\0"; // 功能说明input_prompt = "\r\nEnter input: \0"; // 循环输入提示scia_send_str(init_prompt1);scia_send_str(init_prompt2);scia_send_str(input_prompt);// Infinite loop: Continuous receive and echoUint16 recv_count = 0; // 统计本次接收的总字符数// 无限循环:持续接收并回显for(;;){Uint16 has_enter = 0; // 标记是否收到回车符// 1. 读取FIFO中所有字符,同时检查是否有回车// 读取FIFO所有字符,同时计数while(SciaRegs.SCIFFRX.bit.RXFFST > 0){ReceivedChar = SciaRegs.SCIRXBUF.all;scia_xmit(ReceivedChar);if(ReceivedChar == 0x0D) has_enter = 1;recv_count++; // 每读1个字符,计数+1LoopCount++;}// 2. 仅当收到回车时,才发送下一次输入提示if(has_enter == 1){scia_send_str("\r\nEnter input: "); // 换行后提示,格式更清晰}// 3. 等待下一批数据(避免空循环占用CPU)while(SciaRegs.SCIFFRX.bit.RXFFST == 0) { }}
}// SCI Core Initialization (9600 baud, 8-bit data, no parity, 1 stop bit)
// SCI核心初始化(9600波特率,8位数据位,无校验,1位停止位)
void scia_echoback_init()
{// SCI Communication Control Register (SCI通信控制寄存器)SciaRegs.SCICCR.all = 0x0007; // 8-bit data, 1 stop bit, no parity, async mode// 8位数据,1位停止位,无校验,异步模式// SCI Control Register 1 (SCI控制寄存器1)SciaRegs.SCICTL1.all = 0x0003; // Enable TX/RX, internal clock; disable RX error/sleep// 使能发送/接收,内部时钟;禁用接收错误检测/睡眠模式// SCI Control Register 2 (SCI控制寄存器2)SciaRegs.SCICTL2.all = 0x0003; // Enable TX/RX interrupts (reserved for future use)// 使能发送/接收中断(预留后续扩展使用)SciaRegs.SCICTL2.bit.TXINTENA = 1; // Enable TX interrupt (使能发送中断)SciaRegs.SCICTL2.bit.RXBKINTENA = 1;// Enable RX interrupt (使能接收中断)// Baud Rate Configuration (based on CPU frequency)// 波特率配置(根据CPU频率选择)#if (CPU_FRQ_150MHZ)SciaRegs.SCIHBAUD = 0x0001; // 9600 baud @ LSPCLK = 37.5MHz (150MHz CPU)SciaRegs.SCILBAUD = 0x00E7;#endif#if (CPU_FRQ_100MHZ)SciaRegs.SCIHBAUD = 0x0001; // 9600 baud @ LSPCLK = 25.0MHz (100MHz CPU)SciaRegs.SCILBAUD = 0x0044;#endifSciaRegs.SCICTL1.all = 0x0023; // Release SCI from reset (启动SCI,解除复位)
}// Transmit single character via SCI (通过SCI发送单个字符)
void scia_xmit(int a)
{// Wait until transmit FIFO is empty (避免发送溢出,等待发送FIFO为空)while (SciaRegs.SCIFFTX.bit.TXFFST != 0) {}SciaRegs.SCITXBUF = a; // Write character to transmit buffer (写入发送缓冲区)
}// Transmit string via SCI (循环调用单字符发送函数,实现字符串发送)
void scia_send_str(char *str)
{int i = 0;// Send each character until null terminator ('\0') is reached// 循环发送字符,直到遇到字符串结束符('\0')while(str[i] != '\0'){scia_xmit(str[i]);i++;}
}// SCI FIFO Initialization (SCI FIFO初始化)
void scia_fifo_init()
{SciaRegs.SCIFFTX.all = 0xE040; // Enable TX FIFO, clear TX FIFO (使能发送FIFO,清空缓冲区)SciaRegs.SCIFFRX.all = 0x2001; // Enable RX FIFO, threshold = 1 (使能接收FIFO,阈值=1)SciaRegs.SCIFFCT.all = 0x0; // Disable FIFO timeout (禁用FIFO超时功能)
}
一、代码整体层次结构
DSP28335 SCI 串口回显代码遵循 “系统基础→外设配置→驱动封装→应用逻辑” 的嵌入式开发层次逻辑,各层职责明确、依赖关系清晰,具体分层如下:
层次 | 核心职责 | 关键代码 / 函数 |
应用逻辑层 | 实现串口回显业务(发送欢迎提示、等待接收、回显数据、统计计数) | main() 函数中的无限循环、交互逻辑 |
驱动封装层 | 封装 SCI 硬件操作,提供简洁的软件接口(单字符发送、字符串发送) | scia_xmit()、scia_send_str() |
外设配置层 | 配置 SCI 核心参数(通信协议、波特率)和 FIFO(缓冲区阈值、使能) | scia_echoback_init()、scia_fifo_init() |
系统基础层 | 初始化系统运行环境(时钟、GPIO 引脚、中断屏蔽),为 SCI 提供硬件前提 | InitSysCtrl()、InitSciaGpio()、中断初始化函数 |
硬件抽象层 | 直接操作 SCI 寄存器,连接软件与硬件(如 FIFO 状态位、数据缓冲区) | SciaRegs 结构体(SCIFFTX、SCIFFRX、SCITXBUF等) |
二、SCI 初始化完整流程
初始化是 SCI 正常工作的前提,需按 “系统→GPIO→中断→SCI 参数→FIFO” 的顺序执行,避免因硬件依赖导致配置失效,具体步骤如下:
1. 系统控制初始化(InitSysCtrl())
- 核心作用:配置 DSP 主频(如 150MHz/100MHz)、使能 SCI 外设时钟、禁用看门狗。
- 关键细节:SCI 模块默认处于 “时钟关闭” 状态,InitSysCtrl() 会开启 SCI 的外设时钟(LSPCLK,如 150MHz 主频下 LSPCLK=37.5MHz),否则后续 SCI 寄存器操作全部无效。
2. GPIO 引脚初始化(InitSciaGpio())
- 核心作用:将指定 GPIO 配置为 SCI 功能,建立物理通信通路。
- 具体配置:DSP28335 的 SCI-A 默认对应引脚为:
- TX 引脚:GPIO28(SCITXDA),配置为 “外设功能模式”,禁用通用 IO 输出;
- RX 引脚:GPIO29(SCIRXDA),配置为 “外设功能模式”,禁用通用 IO 输入。
- 为什么必须配置:若 GPIO 保持默认的 “通用 IO 模式”,SCI 发送的电信号无法通过 TX 引脚输出,接收的信号也无法通过 RX 引脚传入硬件缓冲区。
3. 中断初始化(屏蔽无关中断)
- 核心作用:避免未使用的中断干扰 SCI 通信(如 ADC、SPI 中断)。
- 关键操作:
- DINT:禁用全局 CPU 中断,防止初始化过程中被中断打断;
- InitPieCtrl():复位 PIE(外设中断扩展模块)控制寄存器,禁用所有 PIE 中断;
- IER=0x0000、IFR=0x0000:清空 CPU 中断使能和标志寄存器;
- InitPieVectTable():初始化 PIE 向量表,将未使用的中断映射到默认空函数。
4. SCI 核心参数配置(scia_echoback_init())
- 核心作用:设定 SCI 的通信协议,确保与接收设备(如电脑串口助手)一致,是串口通信的 “规则约定”。
- 关键配置项(寄存器级):
寄存器 | 配置值 | 功能说明 |
SCICCR.all | 0x0007 | 8 位数据位、1 位停止位、无校验、异步模式(标准串口配置,匹配电脑默认设置) |
SCICTL1.all | 0x0003 | 使能 TX(发送)和 RX(接收)功能,禁用睡眠模式和接收错误中断(简化调试) |
SCICTL2.all | 0x0003 | 使能 TX 和 RX 中断(预留扩展,当前代码未使用中断,仅为后续功能准备) |
SCIHBAUD+SCILBAUD | 分主频配置 | 9600 波特率:150MHz 主频下为0x0001+0x00E7,100MHz 主频下为0x0001+0x0044 |
SCICTL1.all | 0x0023 | 释放 SCI 复位状态(初始化最后一步,相当于 “启动” SCI 模块) |
5. SCI FIFO 配置(scia_fifo_init())
- 核心作用:启用 SCI 的 FIFO(先进先出缓冲区),减少 CPU 等待时间,避免数据丢失。
- 关键配置项(寄存器级):
寄存器 | 配置值 | 功能说明 |
SCIFFTX.all | 0xE040 | 使能发送 FIFO(E0 bit)、清空发送 FIFO(40 bit),确保初始无残留数据 |
SCIFFRX.all | 0x2001 | 使能接收 FIFO(20 bit)、设置接收 FIFO 阈值为 1(01 bit),即收到 1 个字符就可读取 |
SCIFFCT.all | 0x00 | 禁用 FIFO 超时功能(简化调试,默认无需启用) |
三、SCI 发送 “1234” 的底层原理(含SCIFFTX.bit.TXFFST解析)
发送 “1234” 的本质是 “软件写入数据→FIFO 缓冲→硬件串行发送”,全程由SciaRegs寄存器协作完成,其中SCIFFTX.bit.TXFFST是核心状态位。
1. 发送流程拆解(以scia_send_str("1234")为入口)
步骤 1:字符串发送函数调用
scia_send_str("1234") 会循环调用 scia_xmit() 函数,逐个发送字符 '1'(ASCII 码0x31)、'2'(0x32)、'3'(0x33)、'4'(0x34)。
步骤 2:单字符发送(scia_xmit('1'))
- 核心操作 1:等待发送 FIFO 为空(SCIFFTX.bit.TXFFST == 0)
scia_xmit() 中 while (SciaRegs.SCIFFTX.bit.TXFFST != 0) {} 的作用:
- SCIFFTX.bit.TXFFST 是发送 FIFO 的填充计数位(0~16,16 为 FIFO 满),值为 0 表示 FIFO 无数据,可写入新字符;
- 若 FIFO 已满(TXFFST=16),直接写入会导致数据溢出(新数据覆盖旧数据),因此必须等待 FIFO 为空。
- 核心操作 2:写入发送缓冲区(SCITXBUF)
执行 SciaRegs.SCITXBUF = '1'(即写入0x31):
- 硬件检测到SCITXBUF有新数据,自动将数据移入发送 FIFO;
- 此时 SCIFFTX.bit.TXFFST 从 0 变为 1(FIFO 中存入 1 个字符)。
步骤 3:硬件自动串行发送
发送 FIFO 有数据后,SCI 硬件模块无需 CPU 干预,自动执行以下操作:
- FIFO 取数:从发送 FIFO 中 “弹出”'1'(0x31),送入串行发送逻辑单元;
- 并行→串行转换:将 8 位并行数据0x31(二进制00110001)转换为串口帧:起始位(0)+ 数据位(00110001)+ 停止位(1)(共 10 位);
- 按波特率发送:9600 波特率下,每 bit 发送时间约 104μs,硬件按此时序将 10 位帧逐位输出到 TX 引脚(GPIO28);
- 更新 FIFO 状态:'1'发送完成后,发送 FIFO 的填充计数自动减 1,SCIFFTX.bit.TXFFST 从 1 变回 0,等待下一个字符写入。
步骤 4:循环发送剩余字符
- 发送'2'、'3'、'4'时,重复步骤 2~3;
- 若 CPU 写入速度快于硬件发送速度(如连续写入 4 个字符),发送 FIFO 会短暂缓存数据,SCIFFTX.bit.TXFFST 会依次变为 1→2→3→4,硬件发送时再逐次减为 0,实现 “批量缓冲 + 连续发送”。
四、SCI 接收 “1234” 的底层原理(含SCIFFRX.bit.RXFFST解析)
接收 “1234” 的本质是 “硬件串行接收→FIFO 缓冲→软件读取数据”,核心状态位为SCIFFRX.bit.RXFFST。
1. 接收流程拆解(以main()函数中的无限循环为入口)
步骤 1:等待接收 FIFO 有数据(SCIFFRX.bit.RXFFST > 0)
main() 中 while(SciaRegs.SCIFFRX.bit.RXFFST == 0) {} 的作用:
- SCIFFRX.bit.RXFFST 是接收 FIFO 的填充计数位(0~16),值为 0 表示 FIFO 无数据,需等待外部设备发送;
- 当电脑通过串口发送 “1234” 时,RX 引脚(GPIO29)会逐位接收串行信号,硬件自动将其转换为并行数据。
步骤 2:硬件自动接收并缓存到 FIFO
- 串行→并行转换:硬件接收 RX 引脚的串行帧(如'1'的 10 位帧),剥离起始位和停止位,提取 8 位数据0x31;
- FIFO 缓存:将0x31存入接收 FIFO,此时 SCIFFRX.bit.RXFFST 从 0 变为 1(匹配 FIFO 阈值 1);
- 连续接收:接收'2'、'3'、'4'时,FIFO 依次缓存数据,SCIFFRX.bit.RXFFST 依次变为 2→3→4。
步骤 3:软件读取 FIFO 数据
main() 中 while(SciaRegs.SCIFFRX.bit.RXFFST > 0) 循环读取数据:
- 读取操作:执行 ReceivedChar = SciaRegs.SCIRXBUF.all,从接收缓冲区读取 1 个字符(如'1');
- 更新 FIFO 状态:读取后,接收 FIFO 的填充计数自动减 1,SCIFFRX.bit.RXFFST 从 4→3→2→1→0;
- 回显数据:调用 scia_xmit(ReceivedChar),将读取的字符回传给电脑,完成 “接收→回显” 闭环。
步骤 4:检测回车并提示下一次输入
若用户发送 “1234” 后按回车(ASCII 码0x0D),代码会检测到ReceivedChar == 0x0D,设置has_enter=1,随后发送"Enter input: ",等待下一次接收。
五、核心状态位总结
状态位 | 功能描述 | 发送 / 接收场景下的关键作用 |
SCIFFTX.bit.TXFFST | 发送 FIFO 填充计数(0~16) | 发送时:等待其为 0,确保 FIFO 有空位,避免数据溢出;发送中:观察其变化,判断 FIFO 缓存状态 |
SCIFFRX.bit.RXFFST | 接收 FIFO 填充计数(0~16) | 接收时:等待其 > 0,确保有数据可读取;接收中:观察其变化,判断是否有残留数据未读取 |
通过以上层次解析、初始化流程和底层原理,可清晰理解 DSP28335 SCI 串口回显功能的实现逻辑,以及核心寄存器的作用,为后续扩展(如中断发送、多字节通信)奠定基础。