串口通信
本节介绍
📝本节您将了解 串口通信 的协议以及使用方法,了解 F28P550 的串口外设 SCI,通过图形化代码生成工具配置实现串口发送与接收功能。
🏆本章⽬标
1️⃣了解串口通信。
2️⃣了解主控 F28P550 的 串口外设SCI。
3️⃣了解如何通过图形化代码生成工具 sysconfig 配置串口外设。
4️⃣应用以上知识配置串口实现发送与接收功能。
串口通信介绍
串口通信(Serial Communication)是一种通过单根数据线 逐位 传输数据的通信方式,属于异步串行通信协议。尽管传输速度比并行传输低。但串口可以在使用一根线发送数据的同时用另一根线接收数据。 这种通信方式使用的数据线少,在远距离通信中可以节约通信成本。串口通信最重要的参数是波特率
、数据位
、停止位
和奇偶校验位
,这些参数在两个通信端口之间必须一致
。
串口通信参数介绍
串口通信参数包括波特率(Baud Rate)、数据位(Data Bits)、校验位(Parity Bits)、停止位(Stop Bits)等。这些参数描述了传输数据的基本规格。例如,波特率定义了数据传输的速率,数据位确定每个数据字节中包含的位数,校验位用于数据的差错检测,停止位表示数据传输结束的标志等。
波特率
:衡量通信速度的参数,它表示每秒钟传送的 bit 的个数。数据位
:衡量通信中实际数据位的参数,表示一个信息包里包含的数据位的个数。停止位
:用于表示单个信息包的最后位,典型值为 1、1.5 和 2 位。由于数据是在传输线上传输的,每个设备都有自己的时钟,很有可能在通信过程中出现不同步,停止位不仅仅表示传输的结束,还能提供校正时钟同步的机会。停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率也越慢。奇偶检验位
:表示一种简单的检查错误的方式。
串口通信协议定义了在串口上进行数据交换的规则和格式。常见的串口通信协议包括ASCII协议、Modbus协议、RS-232协议等。协议规定了数据的帧结构、数据格式、校验方式等,确保发送和接收双方按照相同的规则进行数据交换,从而实现数据的正确传输和解析。
串口通信是一位一位地传输
,每传输一个字符总是以起始位开始,以停止位结束,字符之间没有固定的时间间隔要求。每一个字符的前面都有一位起始位(低电平),后面由 7 位数据位组成,接着是一位校验位,最后是停止位。停止位后面是不定长的空闲位,停止位和空闲位都规定为高电平。
![]() |
---|
串口通信的作用及优势
串口是计算机和外部设备之间最常见的通信接口之一,具有重要的作用和广泛的应用。在计算机领域,串口的重要性体现在以下几个方面:
数据传输
:串口是一种常用的数据传输接口。通过串口,计算机可以与各种外部设备进行数据交换和通信。无论是传感器、执行器、显示器、打印机还是其他外部设备,串口通信都可以实现数据的传输和控制。远程控制和监控
:串口通信被广泛应用于远程控制和监控领域。通过串口,计算机可以远程控制设备的动作,并实时监测设备的状态和数据信息。这在工业控制、自动化系统、远程监控等场景中具有重要的作用。调试和故障排查
:串口通信是调试和故障排查的重要工具。通过串口,计算机可以与嵌入式系统、单片机等进行通信,实时监控和调试程序,输出调试信息,进行错误定位和排查,并对系统进行状态监测和故障诊断。硬件连接
:串口可以作为计算机与各种外部设备之间的连接桥梁。通过串口,可以连接和控制各类外部设备,如传感器、执行器、外围设备等。串口能够提供稳定的数据传输和双向通信功能。通信协议
:串口通信协议是计算机与外部设备之间数据传输的规范和约定。通过定义不同的协议,可以实现不同设备之间的数据交互和通信。常见的串口通信协议有UART、RS-232、RS-485等。
总之,串口对于计算机和外部设备之间的通信具有重要的作用。它是数据传输、远程控制和监控、调试和故障排查的关键工具,是计算机与外部设备连接和通信的桥梁。通过串口通信,可以实现与各种外部设备的数据交互,提高系统的功能和性能。
F28P550的串口介绍
TMS320F28P550集成了三个高性能串行通信接口(SCI),分别是SCI-A、SCI-B、SCI-C,它们支持全双工异步通信,每个接口独立工作。在开发板上的支持串口的引脚见下方的开发板引脚示意图:(橙色的SCI)
![]() |
---|
![]() |
![]() |
串口通信参数
- 波特率范围:最高支持
12.5Mbps
(具体取决于系统时钟分频配置)。
数据格式:
数据位
:5 - 9 位可编程停止位
:1 / 1.5 / 2 位可选校验位
:无校验 / 奇校验 / 偶校验硬件流控制
:支持RTS(请求发送)和CTS(清除发送)信号,防止数据溢出。
![]() |
---|
串口的硬件连接
在开发板的下载接口处,引出了GPIO28和GPIO29引脚,这两个引脚是支持SCI的,GPIO28=SCIA-RX,GPIO29=SCIA-TX。
我们可以通过XDS110下载器的RXD和TXD接到这两个引脚上。不过需要注意的是,串口需要交叉连接,开发板的 RX 接到 XDS110下载器的 TX,开发板的 TX 接到 XDS110下载器的 RX。
案例实验介绍
实现串口的发送与接收,并将接收到的数据再发送出去实现回显功能。
工程创建
打开CCS,创建一个新的基于 F28P55X 的工程。
![]() | ![]() |
---|---|
工程配置
配置工程选项,将我们后面写好的代码烧录到 FLASH
中,并且使用的烧录模式是 cJTAG(1149.7)2-pin
模式。
![]() | ![]() |
---|---|
串口通信的配置
打开工程下的 .syscfg 文件。找到 SCI 选项开始配置:
配置串口参数为波特率 115200、8 位数据长度、1 位停止位、无校验位。
开启串口发送与接收功能,并开启串口接收中断。
使用 SCIA-RX = GPIO28 和 SCIA-TX = GPIO29。
![]() | ![]() |
---|---|
SCI基本选项说明
序号 | 名称 | 功能 |
---|---|---|
1 | Name | 为当前配置的SCI(串口)起一个名字 |
2 | Baud Rate | 配置的当前SCI的波特率 |
3 | Actual Baud Rate Value | 实际波特率值,与Baud Rate联动,由软件生成无法修改 |
4 | Baud Rate Error Percent [%] | 波特率误差百分比[%],与Baud Rate联动,由软件生成无法修改 |
5 | Use Loopback Mode | 是否使用环回模式(在 Loopback 模式下,发送端(TX)的数据会被直接连接到接收端(RX),形成内部闭环通路) |
6 | Word Length | 配置串口数据位长度 |
7 | Stop Mode | 配置串口停止位长度 |
8 | Parity Mode | 配置串口校验位长度 |
9 | Use Interrupt | 是否使用中断 |
10 | Use FIFO | 是否使用数据缓存机制(用于在发送和接收数据时暂存数据流,减少中断频率提升吞吐量) |
11 | Select Interrupt Handlers to register | 选择中断处理程序并进行注册,有Transmit发送中断和Receive接收中断 |
12 | Enabled Interrupts | 使能开启什么中断,有Transmit发送中断和Receive接收中断 |
13 | Enabled Error Interrupts | 配置使能串口错误中断,有接收错误、奇偶校验错误、帧错误、超限错误 |
14 | Use Case | 外设使用配置,有自定义、只发送、只接收、既发送也接收 |
SCI的中断配置说明
序号 | 名称 | 功能 |
---|---|---|
1 | Name | 为当前配置的SCI(串口)中断起一个名称 |
2 | Interrupt Name | 显示当前中断名称,由软件生成无法修改 |
3 | Interrupt Handler | 注册中断服务函数,添加中断服务函数的函数名 |
4 | Enable Interrupt in PIE | 使能开启中断的PIE |
注意,配置开启了中断服务函数的配置后,后面在写代码时,需要根据配置的中断服务函数名称编写对应的中断程序。根据上图的配置,到时候在代码中加入串口接收中断函数即可:
__interrupt void INT_mySCI0_RX_ISR(void)
{
//要在中断实现的功能
//清除接收中断标志位
SCI_clearInterruptStatus(SCIA_BASE, SCI_INT_RXFF);
//清除中断标志位
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);
}
2
3
4
5
6
7
8
9
相关函数介绍
//*****************************************************************************
//
//! 等待并通过指定串口发送一个字符数组。
//!
//! \param base 是SCI端口的基地址。
//! \param array 是要发送的字符数组地址。
//! 这是一个指向待发送字符数组的指针。
//! \param length 是数组的长度,即需要发送的字符数量。
//!
//! 从指定端口的发送缓冲区(如果启用了发送FIFO则为FIFO)中发送从\e array地址开始的\e length个字符。
//! 如果发送缓冲区或FIFO没有可用空间,该函数将阻塞等待直到有足够空间,并在所有\e length个字符成功发送后返回。
//! 注意:\e array虽然是指向uint16_t的指针,但只有最低有效8位会被写入SCI端口,因为SCI仅支持8位字符传输。
//!
//! \return 无返回值。
//
//*****************************************************************************
void SCI_writeCharArray(uint32_t base, const uint16_t * const array, uint16_t length);
//*****************************************************************************
//
//! 在FIFO增强功能未启用时,阻塞式等待并读取指定端口的单个字符。
//!
//! \param base 是SCI端口的基地址。
//!
//! 从指定端口的接收缓冲区(非FIFO模式)获取字符。若缓冲区无可用字符,
//! 此函数将阻塞等待直到接收到数据后才返回。
//!
//! \return 返回从指定端口读取的字符,以\e uint16_t形式表示。
//
//*****************************************************************************/
static inline uint16_t SCI_readCharBlockingNonFIFO(uint32_t base)
/*****************************************************************************
//
//! 逐字节填充指定内存区域,常用于内存初始化与清零操作
//!
//! \param s 目标内存块的首地址指针,支持任意类型内存(如数组、结构体等)
//! \param c 填充值(将被转换为无符号字符,仅低8位有效)
//! \param n 需填充的字节数(单位:字节)
//!
//! 功能说明:
//! 1. 将内存块的前 n 个字节设置为 c 的ASCII码值[1,5,7](@ref)
//! 2. 典型用途包括:
//! - 清零操作:将 c 设为0以初始化内存或清除敏感数据[3,6](@ref)
//! - 字符填充:如将字符串缓冲区填充为特定字符(需保留终止符'\0')[2,5](@ref)
//! - 结构体初始化:快速清空复杂数据结构[3,8](@ref)
//!
//! 注意事项:
//! - 对非字符类型(如int数组)赋值非0/-1值时,可能导致非预期结果[2,7](@ref)
//! (因逐字节填充可能破坏多字节数据类型的存储结构)
//! - 避免溢出:确保 n 不超过目标内存的物理大小[2,6](@ref)
//! - 字符串操作时需手动处理终止符,避免覆盖导致未定义行为[5,7](@ref)
//!
//! \return 指向目标内存块的指针(即 s 的原始地址)
//
//*****************************************************************************/
void *memset(void *s, int c, size_t n);
/*****************************************************************************
//
//! 计算以空字符('\0')结尾的字符串的字节长度
//!
//! \param str 指向待计算字符串的指针,必须为以'\0'结尾的有效字符串
//!
//! 功能说明:
//! 1. 从起始地址遍历内存,直到遇到第一个'\0'字符为止,统计遍历的字节数
//! 2. 不包含终止符'\0'本身的长度
//! 3. 适用于ASCII、ISO-8859-1等单字节编码字符串
//!
//! 注意事项:
//! - 安全性:若传入未初始化的指针或非字符串内存区域,可能导致未定义行为[2,10](@ref)
//! - 多字节字符:对UTF-8等编码的字符串,返回值是字节数而非字符数(如"中文"返回6字节)[11,12](@ref)
//! - 性能影响:时间复杂度为O(n),高频调用时需考虑缓存计算结果[5,12](@ref)
//! - 与sizeof区别:sizeof计算数组总大小(含未使用空间),strlen统计实际字符数[6,10](@ref)
//!
//! \return 返回无符号整数类型 size_t,表示字符串的字节长度
//
//*****************************************************************************/
size_t strlen(const char *str);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
案例验证
更新主函数代码
更新工程的 empty_driverlib_main.c 文件为以下代码:
#include "driverlib.h"
#include "device.h"
#include "board.h"
#include "c2000ware_libraries.h"
#include "string.h" //使用 memset(), strlen() 需导入该文件
#define uart_rx_max 50 //定义最大接收长度
uint16_t rDataA[ uart_rx_max ]={0}; //定义串口接收数据的缓冲区
uint16_t rx_len = 0; //串口接收数据长度
uint16_t rx_flag = 0; //串口是否有接收到数据的标志位 =0没有 =1有
//任意毫秒的延时
void delay_ms(int x)
{
while(x--)
{
DEVICE_DELAY_US(1000);
}
}
void main(void)
{
Device_init();
Device_initGPIO();
Interrupt_initModule();
Interrupt_initVectorTable();
Board_init();
C2000Ware_libraries_init();
EINT;
ERTM;
while(1)
{
//如果串口接收标志位为1
if( rx_flag== 1 )
{
rx_flag=0;
//发送数据
SCI_writeCharArray(SCIA_BASE, rDataA, strlen(rDataA));
//清除数据
memset(rDataA, 0, sizeof(rDataA));
rx_len= 0;
}
delay_ms(1000);
}
}
__interrupt void INT_mySCI0_RX_ISR(void)
{
//没有开启FIFO的情况下,等待接收区有数据了就返回数据
rDataA[rx_len] = SCI_readCharBlockingNonFIFO(SCIA_BASE);
//回环长度限制
rx_len = ( rx_len + 1 ) % uart_rx_max;
//设置接收标志位为1
rx_flag = 1;
SCI_clearInterruptStatus(SCIA_BASE, SCI_INT_RXFF);
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
下载器连接
XDS110下载器 | 开发板 |
---|---|
SWD | SWD/TMS |
CLK | CLK/TCK |
GND | GND |
5V | 5V |
RXD | TX |
TXD | RX |
代码烧录
GIF 动图
案例现象
通过电脑端的串口调试助手发送数据给开发板,开发板收到后又返回数据。