发新帖本帖赏金 3.00元(功能说明)我要提问
返回列表
打印
[STM32F4]

ST分享大集结+高速USB数据采集之固件、驱动、应用

[复制链接]
2416|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 1223657347 于 2017-11-25 12:06 编辑

        STMF4系列从F405起就自带了高速USB控制器了(当然要外挂PHY),再加上各种片上优秀的模拟/数字外设,很适合做些自定义功能的数据采集设备。本贴基于微控制器STM32F407VE研究了一种高速USB数据采集系统,主要可应用于半实物仿真、在线监测、音频处理等领域。欢迎各位前来拍砖

0 引言
  数据采集(DAQ, Data Acquisition)是信号采样的过程,数据采集将测量到的物理量或物理现象量化为计算机能够处理的数字量。数据采集系统(Data Acquisition System,DAS)是结合基于计算机的测量软硬件产品来实现灵活的、用户自定义的测量系统,数据采集系统主要由传感器,数据采集设备和计算机软件组成,如图1所示。传感器(Sensor)在数据采集系统中位于最前端,将温度、光强、气压、流量、压力等物理量或者物理现象转化电压、电流、电荷等电信号;数据采集设备先对传感器输出的电信号进行信号调理(线性化),再采样量化为数字量传输给计算机;计算机软件读取数据采集设备的采样结果,最后由应用层软件对数据进行处理。
  数据采集系统在各种工业领域都有应用,如数值分析、原型系统设计与验证、状态监控、自动化等。在数值分析(Numerical Analysis)领域,数据采集系统完成时域的抽样,其抽样结果交由数学软件进行计算,变换,分析;在快速控制原型(RCP, Rapid Control Prototyping)系统中,计算机上的控制器通过数据采集系统获取实际对象的实时状态,同时对目标对象执行控制,利用这种半实物仿真,可以方便快速地修改控制器、验证控制器功能,最终完成控制原型的设计;在集散控制系统(DCS, Distributed Control System)中,数据采集系统以现场仪表和I/O单元呈现,监控现场设备的运行状态,保证整个系统正常稳定运行。
  接下来本贴将从软硬件平台,固件、驱动、应用关键点,实际应用这三个点出发依次说明。







打赏榜单

21ic小管家 打赏了 3.00 元 2017-12-13

沙发
1223657347|  楼主 | 2017-11-23 16:46 | 只看该作者

1 软硬件平台

本帖最后由 1223657347 于 2017-11-25 13:07 编辑

1 软硬件平台
1.1 硬件组成
  由于我们重点不在DAQ硬件设计上,因此硬件组成这部分主要由模拟输入/输出端口,数字输入/输出端口,微控制器,USB,电源管理组成,如图2所示。

2 DAQ硬件组成
  • 模拟输入/输出端口:使用电压跟随器进行阻抗转换,增强驱动能力

  • 数字输入/输出端口:使用三态门缓冲器,增强驱动能力、保护微控制器I/O端口

  • 微控制器:主控型号为STM32F407VE,以下介绍摘官网,
STM32F407/417系列专为医疗,工业和消费类应用而设计,其中集成度和性能要求高,需要嵌入式存储器和丰富的外围封装,尺寸小至10 x 10 mm。
STM32F407/417具有运行在168 MHz的Cortex™-M4内核(带浮点单元)的性能。
性能:在168 MHz时,STM32F407/417提供从Flash存储器执行的210 DMIPS/566 CoreMark性能,采用ST的ART Accelerator进行0等待。DSP指令和浮点单元扩大了可寻址应用的范围。
功率效率:ST的90纳米工艺,ART***和动态功率缩放功能使运行模式下的电流消耗和从闪存执行的电流在168 MHz时低至238μA/MHz。
丰富的连接性:卓越和创新的外设:与STM32F4x5系列相比,STM32F407/417系列产品具有支持IEEE 1588 v2的以太网MAC10/100以及连接CMOS摄像头传感器的8至14位并行摄像头接口。
而我们所用到的重要片上外设有:ADCDACUSBGPIO等。
  • USB:由于ST全系列都不带高速USB PHY,因此需要外挂PHY,这里使用的Transceiver型号为USB3300,微控制器通过ULPI总线访问。
  • 电源管理:一路3.3V以及一路2.5V参考电压
  • 实物图


1.2 固件、驱动软件、应用软件开发平台
  • STM32F407的固件开发工具主要有STM32 CubeMX和MDK,CubeMX强大的初始化配置和中间件模板大大方便了固件工程管理。
  • 驱动软件指的是Windows操作系统下的设备信息驱动(inf),设备读写管理驱动。这部分都在Visual Studio中完成。
  • 应用软件平台使用的为MATLAB/SIMULINK,相比于Labview有更强大的数学计算、模型管理能力,在研究和技术人员中是主流的应用平台。

  这部分软件介绍就略过啦,各位感兴趣可以进官网进行深入了解
名称 网站
Keil MDK http://www2.keil.com/mdk5
MATLAB https://cn.mathworks.com/
STM32CubeMX http://www.st.com/en/development-tools/stm32cubemx.html
Visual Studio https://www.visualstudio.com/zh-hans/












使用特权

评论回复
板凳
1223657347|  楼主 | 2017-11-23 16:46 | 只看该作者
本帖最后由 1223657347 于 2017-11-25 13:46 编辑

2.1固件
  固件核心结构如下图

  固件设计难点和要点主要有以下几个方面:
  (1)USB枚举和通信。USB设备在接入主机时,主机通过总线枚举(Bus Enumeration)获取设备描述、设备配置。USB端点(Endpoint)是USB通信的管道,有输入事务(IN Transaction)和输出事务(OUT Transaction)。数据采集设备就通过IN端点向主机传输数据。
  (2)定时采样和存储。输入采样是数据采集设备的基本功能,定时采样的间隔即为采样率。采样结果需要缓冲暂存,再通过USB集中发送。
  (3)采样控制。数据采集设备的开始、停止采样需要可控,同时可设定的采样率也是必须的。
USB枚举和通信是固件最难的部分,但ST公司提供的STM32Cube USB库可以使这部分的设计变得非常容易。用户只需要对设备描述符(Device Descriptor)、配置描述符(Configuration Descriptor)和接口描述符(Interface Descriptor)修改,USB枚举过程完全由HAL库和STM32CubeUSB库完成。由于数据采集设备不是标准USB设备,因此设备描述符中设备类字段为自定义USB设备;接口描述符中向USB主机报告了所使用的端点及端点配置,本数据采集设备使用批量(Bulk)端点传输数据。
  模拟/数字输入定时采样和存储需要STM32的多个外设进行配合,包含了ADC,定时器、DMA(Direct Memory Access,直接内存存取)。定时器溢出事件触发ADC转换,ADC转换完成的结果由DMA进行搬运,该流程中不消耗CPU资源,减弱对CPU搬运数据的依赖。4K点的环形缓冲区使用了片上SRAM(实际占用64Kb),通过修改DMA目标地址(写指针),可以依次放置ADC序列采样结果到缓冲区去。在读指针和写指针的指示下,USB端点读取缓冲区数据不会发生阻塞和覆盖。
  数据采集设备的控制采用多级指令的方式,将端点2传输数据解码/编码后得到指令包,指令包分为命令包和响应包。命令包又细分为控制类、改写类、读取类,响应包细分为无数据响应和有数据响应。为了优化采样启动和停止速度,相应的命令包取消了响应机制。USB主机在启动采样之后,还需要对缓冲区进行预读,防止非法的指针访问和批量输入事务阻塞。  下面展示并说明部分核心固件代码:
static uint8_t DAQ_Itf_DataRxCallBack(uint8_t* pbuff, uint16_t length)
{
       
        USBD_DAQ_HandleTypedef *hdaq = (USBD_DAQ_HandleTypedef*)hdaq_temp->pClassData;
       
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_SET);
       
        /* Recall Functions Here */       
        if(daq_cfg.SampleIsEN == DAQ_SAMPLE_ON)
        {
                if(daq_cfg.Mode == DAQ_MODE_CONTI)
                {
                        //Out Pack Write Pointer Manage
                        hdaq_io.Out_wPointer += length / DAQ_DATA_OUT_PACK_SIZE;
                        if(hdaq_io.Out_wPointer >= DAQ_DATA_OUT_DEPTH)
                        {
                                hdaq_io.Out_wPointer = 0;
                        }
                }
                else if(daq_cfg.Mode == DAQ_MODE_BURST)
                {
                        hdaq_io.Out_wPointer = 0;
                       
                        if(*(hdaq_io.Out_Buff + hdaq_io.Out_wPointer * DAQ_DATA_OUT_PACK_SIZE) == DAQ_DATA_OUT_SOH)
                        {
                                if(daq_cfg.AO_IsEN == DAQ_ENABLE)
                                {
                                        //AO0 Updata                                       
                                        DAC->DHR12R1 = *(uint16_t*)(hdaq_io.Out_Buff + hdaq_io.Out_wPointer * DAQ_DATA_OUT_PACK_SIZE + 2);
                                }
                               
                                if(daq_cfg.DO_IsEN == DAQ_ENABLE)
                                {
                                        //DO Channel Updata
                                        DO0_GPIO_Port->ODR = (DO0_GPIO_Port->ODR & 0x00FF) |
                                                                                ((*(hdaq_io.Out_Buff + hdaq_io.Out_wPointer * DAQ_DATA_OUT_PACK_SIZE + 1)) << 8);
                                }
                                 
                        }
                       
                        DAQ_Itf_DataTxPack(hdaq_io.In_Buff, DAQ_DATA_IN_PACK_SIZE);
                }
        }
       
        USBD_DAQ_SetDataRxBuffer(hdaq_temp, hdaq_io.Out_Buff + hdaq_io.Out_wPointer * DAQ_DATA_OUT_PACK_SIZE);
        USBD_LL_PrepareReceive(hdaq_temp, DAQ_DATA_EPOUT_ADDR, hdaq->DataRxBuffer, DAQ_DATA_EPOUT_SIZE);
       
//        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_RESET);
       
        return USBD_OK;
}

static uint8_t DAQ_Itf_CtlRxCallBack(uint8_t* pbuff, uint16_t length)
{
       
        USBD_DAQ_HandleTypedef *hdaq = (USBD_DAQ_HandleTypedef*)hdaq_temp->pClassData;
        uint8_t ack_rs = DAQ_ACK_FAIL;
       
        /* Recall Functions Here */       
        if(*pbuff == length)
        {
                hdaq_ctl.Out_Buff = pbuff;
                DAQ_CTL_PackDeCode(&hdaq_ctl);
               
                if(hdaq_ctl.Out_Pack.bType == DAQ_CMD)
                {
                        switch(hdaq_ctl.Out_Pack.data[0])
                        {
                                case DAQ_CMD_CTL: //*(data) -> level 0 Command
                                        switch(hdaq_ctl.Out_Pack.data[1])
                                        {
                                                case DAQ_CFG: //NID ACK
                                                        switch(hdaq_ctl.Out_Pack.data[2])
                                                        {
                                                                case DAQ_AI:
                                                                        daq_cfg.AI_IsEN = hdaq_ctl.Out_Pack.data[3];
                                                                        break;
                                                                case DAQ_DI:
                                                                        daq_cfg.DI_IsEN = hdaq_ctl.Out_Pack.data[3];
                                                                        break;
                                                                case DAQ_AO:
                                                                        daq_cfg.AO_IsEN = hdaq_ctl.Out_Pack.data[3];
                                                                        break;
                                                                case DAQ_DO:
                                                                        daq_cfg.DO_IsEN = hdaq_ctl.Out_Pack.data[3];
                                                                        break;
                                                                default:
                                                                        goto ACK_NONE;
                                                        }
                                                       
                                                        /* DAQ Parts Manage Callback here */
                                                        DAQ_CMD_PartsEN(&daq_cfg);
                                                       
                                                        /* ACK */
                                                        ack_rs = DAQ_ACK_OK;
                                                        goto ACK_MNG;
                                                       
                                                case DAQ_MODE: //NID ACK
                                                        /*************DAQ Running Mode************
                                                         * DAQ_MODE_BURST
                                                         * DAQ_MODE_CONTI
                                                         **/
                                                        daq_cfg.Mode = hdaq_ctl.Out_Pack.data[2];
                                               
                                                        /* DAQ running mode Callback here */
                                                        if((daq_cfg.Mode == DAQ_MODE_BURST) || (daq_cfg.Mode == DAQ_MODE_CONTI))
                                                        {
                                                                ack_rs = DAQ_ACK_OK;
                                                        }
                                                        else
                                                        {
                                                                ack_rs = DAQ_ACK_FAIL;
                                                        }
                                                       
                                                        /* ACK */
                                                        goto ACK_MNG;
                                               
                                                case DAQ_SAMPLE: //NO ACK
                                                        daq_cfg.SampleIsEN = hdaq_ctl.Out_Pack.data[2];
                                                       
                                                        /* DAQ Sample Start or Stop Callback here */
                                                        ack_rs = DAQ_CMD_RunStop(&daq_cfg, &hdaq_io);
                                               
                                                        goto ACK_MNG;
                                               
                                                default:
                                                        goto ACK_NONE;                                       
                                        }       

                                case DAQ_CMD_ASIGN:
                                        switch(hdaq_ctl.Out_Pack.data[1])
                                        {
                                                case DAQ_AI_CH_CFG:
                                                        /***********AI Channel CFG Msg*************
                                                         * Channelx = hdaq_ctl.Out_Pack.data[2]
                                                         * State = hdaq_ctl.Out_Pack.data[3]
                                                         * Index = hdaq_ctl.Out_Pack.data[4]
                                                         **/
                                                        /* DAQ AI Channel Config Callback here */
                                                        ack_rs = DAQ_CMD_AI_CH_CFG_Callback(&hdaq_ctl, &daq_cfg);
                                               
                                                        /* ACK */
                                                        goto ACK_MNG;
                                               
                                                case DAQ_IO_SR:
                                                        /* DAQ Sample Rate Config Callback here */
                                                        //sr = (hdaq_ctl.Out_Pack.data[4] << 16) | (hdaq_ctl.Out_Pack.data[3] << 8) | (hdaq_ctl.Out_Pack.data[2]);
                                               
                                                        daq_cfg.SampleRate = (hdaq_ctl.Out_Pack.data[4] << 16) | (hdaq_ctl.Out_Pack.data[3] << 8) | (hdaq_ctl.Out_Pack.data[2]);
                                                       
                                                        /* DAQ SampleRate Setting Callback here */
                                                        ack_rs = DAQ_CMD_SampleRate(&daq_cfg);

                                                        /* ACK */
                                                        goto ACK_MNG;
                                               
                                                default:
                                                        goto ACK_NONE;
                                        }
                                       
                                case DAQ_CMD_FETCH: //NID Data
                                        switch(hdaq_ctl.Out_Pack.data[1])
                                        {
                                                case DAQ_CFG:
                                                        ack_rs = DAQ_CMD_Cfg(&hdaq_ctl, &daq_cfg);
                                                        break;
                                               
                                                case DAQ_MODE:
                                                        ack_rs = DAQ_CMD_Mode(&hdaq_ctl, &daq_cfg);
                                                        break;
                                               
                                                case DAQ_SAMPLE:
                                                        ack_rs = DAQ_CMD_Sample(&hdaq_ctl, &daq_cfg);
                                                        break;
                                               
                                                case DAQ_AI_CH_CFG:
                                                        ack_rs = DAQ_CMD_AI_CH_Cfg(&hdaq_ctl);
                                                        break;
                                               
                                                case DAQ_IO_SR:
                                                        ack_rs = DAQ_CMD_IO_SR(&hdaq_ctl, &daq_cfg);
                                                        break;
                                               
                                                case DAQ_IN_BUFF:
                                                        ack_rs = DAQ_CMD_IN_Buff(&hdaq_ctl, DAQ_EPIN_State(DAQ_DATA_EPIN_ADDR));
                                                        break;
                                               
                                                default:
                                                        goto ACK_NONE;
                                                       
                                        }
                                       
                                        if(ack_rs == DAQ_ACK_OK)
                                        {
                                               
                                                goto ACK_DATA;
                                        }
                                        else
                                        {
                                                goto ACK_MNG;
                                        }
                                       
                                default:
                                        goto ACK_NONE;

                        }
                }
        }
        else
        {
                goto ACK_NONE;
        }
       
ACK_MNG:
        DAQ_CTL_ACK_Result(&hdaq_ctl, ack_rs);
ACK_DATA:       
        DAQ_Itf_CtlTxPack(hdaq_ctl.In_Buff, hdaq_ctl.In_Pack.bLength);
ACK_NONE:       
        USBD_LL_PrepareReceive(hdaq_temp, DAQ_CTL_EPOUT_ADDR, hdaq->CtlRxBuffer, DAQ_CTL_EPOUT_SIZE);
       
        return USBD_OK;
}
uint8_t DAQ_Itf_DataTxPack(uint8_t* pbuff, uint16_t length)
{
        USBD_DAQ_HandleTypedef *hdaq = (USBD_DAQ_HandleTypedef*)hdaq_temp->pClassData;
       
        if((hdaq_temp->pClassData != NULL)||(pbuff != NULL))
        {
                if(hdaq->DataTxState == 0)
                {
                        USBD_DAQ_SetDataTxBuffer(hdaq_temp, pbuff, length);
                       
                        hdaq->DataTxState = 1;
                       
                        USBD_LL_Transmit(hdaq_temp, DAQ_DATA_EPIN_ADDR, hdaq->DataTxBuffer, hdaq->DataTxLength);
                       
                        return USBD_OK;
                }
                else
                {
                        return USBD_BUSY;
                }
        }
        else
        {
                return USBD_FAIL;
        }

}

uint8_t DAQ_Itf_CtlTxPack(uint8_t* pbuff, uint16_t length)
{
        USBD_DAQ_HandleTypedef *hdaq = (USBD_DAQ_HandleTypedef*)hdaq_temp->pClassData;
       
        if((hdaq_temp->pClassData != NULL)||(pbuff != NULL))
        {
                if(hdaq->CtlTxState == 0)
                {
                        USBD_DAQ_SetCtlTxBuffer(hdaq_temp, pbuff, length);
                       
                        hdaq->CtlTxState = 1;
                       
                        USBD_LL_Transmit(hdaq_temp, DAQ_CTL_EPIN_ADDR, hdaq->CtlTxBuffer, hdaq->CtlTxLength);
                       
                        return USBD_OK;
                }
                else
                {
                        return USBD_BUSY;
                }
        }
        else
        {
                return USBD_FAIL;
        }
}
  这4个函数对主机命令进行响应,控制4个端点的数据/控制指令

2.2 驱动
  驱动这部分实际上没有太多可以发挥的内容,必须按照巨硬的规范来,inf文件部分参考书可看《Windows驱动开发技术详解》—张帆。具体inf文件如下
; Ksp_DAQ_v20.inf
; Copyright (c) 2010-2016 Pete Batard <pete@akeo.ie> (GNU LGPL)
[Strings]
DeviceName        = "Ksp DAQ v2.0" ;设备名
VendorName        = "Ksp" ;制造商名
SourceName        = "Ksp DAQ v2.0Install Disk"
DeviceID        = "VID_0308&PID_0200" ;硬件ID
DeviceGUID        = "{44B53307-B39F-43D1-B7F0-69D647B12660}" ;设备接口GUID

[Version]
Signature   = "$Windows NT$"
Class       = "USBDevice"
ClassGuid   = {88bae032-5a81-49f0-bc3d-a4ff138216d6}
Provider    = %VendorName%
CatalogFile = Ksp_DAQ_v20.cat
DriverVer        = 04/10/2016, 15.27.20.931
[ClassInstall32]
Addreg = WinUSBDeviceClassReg

[WinUSBDeviceClassReg]
HKR,,,0,"Universal Serial Bus devices"
HKR,,Icon,,-20

[Manufacturer]
%VendorName% = libusbDevice_WinUSB,NTx86,NTamd64,NTarm

[libusbDevice_WinUSB.NTx86]
%DeviceName% = USB_Install, USB\%DeviceID%

[libusbDevice_WinUSB.NTamd64]
%DeviceName% = USB_Install, USB\%DeviceID%

[libusbDevice_WinUSB.NTarm]
%DeviceName% = USB_Install, USB\%DeviceID%

[USB_Install]
Include = winusb.inf
Needs   = WINUSB.NT

[USB_Install.Services]
Include    = winusb.inf
AddService = WinUSB,0x00000002,WinUSB_ServiceInstall

[WinUSB_ServiceInstall]
DisplayName   = "WinUSB - Kernel Driver 03/31/2015 6.1.7600.16385"
ServiceType   = 1
StartType     = 3
ErrorControl  = 1
ServiceBinary = %12%\WinUSB.sys

[USB_Install.Wdf]
KmdfService = WINUSB, WinUsb_Install

[WinUSB_Install]
KmdfLibraryVersion = 1.11

[USB_Install.HW]
AddReg = AddDeviceInterfaceGUID

[NoDeviceInterfaceGUID]
; Avoids adding a DeviceInterfaceGUID for generic driver

[AddDeviceInterfaceGUID]
HKR,,DeviceInterfaceGUIDs,0x10000,%DeviceGUID%

[USB_Install.CoInstallers]
AddReg    = CoInstallers_AddReg
CopyFiles = CoInstallers_CopyFiles

[CoInstallers_AddReg]
HKR,,CoInstallers32,0x00010000,"WdfCoInstaller01011.dll,WdfCoInstaller","WinUSBCoInstaller2.dll"

[CoInstallers_CopyFiles]
WinUSBCoInstaller2.dll
WdfCoInstaller01011.dll

[DestinationDirs]
CoInstallers_CopyFiles = 11

[SourceDisksNames]
1 = %SourceName%

[SourceDisksFiles.x86]
WinUSBCoInstaller2.dll = 1,x86
WdfCoInstaller01011.dll = 1,x86

[SourceDisksFiles.amd64]
WinUSBCoInstaller2.dll = 1,amd64
WdfCoInstaller01011.dll = 1,amd64

[SourceDisksFiles.arm]
WinUSBCoInstaller2.dll = 1,arm
WdfCoInstaller01011.dll = 1,arm
  对于自定义USB设备驱动,目前常见的教程都是在使用LibUSB(1.0版本)等,我们这里使用的是Windows自带的WinUSB,开发者不需要关心内核,详细可阅读WinUSB官网(Website:https://msdn.microsoft.com/en-us/library/windows/hardware/ff540196%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396)。这里给出DAQ的模/数数据读写代码,
KSP_WinUSB_StatusTypeDef KSP_DAQ_BURST_WR(uint8_t* w_buff, uint8_t* r_buff)
{
        uint32_t rx_len;

        if (KSP_WinUSB_USBD_BulkWrite(DAQ_DATA_EPOUT_ADDR, w_buff, DAQ_DATA_OUT_PACK_SIZE) != USBD_OK)
        {
                return USBD_FAIL;
        }

        if (KSP_WinUSB_USBD_BulkRead(DAQ_DATA_EPIN_ADDR, r_buff, DAQ_DATA_IN_PACK_SIZE, &rx_len) != USBD_OK)
        {
                return USBD_FAIL;
        }

        return USBD_OK;

}

KSP_WinUSB_StatusTypeDef KSP_DAQ_CONTI_W(uint8_t* w_buff, uint32_t w_buff_len)
{

        return KSP_WinUSB_USBD_BulkWrite(DAQ_DATA_EPOUT_ADDR, w_buff, w_buff_len);
}

KSP_WinUSB_StatusTypeDef KSP_DAQ_CONTI_R(uint8_t* r_buff, uint32_t* r_len)
{

        return KSP_WinUSB_USBD_BulkRead(DAQ_DATA_EPIN_ADDR, r_buff, DAQ_DATA_EPIN_SIZE, r_len);
}
  而其中核心的块读写代码如下
/**
* @brief        通过指定端点写入数据
* @param        ep_addr:端点号
                        pbuff:待发送的数据缓冲
                        txsize:数据缓冲长度
* @retval        操作结果
                        @arg @KSP_WinUSB_StatusTypeDef
*/
KSP_WinUSB_StatusTypeDef KSP_WinUSB_USBD_BulkWrite(uint8_t ep_addr, uint8_t* pbuff, uint32_t txsize)
{
        KSP_WinUSB_StatusTypeDef status = USBD_OK;
        uint32_t bytessend;

        if (KSP_WinUSB_USBD_IsOpen() != 1)
        {
                status = USBD_FAIL;
                goto DONE;
        }

        if (WinUsb_WritePipe(h_ksp_winusb.WinusbHandle, ep_addr, pbuff, txsize, (PULONG)&bytessend, 0) == FALSE)
        {
                status = USBD_FAIL;
        }

DONE:
        return status;

}

/**·
* @brief        通过指定端点读出数据
* @param        ep_addr:端点号
                        pbuff:读出数据的缓冲区
                        rxsize:接收到的数据长度(按字节计)
* @retval        操作结果
                        @arg @KSP_WinUSB_StatusTypeDef
*/
KSP_WinUSB_StatusTypeDef KSP_WinUSB_USBD_BulkRead(uint8_t ep_addr, uint8_t* pbuff, uint32_t buflen, uint32_t* rxsize)
{
        KSP_WinUSB_StatusTypeDef status = USBD_OK;

        if (KSP_WinUSB_USBD_IsOpen() != 1)
        {
                status = USBD_FAIL;
                goto DONE;
        }

        if (WinUsb_ReadPipe(h_ksp_winusb.WinusbHandle, ep_addr, pbuff, buflen, (PULONG)rxsize, 0) == FALSE)
        {
                status = USBD_FAIL;
        }

DONE:
        return status;

}
  在Visual Studio平台中,inf的编写编译,DAQ设备的读写测试,封装成动态链接库(dll)一气呵成。对外提供的函数接口如下
LIBRARY Ksp_daq_v21_dll
EXPORTS

KSP_DAQ_Open                                        [url=home.php?mod=space&uid=72445]@[/url] 1
KSP_DAQ_Close                                        @ 2

KSP_DAQ_CTL_PartsEN                                @ 3
KSP_DAQ_CTL_Mode                                @ 4
KSP_DAQ_CTL_SampleEN                        @ 5

KSP_DAQ_ASIGN_AI_CH_Config                @ 6
KSP_DAQ_ASIGN_SampleRate                @ 7

KSP_DAQ_FETCH_PartsEN                        @ 8
KSP_DAQ_FETCH_Mode                                @ 9
KSP_DAQ_FETCH_SampleEN                        @ 10
KSP_DAQ_FETCH_AI_CH_Config                @ 11
KSP_DAQ_FETCH_SampleRate                @ 12
KSP_DAQ_FETCH_BUFF_State                @ 13

KSP_DAQ_BURST_WR                                @ 14

KSP_DAQ_CONTI_W                                        @ 15
KSP_DAQ_CONTI_R                                        @ 16


2.3 应用软件
  对于Windows下设备访问只要把接口写好,其他都能由系统与环境自动完成。MATLAB中如何使用前一节Release的dll文件呢,请看如下测试DAQ设备的m文件
%%测试DLL文件
[~,~]=loadlibrary('Ksp_daq_v21_dll','ksp_daq_vxx.h');

SampleDepth = 400;
SampleRate = 900000;

if strcmp(calllib('Ksp_daq_v21_dll','KSP_DAQ_Open'),'USBD_OK') == 1
    disp('设备打开成功')
   
    %设置采样率为100KHz
    calllib('Ksp_daq_v21_dll','KSP_DAQ_ASIGN_SampleRate',SampleRate);
    disp(strcat('当前采样率为',int2str(SampleRate),'KHz'));
   
    %设定采样模式
    calllib('Ksp_daq_v21_dll', 'KSP_DAQ_CTL_Mode', 2);
   
    %预读缓冲区
    IN_BUFF_State = zeros(1, 1, 'uint8');
    rx_len = zeros(1, 1, 'uint32');
    rx_buff = zeros(1, (16*SampleDepth + 512), 'uint8');
   
    tx_buff = uint8(hex2dec({'bb','01','99','09'}))';
   
    calllib('Ksp_daq_v21_dll','KSP_DAQ_FETCH_BUFF_State', IN_BUFF_State);
    if (IN_BUFF_State == 2)
        calllib('Ksp_daq_v21_dll','KSP_DAQ_CONTI_R',rx_buff, rx_len);
    end

    %通道配置
    calllib('Ksp_daq_v21_dll','KSP_DAQ_ASIGN_AI_CH_Config', 0, 2, 8);
    calllib('Ksp_daq_v21_dll','KSP_DAQ_ASIGN_AI_CH_Config', 1, 2, 8);
    calllib('Ksp_daq_v21_dll','KSP_DAQ_ASIGN_AI_CH_Config', 2, 2, 8);
    calllib('Ksp_daq_v21_dll','KSP_DAQ_ASIGN_AI_CH_Config', 3, 2, 8);
    calllib('Ksp_daq_v21_dll','KSP_DAQ_ASIGN_AI_CH_Config', 4, 2, 8);
    calllib('Ksp_daq_v21_dll','KSP_DAQ_ASIGN_AI_CH_Config', 5, 2, 8);
    calllib('Ksp_daq_v21_dll','KSP_DAQ_ASIGN_AI_CH_Config', 6, 2, 8);
   
    calllib('Ksp_daq_v21_dll','KSP_DAQ_ASIGN_AI_CH_Config', 0, 1, 1);
    %calllib('Ksp_daq_v21_dll','KSP_DAQ_ASIGN_AI_CH_Config', 1, 1, 2);
   
    total_len = 0;
   
    %启动采样
    calllib('Ksp_daq_v21_dll','KSP_DAQ_CTL_SampleEN', 1);
   
    calllib('Ksp_daq_v21_dll', 'KSP_DAQ_CONTI_W', tx_buff, 4);
   
    while (total_len<(16*SampleDepth))
     [rs, rx_buff((total_len+1):( total_len+512)), rx_len]=calllib('Ksp_daq_v21_dll','KSP_DAQ_CONTI_R', rx_buff((total_len+1):( total_len+512)), rx_len);
     total_len = total_len + rx_len;
    end
     
    %停止采样
    calllib('Ksp_daq_v21_dll','KSP_DAQ_CTL_SampleEN', 2);
   
    if strcmp(calllib('Ksp_daq_v21_dll','KSP_DAQ_Close'),'USBD_OK') == 1
         disp('设备关闭成功')
    else
         disp('设备关闭失败')
    end
   
    AI0 = [];
    for i=1:1:SampleDepth
        adc_value = (double(rx_buff((i -1 )*16 + 3)) + 256*double(rx_buff((i -1 )*16 + 4)))*2.5/4095;
        AI0(i) = adc_value;
    end
   
    Ts = 1 / SampleRate;
   
    plot([0:Ts:(Ts*(SampleDepth-1))], AI0);
    xlabel('t/s')
    ylabel('AI0/v')
else
     disp('设备打开失败')
end

unloadlibrary('Ksp_daq_v21_dll')
  运行成功就能看到模拟通道0输入的电压波形图啦,下图是MATLAB下GUIDE一个简单的测试结果,硬件上DO0接到了DI0。




使用特权

评论回复
地板
1223657347|  楼主 | 2017-11-23 16:47 | 只看该作者

实际应用

本帖最后由 1223657347 于 2017-11-25 14:21 编辑

3 实际应用   数据采集设备以及相关服务软硬件很多厂商在做,但一般实验室对DAQ的性能要求不高,同时一些定制性需求商用卡也不能满足,因此自制数据采集系统还在不断发展。以下举出一些数据采集卡的应用实例
  • 快速控制原型(RPC)半实物仿真
  如图中所示的MATLAB/SIUMLINK倒立摆RCP模型,其中的编码器、PWM发生器由自制数据采集卡完成,而SIMULINK中可以方便对控制方法进行实现、修改。

  • 硬件在环(HIL)半实物仿真
  比如在车辆半主动悬架控制研究中,SIMULINK中建立了车辆悬架模型,而半主动悬架中的磁流变阻尼器(MRD)由振动测试台+实物的方式接入该车辆悬架模型,同时MRD的控制也在SIUMLINK中完成,这样就组成了一个半主动悬架控制试验HIL仿真平台。
  • 音频处理
  音频相关应用很多,而MATLAB+数据采集这一平台可以在线式地实时对音频数据进行滤波、变声、加密等操作。下图是我们在MATLAB下做的音频FFT小实验,由于模拟前端没有设计完成,这里的波形只是个浮空输入展示。


使用特权

评论回复
5
1223657347|  楼主 | 2017-11-25 14:25 | 只看该作者
总结
  相对于成熟的商用产品,我们的硬件和应用方案都很粗糙,但其中的固件、驱动、应用软件有很高的可玩性,这也是微控制器玩家的乐趣所在吧。如果有人感兴趣我之后会上传硬件原理图/PCB、微控制器固件工程、驱动工程、MATLAB工程等工程文件。

使用特权

评论回复
6
chenqiang10| | 2017-11-25 19:12 | 只看该作者
不错的资料,感谢分享。

使用特权

评论回复
7
秦时明月94| | 2018-3-5 16:03 | 只看该作者
可以,楼主对USB了解很透彻

使用特权

评论回复
8
小明的同学| | 2018-3-5 21:04 | 只看该作者
多个串口同时用最难

使用特权

评论回复
9
卡哈巴| | 2019-8-21 00:49 | 只看该作者
快速原型半实物仿真中,编码器信号需要实时传递到simulink(500hz),想请教一下楼主怎样做到。

使用特权

评论回复
10
1021256354| | 2019-8-22 14:27 | 只看该作者
2年也传上来,支持HUB么

使用特权

评论回复
发新帖 本帖赏金 3.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

8

主题

168

帖子

3

粉丝