7. TI MSPM0L1306串口通信实战:基于SysConfig与中断的UART0收发配置详解
7. TI MSPM0L1306串口通信实战:基于SysConfig与中断的UART0收发配置详解
最近在TI电赛开发板上做项目,串口通信是调试和与外界交互最常用的功能。很多刚开始接触TI MSPM0系列(比如咱们用的MSPM0L1306)的朋友,可能会对如何使用CCS里的图形化工具SysConfig来配置串口感到陌生。今天,我就手把手带大家走一遍完整的流程,从时钟配置、引脚分配到中断编程,实现一个“收到什么就回发什么”的数据回环实验。只要你跟着步骤做,保证能让开发板上的串口0(UART0)和电脑顺畅地“对话”。
1. 准备工作与核心概念扫盲
在动手配置之前,咱们先快速过一遍几个关键概念,理解它们能让你后面的操作更清晰。
串口通信,简单说就是设备之间一根线发数据,另一根线收数据,按位(bit)来传输。虽然速度比不上并口,但它接线简单,成本低,远距离通信更有优势。要让两个设备能“聊上天”,必须约定好相同的通信参数,主要是这四个:
- 波特率:通信的速度,比如9600,表示每秒传输9600个比特。
- 数据位:一个数据帧里实际有效数据的位数,常见的是8位。
- 停止位:用来表示一个数据帧传输结束,通常是1位。
- 校验位:用于简单的错误检查,比如奇校验或偶校验,如果不需要可以选“无”。
咱们的MSPM0L1306芯片有两个串口:UART0和UART1。在TI电赛开发板上,UART0的引脚(PA22和PA23)已经连接到了板载的CH340E芯片上。CH340E是一个USB转串口芯片,所以你只需要用一根USB Type-C线连接开发板和电脑,电脑上就会出现一个串口设备(比如COM3),非常方便。
注意:PA23这个引脚比较特殊,它同时是串口0的RX引脚、参考电压引脚和BSL(引导加载程序)串口引脚。开发板默认通过一个0欧姆电阻(R24)将它配置为串口功能。如果你后续项目需要用到参考电压功能,记得把这个电阻去掉。
2. 使用SysConfig图形化配置UART0
TI在Code Composer Studio (CCS)里提供的SysConfig工具非常好用,像搭积木一样配置外设,能自动生成初始化代码,省去了我们手动查手册、算寄存器值的麻烦。
2.1 创建工程与打开SysConfig
首先,在CCS里新建一个空的工程(Empty Project)。工程建好后,在左侧的工程资源管理器里,你会看到一个名为工程名.syscfg的文件,双击打开它,SysConfig的配置界面就出来了。
2.2 配置串口时钟源(MFCLK)
串口要工作,必须有时钟驱动。MSPM0L1306的串口时钟可以从三个来源选:
- BUSCLK:内部高频振荡器提供的CPU主时钟,默认通常是32MHz。
- MFCLK:固定的4MHz时钟。我们本次实验就选用这个。
- LFCLK:内部低频振荡器提供的32KHz时钟,用于低功耗场景。
要使用MFCLK,我们需要在SysConfig里先把它“打开”。
- 在SysConfig左侧选项卡中找到并点击SYSCTL。
- 在右侧的选项页中,找到Use Clock Tree并勾选,这样就能配置时钟树了。
- 勾选后,左侧会出现三个图标,点击中间那个时钟树图标。
- 在展开的时钟树图中,找到SYSOSC_4M这一分支,点击其上的选项块,将其状态设置为Enabled。
- 设置成功后,你会看到右侧对应位置显示频率为4.0000MHz。这就说明MFCLK时钟源已经准备好了。
2.3 添加并配置UART0外设
时钟搞定,现在来配置串口本身。
在SysConfig左侧的“Peripherals”或类似选项卡下,找到UART,点击ADD按钮添加一个UART实例。
添加后,右侧会出现详细的配置页面。我们需要设置以下几项:
- Instance: 选择UART0。
- Pin Configuration: 配置引脚。根据数据手册,UART0对应PA22和PA23。在配置页中找到引脚设置,将TX选择为PA22,RX选择为PA23。SysConfig会自动帮你配置引脚的复用功能。
- Basic Settings(基本设置):
- Baud Rate (波特率):9600
- Data Length (数据位):8 bits
- Stop Bits (停止位):1 bit
- Parity (校验位):None
- Hardware Flow Control (硬件流控制):Disabled
- Advanced Settings(高级设置):
- 这里有个关键参数Oversampling(过采样率)。它有3、8、16三个档位。这个值会影响波特率计算的精度。我们的时钟源是4MHz,为了能正确计算出9600波特率的分频系数而不出错,这里选择16。一个简单的原则:只要SysConfig界面底部没有报错(红色错误提示),你的配置就是可行的。
- Interrupt Settings(中断设置):
- 因为我们打算用中断方式接收数据,所以需要开启接收中断。找到接收中断相关的选项(如RX Interrupt),将其使能(Enabled)。
所有配置完成后,记得按
Ctrl+S保存这个.syscfg文件。保存后,SysConfig会自动在后台生成对应的C语言初始化代码(在ti_msp_dl_config.c和.h文件中),我们等下直接调用就行。
3. 编写代码:实现中断接收与回发
配置工具帮我们做好了硬件初始化,但数据怎么收、怎么发,还得我们写代码来实现。我们要实现的功能是:电脑通过串口调试助手发送一个字符给开发板,开发板收到后,立刻把这个字符再发回给电脑。
3.1 主函数与中断使能
打开包含main()函数的那个C文件(比如main.c或empty.c),开始编写代码。
#include "ti_msp_dl_config.h" volatile unsigned char uart_data = 0; // 用于存储接收到的字符 void uart0_send_char(char ch); // 声明发送单个字符的函数 void uart0_send_string(char* str); // 声明发送字符串的函数 int main(void) { // 1. 初始化所有在SysConfig中配置的外设(包括UART0) SYSCFG_DL_init(); // 2. 清除UART0可能已有的中断挂起标志,避免一开中断就误触发 NVIC_ClearPendingIRQ(UART_0_INST_INT_IRQN); // 3. 使能UART0的中断(NVIC是中断控制器) NVIC_EnableIRQ(UART_0_INST_INT_IRQN); // 4. 发送一个启动提示信息 uart0_send_string("uart0 start work\r\n"); // 5. 主循环,这里什么都不做,等待中断发生 while (1) { // 可以在这里添加其他任务 } }代码解析:
SYSCFG_DL_init():这个函数是SysConfig生成的,必须调用,它负责按照我们之前的图形化配置,初始化芯片的时钟、GPIO、UART等所有外设。NVIC_ClearPendingIRQ和NVIC_EnableIRQ:这是操作中断控制器的标准流程。先清除旧的中断标志,再打开这个中断的“开关”。UART_0_INST_INT_IRQN这个宏定义在ti_msp_dl_config.h里,代表UART0的中断号。volatile关键字:修饰uart_data是因为它会在中断服务函数中被修改,在主循环中可能被读取或判断。volatile告诉编译器不要优化这个变量,确保每次访问都从内存中读取最新值。
3.2 串口发送函数
我们需要两个实用的发送函数:发单个字符和发字符串。
// 串口发送单个字符 void uart0_send_char(char ch) { // 等待串口0发送器空闲(不忙)。这是防止数据覆盖丢失的关键! while( DL_UART_isBusy(UART_0_INST) == true ); // 发送字符 DL_UART_Main_transmitData(UART_0_INST, ch); } // 串口发送字符串 void uart0_send_string(char* str) { // 判断字符串指针有效且未到结尾(C语言字符串以'\0'结尾) while(*str != 0 && str != 0) { // 发送当前字符,然后指针指向下一个字符 uart0_send_char(*str++); } }提示:
DL_UART_isBusy这个等待循环非常重要。想象一下,如果你连续调用发送函数,而前一个字节还没从硬件发送出去,后一个字节就写入了发送缓冲区,就会导致数据丢失。这个函数就是检查硬件是否就绪的“哨兵”。
3.3 串口中断服务函数
这是核心部分。当串口收到一个字节的数据时,硬件会自动触发中断,CPU就会跳转到这里来执行。
// UART0的中断服务函数 —— 函数名是固定的,不能改! void UART_0_INST_IRQHandler(void) { // 获取是哪种串口中断触发了本次调用 switch( DL_UART_getPendingInterrupt(UART_0_INST) ) { case DL_UART_IIDX_RX: // 如果是接收中断 // 从接收数据寄存器中读取一个字节 uart_data = DL_UART_Main_receiveData(UART_0_INST); // 将收到的数据原样发送回去 uart0_send_char(uart_data); break; default: // 其他类型的串口中断(如发送完成中断),本例中不处理 break; } }关键点:
- 函数名必须准确:
UART_0_INST_IRQHandler这个名字是芯片启动文件或驱动库定义好的,写错了中断就进不来了。你可以在工程生成的ti_msp_dl_config.h文件里找到这个函数名的宏定义。 - 中断类型判断:使用
DL_UART_getPendingInterrupt来查询具体的中断源。我们只关心接收中断 (DL_UART_IIDX_RX)。 - 读取数据:
DL_UART_Main_receiveData函数会读取并清除接收缓冲区,同时也会清除相应的中断标志位。
3.4 实验验证
将代码编译、下载到开发板。打开电脑上的串口调试助手(如XCOM、SSCOM等),选择开发板对应的串口号(COMx),设置波特率9600、8数据位、1停止位、无校验。
点击发送一个字符或一串字符,你应该能在接收区看到开发板回送的一模一样的数据。同时,第一次上电或复位时,会收到uart0 start work的启动信息。
4. 进阶技巧:重定向printf函数
每次都调用uart0_send_string来输出调试信息有点麻烦。如果能直接用printf函数,那可就方便多了。这就需要我们“重定向”标准输出到串口。
#include "stdio.h" // 必须包含这个头文件 // 重定向fputc函数,使printf的字符输出到串口 int fputc(int ch, FILE *stream) { while( DL_UART_isBusy(UART_0_INST) == true ); DL_UART_Main_transmitData(UART_0_INST, (char)ch); return ch; } // 重定向fputs函数(可选,但更高效) int fputs(const char* restrict s, FILE* restrict stream) { uint16_t char_len = 0; while(*s != 0) { while( DL_UART_isBusy(UART_0_INST) == true ); DL_UART_Main_transmitData(UART_0_INST, *s++); char_len++; } return char_len; }把上面这段代码加到你的工程里,之后你就可以在代码中愉快地使用printf(“温度值:%d\r\n”, temperature);了,输出会自动通过UART0发送到串口助手。
踩坑提醒:如果你在
printf中使用了浮点数格式(如%f),程序可能会卡死。这是因为默认的工程优化设置可能不支持完整的浮点库。解决方法是在CCS的工程属性中,将优化等级从O2暂时调整为O0(不优化)。具体路径是:右键工程 -> Properties -> CCS Build -> Optimization -> Optimization Level -> None (-O0)。
