【物联网智能网关-11】流式驱动之用户驱动(MDK C++开发)

简介: PC平台的,大都是用组态系统搭建,嵌入式系统则是采用嵌入式组态软件,其定制化的软件则采用WinCE等易用的嵌入式系统来开发了,但是对再小型的嵌入式系统,由于选择目前比较少,也只有选用传统的C/C++来开发了。

微软体系的产品给人的感觉一直是易学易用,但是其执行性能却屡受诟病。所以一些对性能要求相对较高的硬件产品研发,一般都是采用linux体系的技术,或者是无操作系统开发,其开发语言也绝大数是C/C++(启动代码或中断部分的代码有时会用汇编代码实现)。但是对工控集成类的项目开发来说,由于项目开发周期比较短,对稳定性要求比较高,如果全部采用C/C++开发,不仅对开发人员的能力要求比较高,并且开发和调试的代价非常大。所以PC平台的,大都是用组态系统搭建,嵌入式系统则是采用嵌入式组态软件,其定制化的软件则采用WinCE等易用的嵌入式系统来开发了,但是对再小型的嵌入式系统,由于选择目前比较少,也只有选用传统的C/C++来开发了。

2001年.NET MicroFramework的开始研发,其实就是基于比尔盖茨所谓的.NET战略,上至服务器大型系统,下至嵌入式领域的芯片都希望是.NET系统,都可以用C#等.NET开发语言进行开发。所以最开始.NET MicroFramework系统就是开发硬件产品的,MSNDirect产品、SideShow,还有一些高端遥控器,键盘,都是采用.NET Micro Framework系统开发(相关介绍,请参见《MSN Direct项目简介》),虽然用.NET MicroFramework系统开发比较容易,但是要达到同样的性能,必须要求系统的主频更快,RAM更大,这对批量生产的硬件产品来说,长远发展来看,不是一个好选择。

我个人认为微软官方的.NET Micro Framework产品类开发的定位是错误的,这一点我可以看到微软在Windows领域的开发也是放弃了全用.NET托管代码实现的诉求,大部分底层或对性能要求很高的代码,依然采用原生C/C++实现(目前iOS和安卓系统的开发,基于性能的考虑,很多开发人员都开始用原生C/C++进行开发)。

用.NET Micro Framework开发用户需求变化少,不需要二次开发接口的产品来说,是非常不适合的,特别是销售量数量非常大的产品,因为随着产品的销量不断增加,前期开发成本所占的成本比重将越来越小。但是对用户需求变化大,用户需要有二次开发,或者是销量比较少的产品来说,用.NET MicroFramework优势就比较明显了。特别是工控集成类的产品,.NET Micro Framework系统有天然的优势(这是我7年工控领域的工作经历深切感受到的)。

PC领域的组态化技术已经非常成熟了,目前已经在向组态软件的第二代或第三代进行发展。但是在嵌入式领域,特别是低端MCU方面,这方面做得远远不够,我工作的定位就是致力于嵌入式领域组态化,并且我认为.NET Micro Framework系统是实现这个愿景的最好的一种技术支撑。

.NET Micro Framework的平台的C#(或VB.NET)开发虽然开发比较简单,但是其执行性能却是一个必须面对的问题(2009年我在微软总部和MSNDirect开发人员交流的时候,他们对.NETMicro Framework的执行性能颇有微词)。

我的解决方案就是:.NET Micro Framework必须尽可能的封装,C#语言执行的不是大段功能代码,而只是一些工艺流程代码即可,那些功能性的代码尽可能用C/C++实现。C#起到粘连串接的作用即可,这一点和网页开发中的脚本语言的角色非常类似。

目前这类封装,必须是porting开发人员完成,是.NETMicro Framework TinyCLR的不可分的部分,普通用户是不能进行C/C++开发的。而我这篇文章所介绍的重点,就是为普通的开发用户,开启C/C++ 基于.NET MicroFramework编程之门。  在《.NETMicro Framework动态调用C/C++底层代码》文章中我介绍了这种技术的实现原理,本篇文章就是基于应用的角度,介绍如果用MDK进行.NET MicroFramework用户驱动开发。

在进行用户流式驱动开发介绍之前,我先比较一下C#和C++开发的性能,让大家有一个直观的感受。
image.png

上图C#代码如下:

    OutputPort io = newOutputPort((Cpu.Pin)GPIO_NAMES.PA6,false);

    while (true)

    {

       io.Write(true);

       io.Write(false);

    }

C++的代码如下(NativeSample下运行)

CPU_GPIO_EnableOutputPin(STM32F20x_GPIO_Driver::PA6,FALSE);

while(TRUE) 

{

    CPU_GPIO_SetPinState(STM32F20x_GPIO_Driver::PA6,FALSE); 

          CPU_GPIO_SetPinState(STM32F20x_GPIO_Driver::PA6,TRUE); 

}

硬件平台采用紫藤207(STM32F207 主频120M),通过示波器检查PA6管脚。

从示波器的显示结果来看,二者相差近60倍,所以说在C#层很难实现微秒级别的控制。

另外我也比较了一下C#和C++的for循环的执行效率。

代码很简单,就是:

for(x=0;x<1000;x++);

1.png
C#层提供的Sleep延时也是毫秒级别的,我做了一个简单的测试,结果如下:
2.png
注:由于底层时钟中断不断触发,Sleep的时间是不确定的。

相信以上的测试结果,对大家的印象是深刻的。所以说,不考虑C#的封装优点,而是非要用C#和C++实现同样的功能,只能是让大家越来越远离.NET Micro Framework。

-------- 分割线 ---------

在《.NET Micro Framework动态调用C/C++底层代码》这篇文章中,我介绍g_GeneralStream_Function的时候,其支持的函数才15个,并且主要是GPIO和时钟类的函数,这次调整以后,已经扩展支持61个了,并且也可以传递初始化函数的字符串或整型变量参数了,新的g_GeneralStream_Function定义如下:

IGeneralStream_Functiong_GeneralStream_Function  =

{       

      -1,

           NULL,

           //--

      &Notice_GenerateEvent,

           &lcd_printf,

          &debug_printf,  

          &HAL_Time_Sleep_MicroSeconds_InterruptEnabled,

          &Events_WaitForEvents,

          &disable_interrupts,

          &enable_interrupts,

           &private_malloc,

           &private_free,

      //mem

      &hal_snprintf,

      &hal_stricmp,

      &hal_strncmp_s,

      &hal_strlen_s,

      &memcpy,

      &memset,    

           //Flash

          &YFSoft_Flash_Erase,

          &YFSoft_Flash_Read,

          &YFSoft_Flash_Write,

           //GPIO

          &CPU_GPIO_DisablePin, 

          &CPU_GPIO_EnableInputPin, 

          &CPU_GPIO_EnableOutputPin,

          &CPU_GPIO_GetPinState, 

          &CPU_GPIO_SetPinState,

           //TIMER

          &CPU_TIMER_Initialize, 

          &CPU_TIMER_Uninitialize,

          &CPU_TIMER_Start,

           &CPU_TIMER_Stop,

          &CPU_TIMER_GetState,

          &CPU_TIMER_SetState,

           //USART

      &USART_Initialize,

          &USART_Uninitialize,

           &USART_Write,

           &USART_Read,

           &USART_Flush,

          &USART_BytesInBuffer,

          &USART_DiscardBuffer,

           //DA/AD

           &DA_Initialize,

           &DA_Write,

           &AD_Initialize,

           &AD_Read,

           //PWM

           &PWM_Initialize,

          &PWM_Uninitialize,

          &PWM_ApplyConfiguration,

           &PWM_Start,

           &PWM_Stop,

          &PWM_GetPinForChannel,

           //TinyGUI

           &LCD_ClearEx,

           &LCD_SetPixel,

           &LCD_GetPixel,

           &LCD_DrawLine,

          &LCD_DrawRectangle,

          &LCD_DrawEllipse,

           &LCD_DrawImage,

          &LCD_DrawImageEx,

           &LCD_DrawString,

           &LCD_DrawStringEx,

          &LCD_FillRectangle,

          &LCD_FillEllipse,

          &LCD_GetFrameBufferEx,

          &LCD_SuspendLayout,

          &LCD_ResumeLayout,

};

有了这些函数支持,就可以在MDK中独立编写 MF的用户流驱动了。当然,你也可以不用这些函数,也可以调用MDK相关的库或STM32提供的库,直接通过寄存器对硬件进行操作(前提是和已有的功能不要冲突就行)。

为了便于在MDK 4.x中开发用户流式驱动,我提供了yfmflib.h和grenralstream.h头文件,也提供了一个UserDriver.cpp模板,用户只要简单修改一下即可。

image.png

UserDriver.cpp模板中的代码如下:

//说明:代码空间0x08010000 -  0x08020000  64K

//      内存空间 0x20002000-  0x20004000   8K

#include "YFMFLib.h"

#include"GeneralStream.h"

 

#define UserDriver_Flag            "UserDriver"

#define UserDriver_Hander          1

 

const IGeneralStream_Function*MF=NULL;

 

intGeneralStream_Open1_UserDriver(LPCSTR config) { return 0;}      //Open1永远也不会被调用

//intGeneralStream_Open2_UserDriver(int config)  { return 0;}

intGeneralStream_Close_UserDriver()  {return 0;}

intGeneralStream_IOControl1_UserDriver(int code, BYTE *inBuffer, int inCount, BYTE*outBuffer, int outCount){return -1;}

intGeneralStream_IOControl2_UserDriver(int code, int parameter){return -1;} 

intGeneralStream_Read_UserDriver(BYTE *buffer, int offset, int count){return -1;}

intGeneralStream_Write_UserDriver(BYTE *buffer, int offset, int count){return -1;}

 

intGeneralStream_Open2_UserDriver(int config)

{ 

  //获取系统函数的指针

  MF = (IGeneralStream_Function*)config;

 

  //C#下传的参数

 MF->lcd_printf("%d,%s\r\n",MF->iParam1,MF->sParam1);

 MF->debug_printf("%d,%s\r\n",MF->iParam1,MF->sParam1);

}

 

extern const IGeneralStreamg_GeneralStream_UserDriver;

const IGeneralStreamg_GeneralStream_UserDriver  =

{

         UserDriver_Flag,

         &GeneralStream_Open1_UserDriver,

         &GeneralStream_Open2_UserDriver,   

         &GeneralStream_Close_UserDriver,     

         &GeneralStream_IOControl1_UserDriver, 

         &GeneralStream_IOControl2_UserDriver, 

         &GeneralStream_Read_UserDriver,

         &GeneralStream_Write_UserDriver,     

};

为了让大家印象深刻,我们以LCD1602的驱动为示例,进行用户驱动编写(我已经为其专门开发了一个流式驱动,以其为例只是便于说明,后续还将详细介绍LCD1602)。

其实LCD1602的显示就是IO操作,其实理论上在C#层也可以实现,但是通过我以上的性能测试,估计大家会鲜有尝试了。LCD1602的驱动,通过上网搜索,无论是C51、STM32还是Arduino都提供了相关的源码,我们只要把相关的IO操作的函数,转换为我们MF的IO操作函数即可。

主要代码如下:

void LCD1602_Write_byte(BYTE data)        

{

   MF->CPU_GPIO_SetPinState(LCD1602_E_Pin,TRUE);   

         MF->CPU_GPIO_SetPinState(LCD1602_D7_Pin,(data& 0x80)>0);

         MF->CPU_GPIO_SetPinState(LCD1602_D6_Pin,(data& 0x40)>0);

         MF->CPU_GPIO_SetPinState(LCD1602_D5_Pin,(data& 0x20)>0);

         MF->CPU_GPIO_SetPinState(LCD1602_D4_Pin,(data& 0x10)>0);

         MF->HAL_Time_Sleep_MicroSeconds_InterruptEnabled(1);

         MF->CPU_GPIO_SetPinState(LCD1602_E_Pin,FALSE);

        

         MF->HAL_Time_Sleep_MicroSeconds_InterruptEnabled(1);

         MF->CPU_GPIO_SetPinState(LCD1602_E_Pin,TRUE);

         MF->CPU_GPIO_SetPinState(LCD1602_D7_Pin,(data& 0x08)>0);

         MF->CPU_GPIO_SetPinState(LCD1602_D6_Pin,(data& 0x04)>0);

         MF->CPU_GPIO_SetPinState(LCD1602_D5_Pin,(data& 0x02)>0);

         MF->CPU_GPIO_SetPinState(LCD1602_D4_Pin,(data& 0x01)>0);

         MF->HAL_Time_Sleep_MicroSeconds_InterruptEnabled(1);

         MF->CPU_GPIO_SetPinState(LCD1602_E_Pin,FALSE);

}

 

void LCD1602_Write_Command(BYTE cmd)        

{

   MF->HAL_Time_Sleep_MicroSeconds_InterruptEnabled(100);

         MF->CPU_GPIO_SetPinState(LCD1602_RS_Pin,FALSE);

    LCD1602_Write_byte(cmd);

}

 

void LCD1602_Write_Data(BYTE data)        

{

   MF->HAL_Time_Sleep_MicroSeconds_InterruptEnabled(100);

         MF->CPU_GPIO_SetPinState(LCD1602_RS_Pin,TRUE);

    LCD1602_Write_byte(data);

}

 

void LCD1602_SetXY(BYTE x,BYTE y)//x:0~15,y:0~1

{

    if(y)LCD1602_Write_Command(0xc0+x);//第二行显示

    else  LCD1602_Write_Command(0x80+x);//第一行显示

}

 

void LCD1602_Write_Char(BYTE x,BYTE y,char data)

{

    LCD1602_SetXY( x, y); //写地址

    LCD1602_Write_Data(data);

}

 

void LCD1602_Print(BYTE x,BYTE y,char *s)

{

         if(x>15)x=15;

    LCD1602_SetXY( x, y ); //写地址  

    int i=0;

    while (*s &&(x+i++)<16)             //写显示字符

    {

        LCD1602_Write_Data(*s++ );

    }       

}

 

void LCD1602_Init()

{

   MF->CPU_GPIO_SetPinState(LCD1602_RW_Pin,FALSE);  //只写

   

   MF->HAL_Time_Sleep_MicroSeconds_InterruptEnabled(100000);

         LCD1602_Write_Command(0x33);

         MF->HAL_Time_Sleep_MicroSeconds_InterruptEnabled(20000);

         LCD1602_Write_Command(0x32);

         MF->HAL_Time_Sleep_MicroSeconds_InterruptEnabled(20000);

 

   LCD1602_Write_Command(0x28);

         LCD1602_Write_Command(0x0C);//显示开

         LCD1602_Write_Command(0x01);//清屏

         MF->HAL_Time_Sleep_MicroSeconds_InterruptEnabled(20000);

}

以上就是LCD驱动相关的代码,下面我们填写接口代码

int GeneralStream_Open2_UserDriver(int obj)

{ 

    //获取系统函数的指针

    MF =(IGeneralStream_Function*)obj;

 

  //--

  LPCSTR config =MF->sParam1;

    //不能有空格

    //   012345678901234567890123456789012345678901234567890123

    //格式RS=PC08,RW=PC09,E=PB06,D4=PB07,D5=PC00,D6=PC02,D7=PC03

    if(config[3]!='P' ||config[11]!='P' || config[18]!='P' || config[26]!='P' || config[34]!='P' ||config[42]!='P' || config[50]!='P')

    {

       return -1;

    }

 

    LCD1602_RS_Pin  =(GPIO_PIN)((config[4]-'A') * 16 +(config[5]-'0') * 10+ (config[6] - '0'));

  LCD1602_RW_Pin  =(GPIO_PIN)((config[12]-'A') * 16 +(config[13]-'0') * 10+ (config[14] - '0'));

    LCD1602_E_Pin  =(GPIO_PIN)((config[19]-'A') * 16 +(config[20]-'0') * 10+ (config[21] - '0'));

  LCD1602_D4_Pin  =(GPIO_PIN)((config[27]-'A') * 16 +(config[28]-'0') * 10+ (config[29] - '0'));

    LCD1602_D5_Pin  =(GPIO_PIN)((config[35]-'A') * 16 +(config[36]-'0') * 10+ (config[37] - '0'));

  LCD1602_D6_Pin   =(GPIO_PIN)((config[43]-'A') * 16 +(config[44]-'0') * 10+ (config[45] - '0'));

  LCD1602_D7_Pin   =(GPIO_PIN)((config[51]-'A') * 16 +(config[52]-'0') * 10+ (config[53] - '0'));

 

 MF->CPU_GPIO_EnableOutputPin(LCD1602_RS_Pin,FALSE);

  MF->CPU_GPIO_EnableOutputPin(LCD1602_RW_Pin,FALSE);

  MF->CPU_GPIO_EnableOutputPin(LCD1602_E_Pin,FALSE);

 MF->CPU_GPIO_EnableOutputPin(LCD1602_D4_Pin,FALSE);

  MF->CPU_GPIO_EnableOutputPin(LCD1602_D5_Pin,FALSE);

  MF->CPU_GPIO_EnableOutputPin(LCD1602_D6_Pin,FALSE);

 MF->CPU_GPIO_EnableOutputPin(LCD1602_D7_Pin,FALSE);

 

  LCD1602_Init(); //初始化液晶 

  return 0;

}

 

int GeneralStream_Write_UserDriver(BYTE *buffer, int offset, intcount)

{

    UINT8 x =(BYTE)((offset>>8) & 0xFF); 

  UINT8 y = (BYTE)(offset &0xFF);

    buffer[count]=0;

 

  if(x>15 || y>1 ) return-1;

    LCD1602_Print(x,y,(char*)buffer);

 

  return 0;

}

以上代码在MDK中直接编译,编译后的bin文件,经过转换适当转换,变为MF部署工具所支持的Hex文件,用MFDeploy或YFAccessFlash工具直接部署即可,如下图所示:
image.png

  

基于C++的代码我们已经完成,下一步我们开始写C#代码,以便调用我们写好的C++代码。

代码如下:

using System;

usingMicrosoft.SPOT;

usingMicrosoft.SPOT.Hardware;

usingYFSoft.IO;

 

namespaceUserDriverTest

{

    public class Program

    {   

        public static void Main()

        {

           Debug.Print("UserDriverTest ...");

           LCD1602 lcd = new LCD1602();

 

           lcd.Print(0, 0, "Hello .NET MF!!!");

           lcd.Print(0, 1, "YFSoft 20120920");

 

           while (true)

           {

               System.Threading.Thread.Sleep(500);

           }

        }

    }

 

    //Width = 16 Height = 2

    public class LCD1602

    {

        GeneralStream gs = null;

        public LCD1602()

        {

           gs = new GeneralStream();

            int ret =0;

           if ((ret = gs.Open("UserDriver", "RS=PC08,RW=PC09,E=PB06,D4=PB07,D5=PC00,D6=PC02,D7=PC03"))<= 0)

           {

               Debug.Print("ERR="+ ret.ToString());

               gs = null;

           }

        }

 

        public void Print(byte x, byte y, string s)

        {

           if (gs == null)return;

           byte[] temp = System.Text.UTF8Encoding.UTF8.GetBytes(s);

           byte[] buff = newbyte[temp.Length + 1];

           Array.Copy(temp, buff, temp.Length);

           buff[buff.Length - 1] = 0;

           gs.Write(buff, x << 8 | y, temp.Length);

        }

    }

}

代码执行后,其运行效果如下图所示:
image.png

   

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

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

MF资料:http://www.sky-walker.com.cn/News.asp?Id=25

相关实践学习
钉钉群中如何接收IoT温控器数据告警通知
本实验主要介绍如何将温控器设备以MQTT协议接入IoT物联网平台,通过云产品流转到函数计算FC,调用钉钉群机器人API,实时推送温湿度消息到钉钉群。
阿里云AIoT物联网开发实战
本课程将由物联网专家带你熟悉阿里云AIoT物联网领域全套云产品,7天轻松搭建基于Arduino的端到端物联网场景应用。 开始学习前,请先开通下方两个云产品,让学习更流畅: IoT物联网平台:https://iot.console.aliyun.com/ LinkWAN物联网络管理平台:https://linkwan.console.aliyun.com/service-open
相关文章
|
16天前
|
机器学习/深度学习 算法 算法框架/工具
为什么使用C++进行机器学习开发
C++作为一种高性能语言,在某些性能要求极高或资源受限的场景下也具有非常重要的地位。C++的高效性和对底层硬件的控制能力,使其在大规模机器学习系统中发挥重要作用,尤其是当需要处理大数据或实时响应的系统时。
31 3
|
18天前
|
传感器 存储 人工智能
智能农业的未来:物联网技术如何革新传统农业
本文探讨了物联网(IoT)技术在农业中的应用及其对传统农业的革新。通过详细分析当前农业面临的挑战,如资源浪费和效率低下,文章阐述了物联网技术如何通过实时数据监控和自动化系统提高农业生产的效率和可持续性。此外,文章还讨论了实施物联网技术时需要考虑的技术、经济和社会因素,以及未来发展趋势。
|
9天前
|
传感器 物联网 人机交互
物联网:物联网,作为新一代信息技术的重要组成部分,通过智能感知、识别技术与普适计算等通信感知技术,将各种信息传感设备与互联网结合起来而形成的一个巨大网络,实现了物物相连、人物相连,开启了万物互联的新时代。
在21世纪,物联网(IoT)作为新一代信息技术的核心,正以前所未有的速度重塑生活、工作和社会结构。本文首先介绍了物联网的概念及其在各领域的广泛应用,强调其技术融合性、广泛的应用范围以及数据驱动的特点。接着,详细阐述了物联网行业的现状和发展趋势,包括政策支持、关键技术突破和应用场景深化。此外,还探讨了物联网面临的挑战与机遇,并展望了其未来在技术创新和模式创新方面的潜力。物联网行业正以其独特魅力引领科技发展潮流,有望成为推动全球经济发展的新引擎。
|
11天前
|
物联网 C# C语言
物联网开发中C、C++和C#哪个更好用
在物联网(IoT)开发中,C、C++和C#各有优缺点,适用场景不同。C语言性能高、资源占用低,适合内存和计算能力有限的嵌入式系统,但开发复杂度高,易出错。C++支持面向对象编程,性能优秀,适用于复杂应用,但学习曲线陡峭,编译时间长。C#易于学习,与.NET框架结合紧密,适合快速开发Windows应用,但性能略低,平台支持有限。选择语言需根据具体项目需求、复杂性和团队技术栈综合考虑。
|
16天前
|
Java Android开发 C++
🚀Android NDK开发实战!Java与C++混合编程,打造极致性能体验!📊
在Android应用开发中,追求卓越性能是不变的主题。本文介绍如何利用Android NDK(Native Development Kit)结合Java与C++进行混合编程,提升应用性能。从环境搭建到JNI接口设计,再到实战示例,全面展示NDK的优势与应用技巧,助你打造高性能应用。通过具体案例,如计算斐波那契数列,详细讲解Java与C++的协作流程,帮助开发者掌握NDK开发精髓,实现高效计算与硬件交互。
58 1
|
2月前
|
人工智能 网络协议 物联网
AIoT智能物联网平台技术架构
AIoT智能物联网平台的技术架构从终端设备到物联网平台可分为边缘侧网关、接入网关层、基础设施层、中台层和应用层。
70 14
|
2月前
|
人工智能 监控 安全
未来家居生活的智能革命:物联网技术的融合与应用
【8月更文挑战第31天】 随着科技的飞速发展,物联网技术已逐渐融入我们的日常生活。本文将探讨物联网技术如何改变我们的生活方式,特别是在家中的应用。从智能家居系统的构建到日常生活中的实际案例,我们将一窥物联网带来的便利与挑战,并提出对未来发展的展望。
37 1
|
1月前
|
图形学 C++ C#
Unity插件开发全攻略:从零起步教你用C++扩展游戏功能,解锁Unity新玩法的详细步骤与实战技巧大公开
【8月更文挑战第31天】Unity 是一款功能强大的游戏开发引擎,支持多平台发布并拥有丰富的插件生态系统。本文介绍 Unity 插件开发基础,帮助读者从零开始编写自定义插件以扩展其功能。插件通常用 C++ 编写,通过 Mono C# 运行时调用,需在不同平台上编译。文中详细讲解了开发环境搭建、简单插件编写及在 Unity 中调用的方法,包括创建 C# 封装脚本和处理跨平台问题,助力开发者提升游戏开发效率。
52 0
|
17天前
|
编译器 C++
C++ 类构造函数初始化列表
构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。
60 30
|
6天前
|
并行计算 Unix Linux
超级好用的C++实用库之线程基类
超级好用的C++实用库之线程基类
12 4

相关产品

  • 物联网平台
  • 下一篇
    无影云桌面