文章目录
04.TMS570LC43入门指南——串口操作
一、简介
通过上节的学习,我们使用按键进行操作控制LED亮灭。这节我们将使用MCU中最重要的外设 串口。
由于从这里开始,需要的寄存器就开始变多了,我并不能一一介绍,望谅解,另外 HALCoGen 生成的串口方面的代码也较为完整,故大家可以不用重复造轮子了。
通过这篇文章你将学习到以下内容:
- TMS570LC43 串口的了解认识
- TMS570LC43 的串口(SCI)的使用(基础收发以及 printf 的使用)
开发平台:
- Windows x64
- TMS570LC43开发板
二、项目实现
在本小节开始之前,大家需要在 CCS 和 HALCoGen 中新建工程文件,具体操作可以参考我前面的文章,链接如下:
其中也详细描述了如何建立工程,欢迎各位大佬阅读。
关于串口方面要介绍一点知识,在 TMS570LC43
平台上,串口并不是我们所熟知的 UART
,而是 SCI/LIN
。
UART | SCI | |
---|---|---|
介绍 | UART(Universal Asynchronous Receiver / Transmitter)是一种可以在两台设备之间进行异步数据传输的硬件接口。它是一种多用途的技术,可以用于在计算机、控制器和其他设备之间传输数据。UART可以支持单向和双向的数据传输,允许两台设备之间的无线数据传输。 | SCI(Serial Communication Interface)是一种可以在两台设备之间进行 高速数据传输的硬件接口。它是一种多用途的技术,可以用于在计算机、控制器和其他设备之间传输数据。SCI可以支持单向和双向的数据传输,允许两台设备之间的无线数据传输。 |
优点 | 它可以在没有任何外部时钟信号的情况下运行,只需要两台设备之间共享一个时钟即可。此外,UART还具有低成本、低功耗和灵活性等优点 | 它可以实现高速数据传输,其传输速率可达4Mbps。此外,SCI还具有低成本、低功耗和灵活性等优点 |
缺点 | 它只能传输低速数据。它的传输速率最高可达115200位/秒,而SCI可以传输高达4Mbps的数据。此外,UART还受到距离限制,其最大传输距离不能超过15米。 | 它需要一个外部时钟信号才能正常工作,这会增加系统的成本和复杂性。此外,SCI还受到距离限制,其最大传输距离不能超过50米。 |
通过对上表的理解,我们可以知道,SCI
比 UART
更有优势,其数据传输速度更快,更稳定。而 LIN
接口专业来讲是 局域互联网络(Local Interconnect Network),常用在汽车工业中,而 LIN
仅用于低速网络,如门控制单元等,我们一般都会选用 CAN
总线去代替,因为 CAN
总线在可靠性与传输速度上均优于 LIN
。
我们在这章中主要介绍SCI
的相关操作。
2.1 硬件部分
在本节内容中,将使用板载的串口进行实验。
如没有原理图可点击: TMS570LC43x and RM57Lx LaunchPad Schematic(源自官网可放心跳转)。
可以在芯片侧看到如下所示的引脚:
注意: 我们使用的是
SCI1
,它与LIN1
是同一个引脚不同功能而已。
跟随查找,我们可以在 XDS110
上找到对应的引脚(当然该开发板也引出了 SCI1
),如下所示:
这里补充说一句话,
XDS110
是板载的下载芯片,它同时继承了串口功能。
最终通过 XDS110
的引脚输出到USB接口上,如下所示:
其在板子上的位置如下所示:
本文采用图中 ① 所示的USB接口进行实验。通过将USB插入电脑后,大家可以查看电脑设备管理器(步骤不再赘述,不会的朋友可以自行百度),找到串口对应的 COM
接口,如下所示:
其中我图中对应的 COM22
为 UART
接口,可以通过其设备名字看出,而 COM21
为下载烧录端口。
2.2 软件部分
2.2.1 HALCoGen 配置
生成完工程之后依旧先取消勾选 Driver Enable
中的 Mark/Unmak all drivers
,然后按照如下步骤进行:
-
勾选
Enbale SCI1 driver
: -
到
SCI1 -> SCI Data Format
菜单中按照所需设置(我这里设置的经典参数:115200 8bits数据位 1bit停止位 无奇偶校验): -
配置完上述功能后,即可生成程序。依旧使用快捷键
F5
或依次点击File -> Generate Code
。
2.2.2 CCS 代码编写
按照上述步骤后,生成文件,依旧在 CCS
中进行编写逻辑代码的操作。还是先找到我们的 HL_sys_main.c
文件。这里有两部分内容,分为直接使用函数进行输出 和 使用 printf 函数进行输出。
2.2.2.1 使用函数输出
这里给出示例代码:
/* USER CODE BEGIN (0) */
/* USER CODE END */
/* Include Files */
#include "HL_sys_common.h"
/* USER CODE BEGIN (1) */
#include "HL_system.h"
#include "HL_sci.h"
#include <stdio.h>
#include <string.h>
/* USER CODE END */
/** @fn void main(void)
* @brief Application main function
* @note This function is empty by default.
*
* This function is called after startup.
* The user can use this function to implement the application.
*/
/* USER CODE BEGIN (2) */
#define SCI_REG sciREG1 // 定义 sci 端口寄存器
#define CNT 5000000
void sci_Printf(char *format, ...);
/* USER CODE END */
int main(void)
{
/* USER CODE BEGIN (3) */
int i;
int count = 0;
sciInit();
while(1)
{
count++; // 计数
sci_Printf("Hello world! count = %d\r\n", count); // 使用自定义 printf 函数输出
for (i = 0; i < CNT; i++); // 粗略延时
}
/* USER CODE END */
return 0;
}
/* USER CODE BEGIN (4) */
/*
* @brief : 自定义SCI printf 函数
* @param : 字符串,可实现类似于 printf 的参数输入
* @return : void
* @author : Liu Jiahao
* @date : 2024-03-26
* @version : v1.1
* @copyright : Copyright By Liu Jiahao, All Rights Reserved
*/
void sci_Printf(char *format, ...)
{
uint16 i;
va_list listdata;
uint8 sci_TxBuff[100];
va_start(listdata, format);
vsprintf((char *)sci_TxBuff, format, listdata);
va_end(listdata);
for (i = 0; i < strlen((const char*)sci_TxBuff); i++) {
while ((SCI_REG->FLR & 0x04U) == 4U);
sciSendByte(SCI_REG, sci_TxBuff[i]);
}
}
/* USER CODE END */
上述代码并不是使用自带的 printf
函数,而是使用我们自定义的 sci_Printf()
函数,其实现原理可以参考C语言的 printf
实现。大家可以借鉴一下这种写法。
题外话:会使你代码执行更有效率吗?并不,只会更让别人看不懂,显得高级!!!哈哈哈!!!
言归正传,可以在主函数中看到,我调用该函数依旧可以使用类似于 printf
函数的传参形式,这相比于死板的写固定参数更加灵活,当然了,只要是 printf
中能用的参数,这里也可以使用 。
而通过串口助手,配置我们之前设置的波特率等参数,下载烧录代码运行后即可看到:
2.2.2.2 使用自带的 pritnf 函数输出
为了方便,这里我就不再重建工程了,仍然使用上面的工程进行修改,首先我们需要一些配置操作:
-
依次在项目工程右键后点击
Properties -> Build -> Arm Compiler -> Advanced Options -> Language Options
,然后设置level of printf/scanf ......
为minimal
,如下所示: -
依旧是刚才的页面,点击
Debug
后使能CIO
,如下所示: -
依次点击
Build -> Arm Linker -> Basic Options
增加heap
空间,保证其大于 320bytes,如下所示:
然后点击 Apply and Close
即可配置完成,接下来我给出示例代码:
/* USER CODE BEGIN (0) */
/* USER CODE END */
/* Include Files */
#include "HL_sys_common.h"
/* USER CODE BEGIN (1) */
#include "HL_system.h"
#include "HL_sci.h"
#include <stdio.h>
#include <string.h>
/* USER CODE END */
/** @fn void main(void)
* @brief Application main function
* @note This function is empty by default.
*
* This function is called after startup.
* The user can use this function to implement the application.
*/
/* USER CODE BEGIN (2) */
#define SCI_REG sciREG1 // 定义 sci 端口寄存器
// sci 模式参数,为 0 则为不采用 printf 形式, 为 1 则采用 printf 形式
#define SCI_MODE 1
#define CNT 5000000
#if (!SCI_MODE)
void sci_Printf(char *format, ...);
#endif
/* USER CODE END */
int main(void)
{
/* USER CODE BEGIN (3) */
int i;
int count = 0;
sciInit();
while(1)
{
count++; // 计数
#if (!SCI_MODE)
sci_Printf("Hello world! count = %d\r\n", count); // 使用自定义 printf 函数输出
#else
printf("Hello world! count = %d\r\n", count); // 使用 printf 函数输出
#endif
for (i = 0; i < CNT; i++); // 粗略延时
}
/* USER CODE END */
return 0;
}
/* USER CODE BEGIN (4) */
#if (!SCI_MODE)
/*
* @brief : 自定义SCI printf 函数
* @param : 字符串,可实现类似于 printf 的参数输入
* @return : void
* @author : Liu Jiahao
* @date : 2024-03-26
* @version : v1.1
* @copyright : Copyright By Liu Jiahao, All Rights Reserved
*/
void sci_Printf(char *format, ...)
{
uint16 i;
va_list listdata;
uint8 sci_TxBuff[100];
va_start(listdata, format);
vsprintf((char *)sci_TxBuff, format, listdata);
va_end(listdata);
for (i = 0; i < strlen((const char*)sci_TxBuff); i++) {
while ((SCI_REG->FLR & 0x04U) == 4U);
sciSendByte(SCI_REG, sci_TxBuff[i]);
}
}
#else
/* fputs 函数 */
int fputs(const char *_ptr, FILE *_fp)
{
unsigned int i, len;
len = strlen(_ptr);
for (i = 0; i < len; i++)
{
while((SCI_REG->FLR & (uint32)SCI_TX_INT) == 0U);
sciSendByte(SCI_REG, _ptr[i]);
}
return len;
}
/* fputc 函数 */
int fputc(int ch, FILE *f)
{
while((SCI_REG->FLR & (uint32)SCI_TX_INT) == 0U);
sciSendByte(SCI_REG, ch);
return ch;
}
#endif
/* USER CODE END */
这部分就类似于 STM32
,依旧要实现 fputs
和 fputc
函数。通过控制 SCI_MODE
宏定义的参数可以实现两种方式的发送。
通过串口助手,得到结果如下所示:
2.2.2.3 串口接收
既然我们已经通过前两种方式实现了串口发送,那么我们将继续实现接收的相关功能。这里值得注意的是,由于我们未开启中断,故实现过程中,必须要等待接收字节完成后程序才能继续执行。
下一篇文章中,我将介绍如何使用中断进行发送和接收。
我们依旧使用上次的工程,示例代码如下所示:
/* USER CODE BEGIN (0) */
/* USER CODE END */
/* Include Files */
#include "HL_sys_common.h"
/* USER CODE BEGIN (1) */
#include "HL_system.h"
#include "HL_sci.h"
#include <stdio.h>
#include <string.h>
/* USER CODE END */
/** @fn void main(void)
* @brief Application main function
* @note This function is empty by default.
*
* This function is called after startup.
* The user can use this function to implement the application.
*/
/* USER CODE BEGIN (2) */
#define SCI_REG sciREG1 // 定义 sci 端口寄存器
// sci 模式参数,为 0 则为不采用 printf 形式, 为 1 则采用 printf 形式
#define SCI_MODE 0
#define CNT 5000000
uint8 RxDataBuf[2];
#if (!SCI_MODE)
void sci_Printf(char *format, ...);
#endif
void printBuf(uint16 len, uint8 * dat);
/* USER CODE END */
int main(void)
{
/* USER CODE BEGIN (3) */
int i;
int count = 0;
sciInit();
while(1)
{
memset(&RxDataBuf[0], 0, 2); // 清空缓冲区
sciReceive(SCI_REG, 2, RxDataBuf);
printBuf(2, RxDataBuf);
for (i = 0; i < CNT; i++); // 粗略延时
}
/* USER CODE END */
return 0;
}
/* USER CODE BEGIN (4) */
/*
* @brief : 打印输出数组
* @param : [len]: 数组长度
* [dat]: 数组指针
* @return : void
* @author : Liu Jiahao
* @date : 2024-03-26
* @version : v1.1
* @copyright : Copyright By Liu Jiahao, All Rights Reserved
*/
void printBuf(uint16 len, uint8 * dat)
{
while (len--) {
while ((SCI_REG->FLR & 0x04U) == 4U);
sciSendByte(SCI_REG, *dat++);
}
}
#if (!SCI_MODE)
/*
* @brief : 自定义SCI printf 函数
* @param : 字符串,可实现类似于 printf 的参数输入
* @return : void
* @author : Liu Jiahao
* @date : 2024-03-26
* @version : v1.1
* @copyright : Copyright By Liu Jiahao, All Rights Reserved
*/
void sci_Printf(char *format, ...)
{
uint16 i;
va_list listdata;
uint8 sci_TxBuff[100];
va_start(listdata, format);
vsprintf((char *)sci_TxBuff, format, listdata);
va_end(listdata);
for (i = 0; i < strlen((const char*)sci_TxBuff); i++) {
while ((SCI_REG->FLR & 0x04U) == 4U);
sciSendByte(SCI_REG, sci_TxBuff[i]);
}
}
#else
/* fputs 函数 */
int fputs(const char *_ptr, FILE *_fp)
{
unsigned int i, len;
len = strlen(_ptr);
for (i = 0; i < len; i++)
{
while((SCI_REG->FLR & (uint32)SCI_TX_INT) == 0U);
sciSendByte(SCI_REG, _ptr[i]);
}
return len;
}
/* fputc 函数 */
int fputc(int ch, FILE *f)
{
while((SCI_REG->FLR & (uint32)SCI_TX_INT) == 0U);
sciSendByte(SCI_REG, ch);
return ch;
}
#endif
/* USER CODE END */
对于以上代码,我首先需要解释的是,由于我们没有封装接收处理,故只能按接收到的字节数判断接收是否完成。这里我定义的是接收2个字节,然后在把接收到的数据发送出去。另外值得注意的是,我没有使用之前定义的函数,而是使用了新构建的函数 printBuf
,这是由于之前构建的函数对于字符串的发送是较为方便的,而像数组这种,处理起来就不够方便,所以我重新写了这个函数实现数组发送。
然后我们使用串口助手发送两字节数据,得到以下内容:
如图所示,我采用 16进制 发送两个字节数据,接收到的仍然是这两个数据。图中有三组是因为我发了三次的原因,并不是有 bug。
至此我们就完成了串口操作的基本实验。
三、写在最后
本文介绍了 如何在TMS570LC43上进行串口收发的操作。
在后续的文章中,将继续对 TMS570LC43x 进行详细的入门指导,欢迎读者关注!!!
目前暂时没有考虑整合的打算,所以各位读者如果需要看别的教程,可以点进 专栏 进行查找。在后续的更新中,将会逐步加入各个文章的链接,以便大家快速翻阅。另外源码会逐步开源。
欢迎广大读者提出问题以及修改意见,本人看到后会给予回应,欢迎留言,后续会逐步进行开源!!!
另外,由于文章是作者手打的文字,有些地方可能文字会出错,望谅解,也可私信联系我,我对其进行更改。
-
个人CSDN账号:刘梓谦_-CSDN博客
-
GitHub:Jiahao-Liu29 (github.com)