为了方便对UART驱动进行测试,特意开发了通用UART测试工具,该工具同时也可用于UART硬件测试和UART应用开发参考。
简要说明
- 命令行解析基于cmdParse模块,这个模块的功能和用法会单独说明。
- 命令每次只针对一个UART端口进行操作。
- 该命令运行后会创建3个执行线程,接收线程,反射线程和发送线程。
- -help 选项是个通用选项,会输出相应命令的选项列表。
- -info 选项用于打印命令默认运行参数。
- -f 选项指定UART设备文件名称,使用
ll /dev/
命令可以查看当前系统有哪些串口设备。 - -d [on/off] 选项用于控制是否打印运行信息。对于较高频率的动作执行时一般要关闭调试信息。
- -e [on/off]选项用于控制是否开启接收反射功能。开启后会把收到的数据原样发送出去。
- -b 选项用于设置串口波特率,默认为115200。
- -o 选项用于设置串口校验方式,默认无校验。
- -i 选项用于设置发送间隔,单位是毫秒,默认为1000毫秒。
- -n 选项用于设置发送次数。
- -w 选项用于设置发送数据内容。后跟uint8_t类型数据数组。
命令执行
显示选项列表
显示命令默认配置
只接收数据
接收并反射数据
发送数据
程序源码
/*********************************************************************************************************
**
** 中国软件开源组织
**
** 嵌入式实时操作系统
**
** SylixOS(TM) LW : long wing
**
** Copyright All Rights Reserved
**
**--------------文件信息--------------------------------------------------------------------------------
**
** 文 件 名: uartAccessCmd.c
**
** 创 建 人: Hou.JinYu (侯进宇)
**
** 文件创建日期: 2017 年 10 月 17 日
**
** 描 述: 通用串口测试工具
*********************************************************************************************************/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "cmdParse/cmdParse.h"
/*********************************************************************************************************
宏定义
*********************************************************************************************************/
#define CFG_DEFAULT_BUF_SIZE (64) /* 默认缓存大小 */
#define CFG_DEFAULT_BUF_COUNT (32) /* 默认缓存个数 */
/*********************************************************************************************************
宏函数
*********************************************************************************************************/
#if 1
#define PRINT_DEBUG(fmt, ...) do {printf(fmt, ##__VA_ARGS__);} while (0)
#else
#define PRINT_DEBUG(fmt, ...)
#endif
#define PRINT_ERROR(fmt, ...) do {printf("[ERROR]%s:%d---", __FILE__, __LINE__); \
printf(fmt, ##__VA_ARGS__); } while (0)
#define PRINT_DATA_UINT8(ptr, len, fmt, ...) \
do {UINT j; \
printf(fmt, ##__VA_ARGS__); \
for (j = 0; j < (len); j++) { \
if ((j & 0x0f) == 0) {printf("\r\n[%04x]", j);} \
printf(" %02x", ((UINT8 *)ptr)[j]); \
} printf("\n");} while (0)
/*********************************************************************************************************
设备配置参数结构
*********************************************************************************************************/
typedef struct {
UINT32 uiEchoState; /* 0 不反射 1反射接收的数据 */
UINT32 uiDisplayState; /* 0 不显示收发数据 1显示 */
INT iFd; /* 设备文件标识符 */
pthread_t tidRecv; /* 接收线程ID */
pthread_t tidEcho; /* 反射线程ID */
pthread_t tidSend; /* 发送线程ID */
LW_OBJECT_HANDLE hQueueSend; /* 消息队列句柄 */
CHAR cFileName[64]; /* 波特率 */
UINT32 uiBaud; /* 波特率 */
UINT32 uiOption; /* 硬件选项 */
UINT32 uiRecvCount; /* 串口接收数据计数 */
UINT32 uiEchoCount; /* 串口反射数据计数 */
UINT32 uiSendCount; /* 串口发送数据计数 */
UINT32 uiSendInterval; /* 发送间隔,单位毫秒 */
UINT32 uiSendNumber; /* 串口发送次数 */
INT32 uiBufLength; /* 缓冲区有效数据长度 */
UINT8 ucBuf[CFG_DEFAULT_BUF_SIZE]; /* 缓冲区 */
} DEV_ARG_ST;
/*********************************************************************************************************
** 名称: getInfo
** 输入: cmd 命令参数
** opti 参数索引
** 输出: 错误码,NONE成功,ERROR错误
** 说明: 获取 info 选项
*********************************************************************************************************/
static int getInfo (command_st *cmd, int opti)
{
DEV_ARG_ST *pArg;
pArg = cmd->priv;
printf("echo = %d\n", pArg->uiEchoState);
printf("display = %d\n", pArg->uiDisplayState);
printf("iFd = %d\n", pArg->iFd);
printf("tidRecv = %08x\n", (UINT)pArg->tidRecv);
printf("tidEcho = %08x\n", (UINT)pArg->tidEcho);
printf("tidSend = %08x\n", (UINT)pArg->tidSend);
printf("file = %s\n", pArg->cFileName);
printf("uiBaud = %d\n", pArg->uiBaud);
printf("uiOption = %08x\n", pArg->uiOption);
printf("recvCount= %d\n", pArg->uiRecvCount);
printf("echoCount= %d\n", pArg->uiEchoCount);
printf("sendCount= %d\n", pArg->uiSendCount);
printf("interval = %d\n", pArg->uiSendInterval);
printf("number = %d\n", pArg->uiSendNumber);
printf("uiLength = %d\n", pArg->uiBufLength);
if (pArg->uiBufLength) {
PRINT_DATA_UINT8(pArg->ucBuf, pArg->uiBufLength, "send data :");
}
return (ERROR);
}
/*********************************************************************************************************
** 名称: getClose
** 输入: cmd 命令参数
** opti 参数索引
** 输出: 错误码,NONE成功,ERROR错误
** 说明: 获取 Option 选项
*********************************************************************************************************/
static int getOption (command_st *cmd, int opti)
{
option_st *opt;
DEV_ARG_ST *pArg;
char *str;
pArg = cmd->priv;
opt = &cmd->optv[opti];
if (opt->argc <= 0) {
return (NONE);
}
str = cmd->argv[opt->index + 1];
if (strcmp(str, "none") == 0) {
pArg->uiOption = CREAD | CS8;
} else if (strcmp(str, "odd") == 0) {
pArg->uiOption = CREAD | CS8 | PARENB | PARODD;
} else if (strcmp(str, "even") == 0) {
pArg->uiOption = CREAD | CS8 | PARENB;
} else {
pArg->uiOption = strtoul(str, NULL, 0);
}
return (NONE);
}
/*********************************************************************************************************
** 名称: devPthreadRecv
** 描述: 串口接收线程
** 输入: pvArg 参数结构
** 输出: ERROR_CODE
*********************************************************************************************************/
static void *devPthreadRecv (void * pvArg)
{
INT res;
UINT8 buf[CFG_DEFAULT_BUF_SIZE];
DEV_ARG_ST *pArg = (DEV_ARG_ST *)pvArg;
while(1) {
res = read(pArg->iFd, buf, sizeof(buf));
if (res > 0) { /* 接收数据 */
pArg->uiRecvCount += res;
if (pArg->uiEchoState) {
Lw_MsgQueue_SendEx2(pArg->hQueueSend,
(PVOID)buf,
res,
LW_OPTION_NOT_WAIT,
LW_OPTION_DEFAULT);
}
if (pArg->uiDisplayState) {
PRINT_DATA_UINT8(buf, res, "[uart %s] recv %d byte, data :", pArg->cFileName, res);
}
} else {
PRINT_ERROR("read err,res = %d \n", res);
break;
}
}
return (NULL);
}
/*********************************************************************************************************
** 名称: devPthreadEcho
** 描述: 串口反射线程
** 输入: pvArg 参数结构
** 输出: ERROR_CODE
*********************************************************************************************************/
static void *devPthreadEcho (void * pvArg)
{
INT res;
size_t txLen;
UINT8 buf[CFG_DEFAULT_BUF_SIZE];
DEV_ARG_ST *pArg = (DEV_ARG_ST *)pvArg;
while(1) {
res = Lw_MsgQueue_Receive(pArg->hQueueSend,
buf,
CFG_DEFAULT_BUF_SIZE,
&txLen,
LW_OPTION_WAIT_INFINITE);
if (res < 0) {
PRINT_ERROR("Lw_MsgQueue_Receive,res = %d", res);
break;
} else { /* 接收数据 */
if (pArg->uiDisplayState) {
pArg->uiEchoCount += txLen;
write(pArg->iFd, buf, txLen);
PRINT_DATA_UINT8(buf, txLen, "[uart %s] echo %d byte, data :", pArg->cFileName, txLen);
}
}
}
return (NULL);
}
/*********************************************************************************************************
** 名称: devPthreadSend
** 描述: 串口发送线程
** 输入: pvArg 参数结构
** 输出: ERROR_CODE
*********************************************************************************************************/
static void *devPthreadSend (void * pvArg)
{
DEV_ARG_ST *pArg = (DEV_ARG_ST *)pvArg;
while(pArg->uiBufLength && pArg->uiSendNumber--) {
if (pArg->uiDisplayState) {
PRINT_DATA_UINT8(pArg->ucBuf, pArg->uiBufLength,
"[uart %s] send %d byte, data :", pArg->cFileName, pArg->uiBufLength);
}
pArg->uiSendCount += pArg->uiBufLength;
write(pArg->iFd, pArg->ucBuf, pArg->uiBufLength);
usleep(pArg->uiSendInterval * 1000);
}
return (NULL);
}
/*********************************************************************************************************
** 名称: main
** 输入: argc 参数个数
** argv 参数列表
** 输出: 错误码,NONE成功,ERROR错误
** 说明: 通用串口测试工具
*********************************************************************************************************/
static int uartTool (int argc, char *argv[])
{
DEV_ARG_ST devArg = {
.uiEchoState = 0,
.uiDisplayState = 1,
.iFd = -1,
.cFileName = "/dev/ttyS1",
.uiBaud = SIO_BAUD_115200,
.uiOption = CREAD | CS8,
.uiRecvCount = 0,
.uiEchoCount = 0,
.uiSendCount = 0,
.uiSendInterval = 1000,
.uiSendNumber = 0,
.uiBufLength = 0,
.ucBuf = { 0 },
};
option_st optv[] = {
{ATT_BUF_SIZE(sizeof(devArg.cFileName)) |
ATT_TYPE_SBUF, &devArg.cFileName, "f", "set uart device file name"},
{ATT_TYPE_BOOL, &devArg.uiDisplayState, "d", "set dispaly state"},
{ATT_TYPE_BOOL, &devArg.uiEchoState, "e", "set echo state"},
{ATT_TYPE_U32, &devArg.uiBaud, "b", "set baudrate"},
{ATT_FUNC, getOption, "o", "set option, none/odd/even/0x0e"},
{ATT_TYPE_U32, &devArg.uiSendInterval, "i", "set send interval, ms"},
{ATT_TYPE_U32, &devArg.uiSendNumber, "n", "set send number"},
{ATT_ARRAY(GET_ARRAY_COUNT(devArg.ucBuf)) |
ATT_TYPE_U8, &devArg.ucBuf, "w", "set send data", &devArg.uiBufLength},
{ATT_FUNC, getInfo, "info", "print cmd info"},
};
command_st cmd;
DEV_ARG_ST *pArg = &devArg;
int err;
LW_CLASS_THREADATTR hThreadAttr;
cmd.argc = argc;
cmd.argv = argv;
cmd.optc = GET_OPTC(optv);
cmd.optv = optv;
cmd.priv = &devArg;
err = cmdParse(&cmd);
if (err != NONE) {
return (err);
}
pArg->hQueueSend = Lw_MsgQueue_Create(
"uartQueue",
CFG_DEFAULT_BUF_COUNT,
CFG_DEFAULT_BUF_SIZE,
LW_OPTION_WAIT_FIFO | LW_OPTION_OBJECT_LOCAL,
NULL);
pArg->iFd = open(pArg->cFileName, O_RDWR, 0666);
if (pArg->iFd < 0) {
PRINT_ERROR("open device [%s] faild\n", pArg->cFileName);
return (PX_ERROR);
}
ioctl(pArg->iFd, FIOFLUSH, NULL); /* 清空设备收发缓冲区 */
if (ioctl(pArg->iFd, SIO_BAUD_SET, pArg->uiBaud) != ERROR_NONE) {
PRINT_ERROR("set baudrate faild\n");
close(pArg->iFd);
return (PX_ERROR);
}
if (ioctl(pArg->iFd, SIO_HW_OPTS_SET, pArg->uiOption) != ERROR_NONE) {
PRINT_ERROR("set opt faild\n");
close(pArg->iFd);
return (PX_ERROR);
}
Lw_ThreadAttr_Build(&hThreadAttr, 4 * LW_CFG_KB_SIZE, LW_PRIO_NORMAL + 0, 0, (void *)pArg);
Lw_Thread_Create("t_uartRecv", devPthreadRecv, &hThreadAttr, &pArg->tidRecv);
Lw_ThreadAttr_Build(&hThreadAttr, 4 * LW_CFG_KB_SIZE, LW_PRIO_NORMAL + 1, 0, (void *)pArg);
Lw_Thread_Create("t_uartEcho", devPthreadEcho, &hThreadAttr, &pArg->tidEcho);
Lw_ThreadAttr_Build(&hThreadAttr, 4 * LW_CFG_KB_SIZE, LW_PRIO_NORMAL + 2, 0, (void *)pArg);
Lw_Thread_Create("t_uartSend", devPthreadSend, &hThreadAttr, &pArg->tidSend);
Lw_Thread_Join(pArg->tidRecv, NULL);
Lw_Thread_Join(pArg->tidEcho, NULL);
return (ERROR_NONE);
}
/*********************************************************************************************************
END
*********************************************************************************************************/