• 【物联网智能网关-17】.NET Micro Framework之MDK C++二次开发


    .NET Micro Framework虽然好学易用,但是在一些需要实时,需要高性能的应用领域,却有些勉为其难。毕竟.NET Micro Framework上层应用程序由底层CLR(TinyCLR)解释执行,执行效率被打个折扣是在所难免的。

    美国GHI公司(国外.NET Micro Framework硬件产品主要生厂商)为此提供了一个称为RLP方案(https://www.ghielectronics.com/docs/50/rlp-enhanced)。可以让.NET Micro Framework的应用程序调用MDK编写的C++程序,主要是解决性能问题,把一些比较运行比较耗时的代码采用C++完成,功能相对简单。

    而我们所提供的方案和他们不同,我们是通过流式驱动的方式用MDK开发C++程序。用户程序采用标准的流式驱动接口进行相关调用。并且流式驱动提供事件机制,底层和上层可以通过事件进行交互。

    另外就是为MDK C++程序提供了丰富的.NET Micro Framework PAL层接口,可以让用户随心所欲地开发出功能强大的程序。

    在此之前我已经写过两篇相关的文章,用户可以先行了解一下:《.NET Micro Framework动态调用C/C++底层代码(原理篇)》和《【物联网智能网关-11】流式驱动之用户驱动(MDK C++开发)》。

    和上一篇文章介绍的功能函数相比,又扩展了一些比较实用的功能,比如I2C、SPI接口,底层中断打开关闭,HAL_COMPLETION、HAL_CONTINUATION类似底层多线程支持和中断程序用户态执行,功能函数由原来的61个扩展到了80个。具体功能接口如下:

    struct IGeneralStream_Function
    
    {
    
      INT32  iParam1;
    
      LPCSTR sParam1;
    
     
    
      //---
    
      void (*Notice_GenerateEvent)(UINT32 data1, UINT32 data2);
    
      void (*lcd_printf)(char const * format,...);
    
      void (*debug_printf)(char const* format, ... );
    
      void (*HAL_Time_Sleep_MicroSeconds_InterruptEnabled)(UINT32 uSec);
    
      UINT32 (*Events_WaitForEvents)(UINT32 WakeupSystemEvents, UINT32 Timeout_Milliseconds);
    
      void (*disable_interrupts)();
    
      void (*enable_interrupts)();
    
      void* (*private_malloc)(size_t len);
    
      void  (*private_free)( void*  ptr);
    
      //--- lock ---
    
      BOOL (*DISABLE_INTERRUPTS)(void* context);
    
      BOOL (*ENABLE_INTERRUPTS)(void* context);
    
     
    
      //HAL_COMPLETION/HAL_CONTINUATION
    
      UINT32 (*HAL_COMPLETION_Initialize)(HAL_CALLBACK_FPN EntryPoint,void* Argument);
    
      void (*HAL_COMPLETION_Uninitialize)(UINT32 handle);
    
      void (*HAL_COMPLETION_EnqueueDelta)(UINT32 handle,UINT32 uSec);
    
      UINT32 (*HAL_CONTINUATION_Initialize)(HAL_CALLBACK_FPN EntryPoint,void* Argument);
    
      void (*HAL_CONTINUATION_Uninitialize)(UINT32 handle);
    
      void (*HAL_CONTINUATION_Enqueue)(UINT32 handle);
    
     
    
      //--- mem ---
    
      int (*hal_snprintf)( char* buffer, size_t len, const char* format, ... );
    
      int (*hal_stricmp)( const char * dst, const char * src );
    
      int (*hal_strncmp_s)( const char* str1, const char* str2, size_t num );
    
      size_t (*hal_strlen_s)(const char * str);          
    
      void *(*memcpy)(void * dst, const void * src, size_t len);
    
      void *(*memset)( void * dst, int value, size_t len );  
    
     
    
      //--- Flash ---
    
      INT32 (*YFSoft_Flash_Erase)( UINT32 address, UINT32 count);
    
      INT32 (*YFSoft_Flash_Read)( UINT32 address, UINT32 count,UINT8 *buffer);
    
      INT32 (*YFSoft_Flash_Write)( UINT32 address, UINT32 count,UINT8 *buffer);
    
     
    
      //--- GPIO ---
    
      void (*CPU_GPIO_DisablePin)(GPIO_PIN Pin, GPIO_RESISTOR ResistorState, UINT32 Direction, GPIO_ALT_MODE AltFunction);
    
      BOOL (*CPU_GPIO_EnableInputPin)(GPIO_PIN Pin, BOOL GlitchFilterEnable, GPIO_INTERRUPT_SERVICE_ROUTINE ISR, GPIO_INT_EDGE IntEdge, GPIO_RESISTOR ResistorState);
    
      void (*CPU_GPIO_EnableOutputPin)(GPIO_PIN Pin, BOOL InitialState);
    
      BOOL (*CPU_GPIO_GetPinState)(GPIO_PIN Pin);
    
      void (*CPU_GPIO_SetPinState)(GPIO_PIN Pin, BOOL PinState);
    
     
    
      //--- TIMER ---
    
      BOOL (*CPU_TIMER_Initialize)(UINT32 timer, UINT32 ARR,UINT16 PSC,HAL_CALLBACK_FPN ISR, void* ISR_Param );
    
      BOOL (*CPU_TIMER_Uninitialize)(UINT32 timer );
    
      void (*CPU_TIMER_Start)(UINT32 timer);
    
      void (*CPU_TIMER_Stop)(UINT32 timer);  
    
      UINT32 (*CPU_TIMER_GetState)(UINT32 timer);
    
      void (*CPU_TIMER_SetState)(UINT32 timer,UINT32 state);
    
     
    
      //--- USART ---  
    
      BOOL (*USART_Initialize)( int ComPortNum, int BaudRate, int Parity, int DataBits, int StopBits, int FlowValue );
    
      BOOL (*USART_Uninitialize)( int ComPortNum );
    
      int  (*USART_Write)( int ComPortNum, const char* Data, size_t size );
    
      int  (*USART_Read)( int ComPortNum, char* Data, size_t size );
    
      BOOL (*USART_Flush)( int ComPortNum );
    
      int  (*USART_BytesInBuffer)( int ComPortNum, BOOL fRx );
    
      void (*USART_DiscardBuffer)( int ComPortNum, BOOL fRx );
    
     
    
      //--- AD/DA ---
    
      BOOL (*DA_Initialize)( ANALOG_CHANNEL channel, INT32 precisionInBits );
    
      void (*DA_Write)( ANALOG_CHANNEL channel, INT32 level );
    
      BOOL (*AD_Initialize)( ANALOG_CHANNEL channel, INT32 precisionInBits );
    
      INT32 (*AD_Read)( ANALOG_CHANNEL channel );
    
     
    
      //--- PWM ----
    
      BOOL (*PWM_Initialize)( PWM_CHANNEL channel );
    
      BOOL (*PWM_Uninitialize)( PWM_CHANNEL channel );
    
      BOOL (*PWM_ApplyConfiguration)( PWM_CHANNEL channel, GPIO_PIN pin, UINT32& period, UINT32& duration, PWM_SCALE_FACTOR &scale, BOOL invert );
    
      BOOL (*PWM_Start)( PWM_CHANNEL channel, GPIO_PIN pin );
    
      void (*PWM_Stop)( PWM_CHANNEL channel, GPIO_PIN pin );
    
      GPIO_PIN (*PWM_GetPinForChannel)( PWM_CHANNEL channel );
    
     
    
      //--- SPI ----
    
      BOOL (*CPU_SPI_nWrite16_nRead16)( const SPI_CONFIGURATION& Configuration, UINT16* Write16, INT32 WriteCount, UINT16* Read16, INT32 ReadCount, INT32 ReadStartOffset );
    
      BOOL (*CPU_SPI_nWrite8_nRead8)( const SPI_CONFIGURATION& Configuration, UINT8* Write8, INT32 WriteCount, UINT8* Read8, INT32 ReadCount, INT32 ReadStartOffset );
    
     
    
      //--- I2C ----
    
      BOOL (*I2C_Initialize)();
    
      BOOL (*I2C_Uninitialize)();
    
      BOOL (*I2C_Execute)(UINT16 address,UINT8 *inBuffer,int inCount,UINT8 *outBuffer,int outCount,UINT32 clockRateKhz,int timeout);
    
     
    
      //--- TinyGUI ----
    
      void (*LCD_ClearEx)(UINT32 color);   
    
      void (*LCD_SetPixel)(INT32 x,INT32 y,UINT32 color);
    
      UINT32 (*LCD_GetPixel)(INT32 x,INT32 y);
    
      void (*LCD_DrawLine)(INT32 x1,INT32 y1,INT32 x2,INT32 y2,UINT32 color);
    
      void (*LCD_DrawRectangle)(INT32 x,INT32 y,INT32 width,INT32 height,UINT32 color);
    
      void (*LCD_DrawEllipse)(INT32 x,INT32 y,INT32 width,INT32 height,UINT32 color);
    
      void (*LCD_DrawImage)(INT32 x,INT32 y,UINT8 *bytData);
    
      void (*LCD_DrawImageEx)(INT32 x,INT32 y,UINT8 *bytData,UINT32 MaskColor);
    
      void (*LCD_DrawString)(INT32 x,INT32 y,LPCSTR s,UINT32 color);
    
      void (*LCD_DrawStringEx)(INT32 x,INT32 y,UINT32 color,UINT8 *fontdata,int width,int height,int count); //2012-08-06
    
      void (*LCD_FillRectangle)(INT32 x,INT32 y,INT32 width,INT32 height,UINT32 color);
    
      void (*LCD_FillEllipse)(INT32 x,INT32 y,INT32 width,INT32 height,UINT32 color);
    
      void (*LCD_GetFrameBufferEx)(UINT8 *bytData,UINT32 offset,UINT32 size);
    
      void (*LCD_SuspendLayout)();
    
      void (*LCD_ResumeLayout)();  
    
    };

    下面简单介绍一下驱动开发步骤。

    1、  在MDK 4.xx版本创建一个新项目,添加generalstream.h头文件,然后再添加模板文件UserDriver.cpp。如下图所示:

     

    2、  选定MCU类型,可以根据实际硬件选择STM32F103/STM32F207/STM32F407/STM32F405。

     

    (凌霄智能终端采用的芯片就是STM32F405RG)

    3、  输入对应的平台宏定义

    4、  配置针对具体硬件所设置的离散加载配置文件

     

       凌霄智能终端的离散加载文件的内容如下:

      LR_IROM1 0x08010000 0x00010000  {    ; load region size_region

      ER_IROM1 0x08010000 0x00010000  {    ; load address = execution address

       .ANY (+RO)

      }

      RW_IRAM1 0x20000400 0x00002000  {  ; RW data

       .ANY (+RW +ZI)

      }

    }

        表示程序加载的位置在0x08010000,大小为64K,RAM空间为0x20000400起始的8K空间。

    注:用户驱动除了这部分RAM可用外,还可以直接通过接口提供的内存操作函数,分配堆上的内存。

    5、  编写用户驱动(C/C++),下面是一个综合示例,用到了GPIO操作、显示操作、时钟中断操作和事件通知。

    #include "GeneralStream.h"
    //--//
    #if defined(YF_Campsis103) || defined(YF_Campsis405)
    #define COM_PORT  COM1
    #else
    #define COM_PORT  COM3
    #endif
    
    volatile UINT32 Num;
    void TIMER_ISR(void* param)
    {
    #if defined(YF_Campsis103) || defined(YF_Campsis405)
       MF->CPU_GPIO_SetPinState(PC0,!MF->CPU_GPIO_GetPinState(PC0));
    #else
       MF->CPU_GPIO_SetPinState(PF6,!MF->CPU_GPIO_GetPinState(PF6));
    #endif
       MF->CPU_TIMER_SetState(TIM3,0);
    
       if(Num++>30)
       {
          Num=0;
          //触发事件
          MF->Notice_GenerateEvent(UserDriver_Hander,123);
       }
    }
    
    //--// 
    //Open1永远也不会被调用
    int GeneralStream_Open1_UserDriver(LPCSTR config) 
    { 
      return 0;
    }    
    
    int GeneralStream_Open2_UserDriver(int config)
    {
      //获取系统函数的指针
      MF = (IGeneralStream_Function*)config;
    
      //C#下传的参数
      //MF->lcd_printf("%d,%s
    ",MF->iParam1,MF->sParam1);
      MF->debug_printf("%d,%s
    ",MF->iParam1,MF->sParam1);
      
      //初始化LED灯
    #if defined(YF_Campsis103) || defined(YF_Campsis405)
      MF->CPU_GPIO_EnableOutputPin(PC0,TRUE); 
    #else
      MF->CPU_GPIO_EnableOutputPin(PF6,TRUE);
      MF->CPU_GPIO_EnableOutputPin(PF7,TRUE);
      MF->CPU_GPIO_EnableOutputPin(PF8,TRUE); 
    #endif        
    
      //时钟定时
      Num = 0;
      MF->CPU_TIMER_Initialize(TIM3,200,(SYSTEM_TIM_CLOCK_HZ/2000-1),TIMER_ISR,NULL);
      MF->CPU_TIMER_Start(TIM3);
    
      //初始化串口
      MF->USART_Initialize(COM_PORT,115200,USART_PARITY_NONE,8,USART_STOP_BITS_ONE,USART_FLOW_NONE);
    
      //显示界面
      MF->LCD_ClearEx(Color_Black);
      MF->LCD_DrawString(10,10,"UserDriver Test",Color_Blue);
    
      return 0;
    }
    
    int GeneralStream_Close_UserDriver()
    {
       return 0;
    }
    
    int GeneralStream_IOControl1_UserDriver(int code, BYTE *inBuffer, int inCount, BYTE *outBuffer, int outCount)
    {
       return -1;
    }
    
    int GeneralStream_IOControl2_UserDriver(int code,int parameter)
    {
        char data[3]={65,66,67};
        MF->USART_Write(COM_PORT, data, 3);
    
        MF->lcd_printf("[4]%d-%d
    ",code,parameter);
        MF->debug_printf("[4]%d-%d
    ",code,parameter);
        
        char str[32];
        MF->hal_snprintf(str,32,"%d-%d",code,parameter);
        MF->LCD_FillRectangle(100,80,100,20,Color_Black);    
        MF->LCD_DrawString(100,80,str,Color_Red);
    
    #if defined(YF_Wisteria207) || defined(YF_Wisteria407)
        MF->CPU_GPIO_SetPinState(PF7,! MF->CPU_GPIO_GetPinState(PF7));
        MF->CPU_GPIO_SetPinState(PF8,! MF->CPU_GPIO_GetPinState(PF8));
    #endif
        return code+parameter;
    }
    
    int GeneralStream_Read_UserDriver(BYTE *buffer, int offset, int count)
    {
       return -1;
    }
    
    int GeneralStream_Write_UserDriver(BYTE *buffer, int offset, int count)
    {
       return -1;
    }
    
    //--//
    //该函数无用,主要是为了编译成功而写  
    int main(void)
    {
      //
    }  
    
    //--//
    const IGeneralStream_Function *MF=NULL;
    const IGeneralStream g_GeneralStream_UserDriver __attribute__ ((at(IGeneralStream_Address)))  =
    {
        UserDriver_Flag,
        &GeneralStream_Open1_UserDriver,
        &GeneralStream_Open2_UserDriver,     
        &GeneralStream_Close_UserDriver,     
        &GeneralStream_IOControl1_UserDriver,  
        &GeneralStream_IOControl2_UserDriver,  
        &GeneralStream_Read_UserDriver,
        &GeneralStream_Write_UserDriver,    
    };

    6、  编译用户驱动,生成UserDriver.bin文件。

    7、  采用YFAccessFlash部署UserDriver.bin文件。

     

    选定UserDriver.bin文件然后直接部署即可。

    注:如果这不是初次部署运行用户驱动,需要先终止当前程序的执行,否则部署会出现问题(凌霄103的设备需要先部署应用,然后再部署用户驱动)。

    8、  用户程序编写(C#)

     

       public class Program
    
        {   
    
            public static void Main()
    
            {
    
                Debug.Print("UserDriver Test ...");
    
                GeneralStream gs = new GeneralStream();
    
                if (gs.Open("UserDriver") <= 0)
    
                {
    
                    Debug.Print("Open UserDriver failed!");
    
                    return;
    
                }
    
                gs.Notice += new GeneralStreamEventHandler(gs_Notice);
    
                Debug.Print("Open UserDriver OK!");
    
                int e = 0;
    
                byte[] bytData= new byte[8];
    
                while (true)
    
                {
    
                    Debug.Print(gs.IOControl(100, e++).ToString());
    
                    gs.Read(bytData, e, 10);
    
                    System.Threading.Thread.Sleep(1000);
    
                }
    
            }
    
            static void gs_Notice(uint hander, uint data, DateTime timestamp)
    
            {
    
                Debug.Print(hander.ToString() + " - " + data.ToString());
    
            }
    
        }
    

      

    9、  用户程序写好后,直接在VS 2010中编译执行。

     

    硬件运行效果图(如下):

     

     -------------------------------------------------------

    MF简介:http://blog.csdn.net/yefanqiu/article/details/5711770

    MF资料:http://www.yfiot.com/DownloadList.asp?Id=2&page=1

  • 相关阅读:
    转:详解iPhone Tableview分批显示数据 点击加载更多
    能不写全局变量就不写全局变量。
    ios 打电话 一键拨号
    下一步目标:整理出1套相对成熟的ios 开发框架
    dispatch_sync 线程 GCD iOS
    iOS 播放声音 最简单的方法
    判断 网络是否通常,以及判断用户使用的网络类型,时2G\3G\还是wifi
    ios 特效 新思路 :加载gif 动画,然后在动画上增加点击事件即可。
    Oracle小技巧
    excel导出时”内存或磁盘空间不足“错误的解决方法
  • 原文地址:https://www.cnblogs.com/yefanqiu/p/3523022.html
Copyright © 2020-2023  润新知