本应用笔记和支持源代码提供了一个简单的便携式框架,用于使用串行传输(如RS-232)在嵌入式系统和运行MATLAB的PC之间实现实时数据传输。
介绍
嵌入式控制和测量系统通常可以从使用诸如此类工具的高级算法开发中受益 作为 MATLAB。为此,有必要将数据从嵌入式系统导出到PC。虽然数学作品 提供便于嵌入式算法开发的附加包,这些包可能很昂贵。 通常,只需要一种从嵌入式系统收集数据以进行基本分析的简单方法。®
本应用笔记和支持源代码为完成 使用串行传输在嵌入式系统和运行 MATLAB 的 PC 之间实时传输数据,例如 RS-232.
要求
本应用笔记所述架构的实现已在MAX35103EVKIT2#PCB上进行了测试,该PCB是MAX35103EVKIT2评估板软件的一部分。 但它可以很容易地移植到其他平台。建议使用MAX35103EVKIT2进行初始评估和 参考。
本应用笔记假设用户对 MATLAB、MATLAB MEX、C 语言和 Win32 API。在基于 IAR 系统的技术、基于 ARM 的系统和视觉C++方面的经验也是 有益的。®®®®
全面评估需要以下工具:
Maxim MAX35103EVKIT2评估板
微软视觉C++
MATLAB(无需额外的软件包)
适用于 ARM 的 IAR 嵌入式工作台
Microsoft Visual C++ Community Edition 可从 Microsoft 网站免费下载。IAR ARM 是 可从IAR系统进行评估。提供MAX35103EVKIT2评估板软件 来自Maxim以及Digi-Key和贸泽电子等许多电子分销商。MATLAB 可从 The Mathworks, Inc. 获得。
MAX35103EVKIT2实现示例
MAX35103EVKIT2评估板软件 被选中用于本应用笔记中描述的框架的初始实现。 MAX35103EVKIT2板由MAX32620(ARM Cortex M4)和MAX35103(超声波时间数字转换器)组成。 这些组件共同支持通过超声波流体收集液体流量测量值。 这些测量结果被格式化并传输到运行 MATLAB 的主机 PC。®
基本了解MAX35103EVKIT2评估板软件 MAX35103有助于理解框架传输的数据和主机/目标的格式 协议数据包。请参考MAX35103EVKIT2文档,了解嵌入式平台的详细信息。
MAX35103EVKIT2嵌入式目标向运行MATLAB的主机PC传输的数据是 MAX35103时间数字转换器该数据流是超声波脉冲发射和接收之间的时间测量数组。test.m MATLAB脚本(详见本文档后面)为从MAX35103EVKIT2板收集数据提供了快速起点。图1所示如何从MAX35103EVKIT2板检索和访问前<>个上游时间测量值。
图1.使用 test.m 的 MATLAB 数据收集示例
系统架构
使用此框架进行数据收集需要能够运行 MATLAB 的主机 PC 和具有 串行接口。此处提供的框架专门支持 RS-232,但可以轻松移植以支持其他接口(参见图 2)。
图2.主机/目标体系结构。
该框架跨越电脑主机和嵌入式目标。C 代码针对目标嵌入式系统编译为 以及主机系统。当前的主机实现需要 Win32 平台,但可以将其移植到 另一个操作系统,如Linux。®
嵌入式目标硬件必须提供某种串行接口。当前框架实现 支持 UART,但设计为可移植到其他传输。目标微控制器必须有足够的 支持框架数据和代码要求的资源,但也有足够的吞吐量来移动数据,而无需 过度影响嵌入式系统的性能。框架不需要RTOS,但它确实需要 不排除使用一个。
当前的框架实现是使用96MHz Cortex-M4处理器开发的,可以在 UART 接近最大吞吐量,处理器开销很小。较小的系统可能需要调整 框架可以接受地工作。
图 3 描述了主机和目标上的软件组件。蓝色组件是 C 语言模块 在主机上运行。红色组件是在目标上运行的 C 语言模块。绿色组件是 两个域通用。紫色的 MATLAB 脚本是标准的 m 脚本,可与 特定于应用程序的接口。此处描述的嵌入式框架实现是为在 MAX35103EVKIT2评估板软件,超声波水流量测量平台
灰色组件是特定于外部平台的组件。
图3.系统架构。
可以使用 C/C++ 和 4GL 语言与 Win32 DLL(如 C# 或 Python)交互,使用 “Win32 DLL I/F”模块。这允许轻松支持可能不需要 MATLAB 的自定义主机应用程序。
“COM”模块实现基本主机/目标协议。这是数据包定义特定于 找到嵌入式应用程序。“序列化”模块实现了二进制数据的基于转义的分组。 这两个模块都可以使用与通信API(主机)或嵌入式接口的回调轻松移植 外围设备(嵌入式目标)。
“COM”模块中的定义驱动“主机端协议”和“设备端”中的实现 协议“组件。这些实现是特定于主机和目标的命令/响应所在的位置 实现并且通常具有很多共性。
以下各节从上到下详细介绍了从主机开始的每个主要体系结构模块。
电脑主机架构
图 2 中描述的架构的主机端主要由 MATLAB 和特定于操作系统的接口组成, 并在以下各节中详细介绍。
MATLAB 脚本
主机堆栈的顶部是执行特定于应用程序的数据收集和控制的 MATLAB 脚本。 代码清单 1 中显示的脚本 test.m 是如何使用 MATLAB MEX 接口打开、设置 参数,并从MAX35103EVKIT2评估板软件收集数据。
h_flow = svflow('open',6);
if( h_flow )
svflow('start',h_flow,100);
samples = flow('get_samples',h_flow,1000);
svflow('stop',h_flow);
svflow('close',h_flow);
clear h_flow;
plot(samples.timestamp,samples.toff_diff)
hold on
transit = (samples.up.average + samples.down.average) ./ 2
yyaxis right
plot(samples.timestamp,transit)
hold off
else
error('failed to open com port');
end
代码清单 1.测试.m.
MEX 接口模块中只能存在一个公共函数。在本例中,它是 svflow()。这个函数是怎么 MATLAB 脚本调用 MEX 模块。函数的名称是任意的,但选择“svflow”作为 MAX35103EVKIT2评估软件上实现的整体主机/目标协议的名称值 (水流量测量平台)。
svflow() 的第一个参数是一个文本字符串,指示要调用的子函数。第二个参数是 引用特定流对象的处理对象。此对象由 svflow('open',...) 返回。这是基本的 框架用于适应 MEX 同时支持面向对象的体系结构的方法。
代码清单 1 中的 test.m 脚本调用子函数 'open' 来打开 Windows 主机上的 COM6。下一个 调用“start”以指定 100Hz 的采样率并开始样本收集。然后调用“get_samples” 以定义的采样率收集 1000 个样本。此同步集合完成后,流对象 已停止并关闭。MATLAB plot() 函数用于显示数据集和派生数据。
MATLAB MEX 接口
用于MAX35103EVKIT2评估板软件的MATLAB MEX接口元件 在一组 C 语言文件中实现。它可以访问内部 MATLAB 函数并公开一个标准 MATLAB 脚本可以调用的接口。所有特定于 MEX 的功能都包含在 mex.c 中,它提供了 特定于 MATLAB 的核心协议功能的包装器,用于在 flow.c 中实现,该功能也用作 Win32 DLL 接口和 serialize.c/com.c,这是主机和嵌入式目标通用的。
该模块是使用 MATLAB mex() 函数创建的。代码清单 2 中的 compile.m 脚本编译 将 C 文件托管为 MATLAB 可用的表单。此命令的输出是 MATLAB MEX 可执行文件。马特实验室 必须先前配置为使用本机工具链。访问 The Mathworks 网站以获取有关 如何设置用于编译 MEX 模块的工具链。
mex -g -output svflow -I'dll' -I'..' dll/mex.c dll/svflow.c ../serialize.c ../com.c
代码清单 2.编译.m.
MAX35103EVKIT2评估板软件专用的MATLAB MEX接口在mex.c中实现。MATLAB 要求所有 MEX 模块实现函数 mexFunction() 作为唯一函数 模块提供的功能的接口。为了给单个 MEX 模块提供一种提供多个面向对象成员函数的方法,使用了子函数机制。在代码清单 3 中,mexFunction() 引用函数调用表用于调度子函数。调用表本身如代码清单 4 所示。
for (i = 0; i < ARRAY_COUNT(s_function_table); i++)
{
if (!lstrcmpA(s_function_table[i].p_name, func))
{
s_function_table[i].p_func(nlhs, p_lhs, nrhs - 1, p_rhs + 1);
return;
}
}
代码清单 3.mexFunction() 子函数调度。
static const function_table_t s_function_table[] =
{
{ "get_samples", mex_get_samples },
{ "open", mex_open },
{ "close", mex_close },
{ "start", mex_start },
{ "stop", mex_stop }
};
代码清单 4.子函数调用表。
调用表中引用的 mex_* 函数是 Win32 DLL 函数的精简包装器,详见下文 部分。
MATLAB MEX 接口还以与 MATLAB的双矩阵定向性质。MATLAB MEX 接口返回的顶级对象是 MATLAB 具有以下字段的结构:
图4.顶级 MATLAB 数据对象。
双精度数组时间戳和toff_diff的大小是可变的。
向上和向下成员是具有以下格式的 MATLAB 结构:
图5.包含MAX35013时间测量值的MATLAB结构。
同样,每个数组的长度是可变的。该数据与MAX35103EVKIT35103评估板上MAX2输出的数据直接对应。
在 mex.c 中,函数 mex_get_samples() 使用 MATLAB mx* 格式化嵌入式目标接收的数据 功能。
static void mex_get_samples(int nlhs, mxArray *p_lhs[], int nrhs, const mxArray *p_rhs[])
{
char * sample_fieldnames[] =
{
"timestamp",
"up",
"down",
"toff_diff"
};
svflow_sample_t sample;
void **pp = (void*)mxGetData(p_rhs[0]); uint32_t sample_count = (uint32_t)mxGetScalar(p_rhs[1]);
mxArray *p_sample_struct = mxCreateStructMatrix( 1, 1, ARRAY_COUNT(sample_fieldnames), sample_fieldnames );
mxArray *p_timestamp = mxCreateNumericMatrix( 1, sample_count, mxDOUBLE_CLASS, mxREAL );
mxSetField( p_sample_struct, 0, "timestamp", p_timestamp );
mxSetField( p_sample_struct, 0, "up", create_direction_struct( sample_count, &sample.up ) );
mxSetField( p_sample_struct, 0, "down", create_direction_struct( sample_count, &sample.down ) );
mxArray *p_toff_diff = mxCreateNumericMatrix( 1, sample_count, mxDOUBLE_CLASS, mxREAL );
mxSetField( p_sample_struct, 0, "toff_diff", p_toff_diff );
sample.p_timestamp = (double_t*)mxGetData( p_timestamp );
sample.p_tof_diff = (double_t*)mxGetData( p_toff_diff );
svflow_get_samples( *pp, &sample, sample_count );
p_lhs[0] = p_sample_struct;
}
代码清单 5.mex_get_samples() 在墨西哥
“up”和“down”结构成员在 mex.c 的 create_direction_struct() 函数中构造。
Win32 DLL 接口
Win32 DLL 接口在 svflow.c 中实现,其中还包含大部分协议和平台 特定代码。与此应用笔记关联的源代码包包含一个 Visual Studio 项目,该项目可以是 用于生成 DLL。但是,MATLAB 不需要 DLL。它只是为了帮助那些对 使用可与 DLL 交互的语言编写自定义数据分析代码。®
下面的代码清单6显示了MAX35103EVKIT2评估板软件支持的DLL接口功能。这些函数与代码清单 1 中所示的 MATLAB 脚本中调用的子函数完全对应。
void* svflow_open( uint32_t comport);
void svflow_close(void *pv_context);
uint32_t svflow_get_samples(void *pv_context, flow_sample_t
*p_flow_sample, uint32_t sample_count);
void svflow_start( void *pv_context, float_t sample_rate_hz );
void svflow_stop( void *pv_context );
代码 6.- flow.h.
svflow_open () 返回与给定 Win32 COM 端口关联的不透明流通信上下文对象或 如果发生错误,则为 NULL。
svflow_close() 使用 svflow_open() 返回的上下文对象关闭通信并释放资源。
svflow_start() 告诉嵌入的目标开始以指定的采样率收集流样本。
svflow_stop() 告诉嵌入式目标结束数据收集。
这些功能特定于MAX35103EVKIT2评估板软件, 它们可以很容易地被适合其他嵌入式应用程序的功能所取代。
主机协议
主机和嵌入式目标使用的协议基于 com.c/h 中的通用定义和函数构建,并且 序列化.c/h.体系结构支持的协议通常由命令/响应和指示事件组成。 主机协议在 svflow.c 中实现,依赖于 com.c 和 serialize.c,这对于 主机和嵌入的目标。
主机端协议使用 com_* 函数发出命令并解码响应和指示。例如,在 代码清单 6, com_tx() 用于向嵌入式目标发送 'com_host_start_sampling_t' 命令数据包。
请务必注意,所有协议函数都是单线程阻塞调用。
void svflow_start( void *pv_context, float_t sample_rate_hz )
{
context_t *p_context = (context_t*)pv_context;
if( p_context )
{
com_host_start_sampling_t cmd;
cmd.sample_rate_hz = sample_rate_hz;
com_tx( &p_context->com, &cmd, COM_ID_HOST_START_SAMPLING,
sizeof( com_host_start_sampling_t ) );
}
}
代码清单 7.flow_start() 在 flow.c.
主机协议模块还定义了与传输通信的数据类型相对应的数据类型 链接,但与它们不完全相同。这种差异允许对 此模块和上述模块。具体来说,它将数据包格式解耦(简洁的单精度浮点数) 来自用于容纳 MATLAB(详细、面向矩阵的双精度)的数据格式。这意味着翻译 代码必须存在于 flow.c 中,如代码清单 7 中的序列化回调函数所示。
static bool serialize_cb(void *pv_context, const void *pv_data, uint16_t length)
{
context t * p_context = (context_t *)pv_context;
const com_union_t *p_packet = (const com_union_t*)pv_data;
if (p_packet->hdr.id == COM_ID_DEVICE_FLOW_SAMPLE )
{
com_device_flow_sample_t *p_com_sample =
(com_device_flow_sample_t*)&p_packet->flow_sample;
if (!p_context->sample_ndx )
{
p_context->time_offset = p_com_sample->timestamp;
}
svflow_sample_t *p_flow_sample = p_context>p_flow_sample;
uint32_t ndx = p_context->sample_ndx;
direction( &p_flow_sample->up, &p_com_sample->up, ndx );
direction( &p_flow_sample->down, &p_com_sample->down, ndx );
p_flow_sample->p_timestamp[ndx] = ( (double_t)( p_com_sample->timestamp –
p_context->time_offset ) ) / 96000000.0;
p_flow_sample->p_tof_diff[ndx] = p_com_sample->tof_diff;
p_context->sample_ndx++;
if( p_context->sample_ndx >= p_context->sample_count )
return true;
}
return false;
}
代码清单 8.flow.c 中的数据转换
svflow.c 还包含初始化和回调函数,以便在 Win32 平台上使用 comports 所需的函数,如下所示 在代码清单 8 中。
static uint16_t uart_write(com_t *p_com, void *pv, uint16_t length)
{
DWORD written;
context_t *p_context = (context_t*)p_com;
WriteFile(p_context->hComm, pv, length, &written, NULL);
return (uint16_t)written;
}
static uint16_t uart_read(com_t *p_com, void *pv, uint16_t length)
{
DWORD read;
context_t *p_context = (context_t*)p_com;
ReadFile(p_context->hComm, pv, length, &read, NULL);
return (uint16_t)read;
}
代码清单 9.flow.c 中的 Win32 串口回调。
COM 模块实现抽象串行传输的例程。com_init() 初始化抽象 调用对象和 com_read() 来反序列化和调度特定的命令/响应和指示。
嵌入式目标架构
嵌入式目标体系结构在概念上很简单,并且反映了主机端体系结构,不包括 平台和 MATLAB 特定组件。
嵌入式应用程序包含支持 svflow.c 中定义的主机端 svflow_* 调用的函数。这些 功能包括MAX35103EVKIT2特有的回调和配置,可在board.c中找到。
与主机端的 comport 抽象一样,嵌入式目标具有串行端口回调,如 Code 所示 清单 9.读回调调用芯片支持库 (CSL) 函数调用,以将长度字节写入 UART。 返回值是实际写入的字节数。读回调使用 CSL 函数调用来读取端口上当前可用的所有字节(最多长度)。
static uint16_t uart_write(com_t * p_com, void * pv, uint16_t length)
{
return UART_Write(MXC_UART0, (uint8_t *)pv, length);
}
static uint16_t uart_read(com_t * p_com, void * pv, uint16_t length)
{
return UART_Read(MXC_UART0, (uint8_t *)pv, length, NULL);
}
代码 10.main.c 中的串行端口回调。
嵌入式目标使用 COM 模块从主机调度命令。com_read() 从主节点调用 转换循环和命令在代码清单 10 中列出的 serialize_cb() 中调度。
main.c包含MAX35103EVKIT2评估软件上用于流量测量的嵌入式应用,使用: MAX3510x.c模块,用于与MAX35103芯片接口。Board.c 包含特定于板的初始化和 中断调度代码。
虽然本示例针对MAX35103EVKIT2评估板软件, COM 和序列化模块不是特定于平台的,可以轻松移植到大多数现代微控制器 和电路板设计。
static bool serialize_cb(void *pv_context, const void *pv_packet, uint16_t length)
{
const com_union_t *p_com = (const com_union_t*)pv_packet;
switch( p_com->hdr.id )
{
case COM_ID_HOST_START_SAMPLING:
{
if( p_com->start_sampling.sample_rate_hz > 0.0F &&
p_com->start_sampling.sample_rate_hz <= 200.0F )
{
s_sampling_underflow = 0;
s_sampling_overrun = 0;
s_sample_state = sample_state_idle;
s_send_samples = true;
SYS_SysTick_Config( (uint32_t)((float_t)SYS_SysTick_GetFreq() /
p_com>start_sampling.sample_rate_hz), 1);
}
break;
}
case COM_ID_HOST_STOP_SAMPLING:{
{
SYS_SysTick_Config( (uint32_t)((float_t)SYS_SysTick_GetFreq() / 10.0F), 1);
s_send_samples = false;
break;
}
}
return false;
}
代码 11.Serialize_cb在主
软件包内容
目标固件和Windows主机软件可从Maxim网站下载。它以拉链形式提供 档案。将存档解压缩到计算机上方便的目录中。图 6 显示了 目标固件和主机软件实现。®
图6.软件目录结构。
根目录包含 main() 以及 COM 和序列化模块。此外,传感器 c/h 包含 MAX35103EVKIT2评估软件附带的超声传感器参数
电路板目录包含 MATLAB 示例固件应用程序支持的每个开发板的子目录。 可以在此处添加对自定义用户板的支持。
csl和MAX3510x目录包含MAX35103EVKIT2评估板软件微控制器和外设的专用代码。
IAR目录包含用于构建和调试MAX35103EVKIT2评估板软件固件的项目文件。 可以添加新的项目配置以支持自定义用户板。最简单的方法是 复制配置,然后对其进行修改以适应新目标。
主机和 dll 目录包含生成 Win32 DLL 和 MEX 接口模块所需的所有源代码。 此外,还提供了用于编译 MEX 接口模块的 MATLAB 脚本以及 中详述的测试脚本 代码清单 1.
构建目标固件
MATLAB 示例固件可以使用 IAR ARM 构建。IAR 项目文件位于 iar 目录中。加载后 项目中,请务必检查调试器配置是否正确(请参阅图 4)。微控制器上的 MAX35103EVKIT2评估板软件 可以使用 IAR 支持的任何 ARM JTAG 适配器使用 10 引脚 ARM 接头 (J1) 进行编程。
图7.IAR 调试器选项。
Project.out 是在生成项目时创建的固件映像。
图8.使用 IAR 构建固件。
构建主机软件
MATLAB MEX 接口可以使用位于主机目录中的 compile.m 脚本从 MATLAB 内部构建。 如图 6 所示。构建的输出是 flow.mexw64。
要使用 compile.m,您必须安装 MATLAB 支持的 C 编译器。请访问 The Mathworks 网站了解详情 这可能会从一个版本的 MATLAB 更改为下一个版本。
在撰写本文时,MATLAB 2016a 可以使用 Microsoft Visual C++ 的免费版本来生成 MEX 文件。
图9.构建 MEX 接口。
主机软件也可以内置到 DLL 模块中,供知道如何与 DLL 通信的非 MATLAB 程序使用。用于生成 DLL 的 Microsoft Visual C++ 项目文件可以在 dll 目录中找到。
硬件配置
MAX35103EVKIT2 PCB必须连接到超声波流体,如MAX35103EVKIT2评估板数据资料中所述。图 10 显示了 上可用的连接 MAX35103EVKIT2电路板
电源应连接到6-24V AC或能够提供200mA的直流电源。
阀门可以保持未连接状态。
PIEZO UP±应连接到其中一个流体传感器
PIEZO DOWN±应与另一流动体换能器连接。
RTD/热敏电阻可以保持未连接状态。
固件不使用旋转开关。
图 10.MAX35103EVKIT2 J12引脚排列
结论
MATLAB 为数据分析和算法开发提供了一个很好的平台。本应用笔记介绍了一种简单、可定制的软件架构,可用于将数据导入 MATLAB,而无需成本和 市售附加模块的复杂性。
审核编辑:郭婷
全部0条评论
快来发表一下你的评论吧 !