EM9380是面向实时控制应用的一款工控主板,这里所说的实时控制是指那些控制周期可能短至几十微妙的控制应用。EM9380通过板上独立运行的硬件协处理器(Cortex-M3),可很好的满足这类应用的需求。多路任意脉冲发生器,就是按照用户预设的流程,按设定的时间间隔(最短25us),同时更新多路(最多8路)数字输出的电平状态,从而构成多路任意周期的脉冲信号输出。多路任意脉冲发生器功能,可用于步进电机的控制,在纺织、印刷设备中有广泛的应用。本文后续部分将主要介绍在应用程序中实现任意脉冲发生器功能的流程。
接口硬件说明
EM9380中的GPIO0–GPIO15是由其硬件协处理器直接控制的,它们均可作为任意脉冲发生器的输出,输出的总数不超过8路。对选定作为意脉冲输出的GPIO,首先需要设置成输出模式。因为在EM9380上电或复位启动后,GPIO0 – GPIO15均处于输出状态的,由于内部100KΩ上拉,其电平均为高电平(管脚悬空时)。在实际应用中,若需要脉冲输出的初始电平为低电平,可在其管脚接10KΩ的下拉电阻至地。
接口软件说明
为了操作GPIO0–GPIO15这组GPIO,首先需要打开硬件写处理器的驱动程序,其设备文件名为”MCU2:”(注意是MCU2,而不是MCU1):
#include // 数据类型定义
HANDLE hMCU2;
hMCU2 = CreateFile(_T(“MCU2:”), // name of device
GENERIC_READ|GENERIC_WRITE, // desired access
FILE_SHARE_READ|FILE_SHARE_WRITE, // sharing mode
NULL, // security attributes (ignored)
OPEN_EXISTING, // creation disposition
FILE_FLAG_RANDOM_ACCESS, // flags/attributes
NULL); // template file (ignored)
驱动程序MCU2通过DeviceIoControl,支持以下GPIO命令:
MCU_GENERIC_GPIO_OE // 输出使能
MCU_GENERIC_GPIO_OD // 输出禁止,作为数字输入
MCU_GENERIC_GPIO_SET // 输出高电平
MCU_GENERIC_GPIO_CLR // 输出低电平
MCU_GENERIC_GPIO_PIN // 读取输入管脚电平状态
MCU_GENERIC_GPIO_OF // 实时控制输出流
其中前5个命令通过以下的数据结构来设置相关参数,从而实现GPIO的命令:
typedef struct
{
BYTE ucSize; // 本数据结构大小 = 17字节
BYTE ucCmd; // GPIO命令码:MCU_GENERIC_GPIO_XXX
DWORD dwPins; // 操作的管脚位
DWORD dwStatus; // 返回的管脚电平状态
DWORD dwPeriod; // 实时控制周期,单位us;= 0: 常规功能
bool bFlashed; // 保存本配置作为启动缺省功能
BYTE ucChkSum; // 校验和
} MCU_GPIO_INFO, *PMCU_GPIO_INFO; // struct for GPIO
对于本应用来说,首先需要把所选择的GPIO设置为输出模式,具体代码如下:
MCU_GPIO_INFO Info; //定义结构对象
memset(&Info, 0, sizeof(MCU_GPIO_INFO));//清零
//填写所需的参数
Info.ucSize = sizeof(MCU_GPIO_INFO);
Info.ucCmd = MCU_GENERIC_GPIO_OE; //设置为输出模式
Info.dwPins = GPIO3 | GPIO2 | GPIO1 | GPIO0; //设置4路脉冲输出
// 生成chksum字节
Info.ucChkSum = ChkSum(&Info, sizeof(MCU_GPIO_INFO)-1);
// 调用DeviceIoControl
if (!DeviceIoControl(hMCU2, // File handle to the driver
MCU_IOCTL_ACCESS, // I/O control code
&Info, // input buffer
sizeof(MCU_GPIO_INFO), // in buffer size
NULL, // out buffer
0, // out buffer size
NULL, // pointer to number of bytes returned
NULL)) // ignored (=NULL)
{
// 出错处理......
return FALSE;
}
其次是设置脉冲的初始电平及脉冲的更新周期,其主要代码如下:
memset(&Info, 0, sizeof(MCU_GPIO_INFO));//清零
//填写所需的参数
Info.ucSize = sizeof(MCU_GPIO_INFO);
Info.ucCmd = MCU_GENERIC_GPIO_SET; //输出的初始电平为高电平
//Info.ucCmd = MCU_GENERIC_GPIO_CLR; //输出的初始电平为低电平
Info.dwPins = GPIO3 | GPIO2 | GPIO1 | GPIO0; //设置4路脉冲输出
Info.dwPeriod = 25; //设置更新周期为25us
接着的生成ChkSum字节和调用DeviceIoControl操作和上一步是一样的,不再赘述。
设置了更新周期后,硬件协处理器已处于脉冲输出状态,只等驱动程序MCU2下传相应的数据流了。下传的数据流将按以下数据结构分包逐一下传:
typedef struct
{
BYTE ucSize; //本数据结构大小 = 64字节
BYTE ucCmd; // = MCU_GENERIC_GPIO_OF
BYTE ucRawDat[60]; // 更新数据字节
BYTE ucChkSum; //
} MCU_GPIO_FLOW, *PMCU_GPIO_FLOW; // struct for GPIO
每一个更新数据字节对应着一次更新,所以一包数据可供60个更新周期使用。数据字节的格式是按GPIO的编号右对齐的,即设置的GPIO中,编号最小的那一路,其更新的bit为更新数据字节的LSB(D0),其他数据按编号递增,向左排列至MSB(D7)。
下传数据流,同样需要做ChkSum,再调用DeviceIoControl:
if (!DeviceIoControl(hMCU2, //File handle to the driver
MCU_IOCTL_ACCESS, // I/O control code
&Flow, // input buffer
sizeof(MCU_GPIO_FLOW), // in buffer size
NULL, // out buffer
0, // out buffer size
NULL, // pointer to number of bytes returned
NULL)) // ignored (=NULL)
{
// 出错处理......
return FALSE;
}
数据更新流程说明
对任意脉冲发生器功能,EM9380硬件协处理器内部设置有一对256字节的乒乓Buffer。这样驱动程序MCU2在下传数据包时,每次可发送4个数据包(第一次下传1个包即可),这样可进一步提高主CPU的工作效率。对25us更新周期来说,就表示驱动程序下传数据的间隔为6ms。一旦第一包数据下传至硬件协处理器,实质性启动任意脉冲波形输出后,当有空闲的乒乓Buffer时,硬件协处理器会立即通知驱动程序MCU2。应用程序可设置专门线程获取数据请求事件,从而启动数据下传。其基本的流程如下:
DWORD dwReturn = 0; // 返回状态,= 1:数据请求,= 0:超时
DWORD dwTimeout = 12; // 12ms,2倍正常下传间隔
// 初始下传一个数据包,然后进入主循环。
// ......
// 主循环:等待下传数据请求
for(; ;)
{
if (!DeviceIoControl(hMCU2, // File handle to the driver
MCU_IOCTL_WAIT_FOR_DATA, // I/O control code
&dwTimeout, // input buffer
sizeof(DWORD), // in buffer size
NULL, // out buffer
0, // out buffer size
&dwReturn, // pointer to number of bytes returned
NULL)) // ignored (=NULL)
{
// 出错处理......
}
if(dwReturn)
{
// 下传数据包(4包)......
dwReturn = 0; // 清标志
}
}
全部0条评论
快来发表一下你的评论吧 !