Tutorial on howto use GPIO from C# and C++

Max Penth
Hello,

as there seem to be many requests on how to do this I will post how I did
it.

I needed a way to access GPIO from C#. As there doesn't seem to be to
access the registers directly you have to create a C++-DLL and call it with
P/Invoke.

I copied parts out of my code to make it more readable, so it is possible
that there are small errors.

This is the DLL-Code:


#include "stdafx.h"
#include "I2CDLL.h"

#include "Pkfuncs.h"

#include "s2440.h"   //In some BSPs this file has a slightly different name
like s3c2440a.h, which then include some other .h files (copy those too)
#include <windows.h>
#include <time.h>
#include <ceddk.h>

void gpioInit();
void Virtual_Alloc();

bool memoryAlloced = false;


BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
           )
{
    switch (ul_reason_for_call)
  {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
      break;
    }
    return TRUE;
}


volatile IOPreg *s2440IOP = (IOPreg *)IOP_BASE;

extern "C" _declspec(dllexport) bool WINAPI DLLreadPin (int port, int pin)
{
  gpioInit();
  switch (port) {
  case 0:
    if (s2440IOP->rGPADAT & (0x1 << pin))
      return true;
    else
      return false;
    break;
  case 1:
    if (s2440IOP->rGPBDAT & (0x1 << pin))
      return true;
    else
      return false;
    break;
  case 2:
    if (s2440IOP->rGPCDAT & (0x1 << pin))
      return true;
    else
      return false;
    break;
  case 3:
    if (s2440IOP->rGPDDAT & (0x1 << pin))
      return true;
    else
      return false;
    break;
  case 4:
    if (s2440IOP->rGPEDAT & (0x1 << pin))
      return true;
    else
      return false;
    break;
  case 5:
    if (s2440IOP->rGPFDAT & (0x1 << pin))
      return true;
    else
      return false;
    break;
  caase 6:
    if (s2440IOP->rGPGDAT & (0x1 << pin))
      return true;
    else
      return false;
    break;
  }
  return false;
}

extern "C" _declspec(dllexport) int WINAPI DLLmakeInputPin(int port, int
pin) {
  gpioInit();
  switch (port) {
  case 0:
    s2440IOP->rGPACON = (s2440IOP->rGPACON &~ ( 3 << pin*2)) ;
    break;
  case 1:
    s2440IOP->rGPBCON = (s2440IOP->rGPBCON &~ ( 3 << pin*2)) ;
    break;
  case 2:
    s2440IOP->rGPCCON = (s2440IOP->rGPCCON &~ ( 3 << pin*2)) ;
    break;
  case 3:
    s2440IOP->rGPDCON = (s2440IOP->rGPDCON &~ ( 3 << pin*2)) ;
    break;
  case 4:
    s2440IOP->rGPECON = (s2440IOP->rGPECON &~ ( 3 << pin*2)) ;
    break;
  case 5:
    s2440IOP->rGPFCON = (s2440IOP->rGPFCON &~ ( 3 << pin*2)) ;
    break;
  case 6:
    s2440IOP->rGPGCON = (s2440IOP->rGPGCON &~ ( 3 << pin*2)) ;
    break;
  }
  return 0;
}

extern "C" _declspec(dllexport) int WINAPI DLLsetPin(int port, int pin,
bool on) 
{
  gpioInit();
  switch (port) {
  case 0:
    s2440IOP->rGPACON = (s2440IOP->rGPACON &~ ( 3 << pin*2)) | ( 1<< pin*2);  
    if (!on)
      s2440IOP->rGPADAT = (s2440IOP->rGPADAT &~ (0x1 << pin));
    else
      s2440IOP->rGPADAT = (s2440IOP->rGPADAT | (0x1 << pin));
    break;
  case 1:
    s2440IOP->rGPBCON = (s2440IOP->rGPBCON &~ ( 3 << pin*2)) | ( 1<< pin*2);  
    if (!on)
      s2440IOP->rGPBDAT = (s2440IOP->rGPBDAT &~ (0x1 << pin));
    else
      s2440IOP->rGPBDAT = (s2440IOP->rGPBDAT | (0x1 << pin));
    break;
  case 2:
    s2440IOP->rGPCCON = (s2440IOP->rGPCCON &~ ( 3 << pin*2)) | ( 1<< pin*2);  
    if (!on)
      s2440IOP->rGPCDAT = (s2440IOP->rGPCDAT &~ (0x1 << pin));
    else
      s2440IOP->rGPCDAT = (s2440IOP->rGPCDAT | (0x1 << pin));
    break;
  case 3:
    s2440IOP->rGPDCON = (s2440IOP->rGPDCON &~ ( 3 << pin*2)) | ( 1<< pin*2);  
    if (!on)
      s2440IOP->rGPDDAT = (s2440IOP->rGPDDAT &~ (0x1 << pin));
    else
      s2440IOP->rGPDDAT = (s2440IOP->rGPDDAT | (0x1 << pin));
    break;
  case 4:
    s2440IOP->rGPECON = (s2440IOP->rGPECON &~ ( 3 << pin*2)) | ( 1<< pin*2);  
    if (!on)
      s2440IOP->rGPEDAT = (s2440IOP->rGPEDAT &~ (0x1 << pin));
    else
      s2440IOP->rGPEDAT = (s2440IOP->rGPEDAT | (0x1 << pin));
    break;
    
  case 5:
    s2440IOP->rGPFCON = (s2440IOP->rGPFCON &~ ( 3 << pin*2)) | ( 1<< pin*2);  
    if (!on)
      s2440IOP->rGPFDAT = (s2440IOP->rGPFDAT &~ (0x1 << pin));
    else
      s2440IOP->rGPFDAT = (s2440IOP->rGPFDAT | (0x1 << pin));
    break;
  case 6:
  
    s2440IOP->rGPGCON = (s2440IOP->rGPGCON &~ ( 3 << pin*2)) | ( 1<< pin*2);  
    if (!on)
      s2440IOP->rGPGDAT = (s2440IOP->rGPGDAT &~ (0x1 << pin));
    else
      s2440IOP->rGPGDAT = (s2440IOP->rGPGDAT | (0x1 << pin));
    break;


  }
  
  return 0;
}

void Virtual_Alloc()
{

    // GPIO Virtual alloc
  s2440IOP = (volatile IOPreg *) VirtualAlloc(0,sizeof(IOPreg),MEM_RESERVE,
PAGE_NOACCESS);
  if(s2440IOP == NULL) {
    RETAILMSG(1,(TEXT("For s2440IOP: VirtualAlloc faiLED!\r\n")));
    
  }
  else {
   
if(!VirtualCopy((PVOID)s2440IOP,(PVOID)(IOP_BASE),sizeof(IOPreg),PAGE_READWRITE
| PAGE_NOCACHE )) {
      RETAILMSG(1,(TEXT("For s2440IOP: VirtualCopy faiLED!\r\n")));
    }
  }
 
  
}

void gpioInit() {
  if (!memoryAlloced) {
    
    Virtual_Alloc();
    memoryAlloced = true;
    
  }

}

The included header files can be found in the WinCE SDK and the BSP for the
Board. The .h file for the DLL looks like this (my DLL is named I2Cdll):

#ifdef I2CDLL_EXPORTS
#define I2CDLL_API __declspec(dllexport)
#else
#define I2CDLL_API __declspec(dllimport)
#endif

// This class is exported from the I2CDLL.dll
class I2CDLL_API CI2CDLL {
public:
  CI2CDLL(void);
  // TODO: add your methods here.
};

extern I2CDLL_API int nI2CDLL;

I2CDLL_API int fnI2CDLL(void);


Compile this DLL with eVC++, then place the .dll in your application folder
(deploy along with application), the it can be accessed with C# like this:

Declare Imports in beginning of class:

[DllImport("I2CDLL.dll")]
extern private static int DLLsetPin(int port, int pin, bool state);

[DllImport("I2CDLL.dll")]
extern private static bool DLLreadPin(int port, int pin);

[DllImport(("I2CDLL.dll"))]
private static extern int DLLmakeInputPin(int port, int pin);


From your code just call the function, e.g. for setting GPBC3 to true :
  DLLsetPin(2, 4, true);  //2 is port, beggining with 0, 4 is pin,
beginning with 0


Using PWM/SPI/Watchdog works basically the same, just initialise the proper
registers like done above with IOPreg.

I am aware that the code could be improved in terms of elegance (basically
make 1 line out of all that switch-clutter), but I really didn't bother.

Btw.: This is _a lot_ faster than implementing this stuff into a stream
interface driver (which has to be included in the OS-Image). Calling a
stream interface driver from C# is painfully slow.

Max Penth
I forgot to mention that DLLsetPin makes the Pin a output pin
automatically, that is why there is no counterpart to DLLmakeInputPin.

wanderer
Nice!! Thanks for that!!

I'm learning how to work with dll's and the .net framework so this will
take me some time to master... but would it be to much to ask for you to
upload or send me via email (_wanderer_@live.com)a simple example that
polls input on a GPIO and shows its status on a form??

Hope I'm not asking for much and thanks in advance!

wanderer
Has anyone being able to compile the .dll??

Robert wright
I apologize for replying to this post but have been unable to figure
out/find how to start a new thread on the website and this is the closest
topic to my questions.  I am new to using CE and am a bit lost.  

I have built several CE images and have it working on my board.  Using
Mini35 from andahammer with 128Mb ram.  

I am trying to figure out first how to write/add an application to my OS
image.  I have Embedded Visual C++ and have opened some applications that
were on the dvd but when I try to build the project it attempts to
communicate with the board and send it directly.  Is it possible to
complile and add to the OS design to be included in the image when I sysgen
it or do I need to add it after the image is installed on the board?  

Once I am sucessful at adding a working application I will need to access
the gpio in an applicaion that I will be writing.  I understand about half
of the code above and next to nothing about using .dll's.  

Would someone be kind enough to point me in the right direction.  What do I
need to teach myself and more importantly where can i find some good
information to get started.

Thank you for your help

Robert wright
Apparently either the computer I was on didn't like the links at the top of
the forums page or the page didnt load properly because they are in place
and working now.  I kept thinking why would someone create a forum main
page and hide the new thread link. Sorry about the confusion and please
disregard the above post or if you are feeling generous I have started a
new thread "Windows CE applications beginner in need of direction"

Anton Gildebrand
Can i download an example where this code is used to something somewhere?

afaz
Hi Max Penth
Thanks for your very good post.It's useful but for C# programmers it's a
little hard to compile C++ code with all include stuff.I can't find all
includes in your code and I get error compiling your codes.It will be
appriciated if you upload somewhere the compiled dll so taht C# programmers
can use that.
if that is possible for you send me that dll on my e-mail address:
afaaz2004@yahoo.com
thanks.

Anton Gildebrand
Hi Max!

Do you think you could email me an example where this code is used?

Antongildebrand@gmail.com

pilamaster
If it not so hard, please send me this compiled dll.
pilamaster[at]gmail.com Thanks a lot.

McJonster
I've compiled the DLL no problem using Visual Studio 2005.  

However I can't call it under WinCE 6 because VirtualCopy/VirtualAlloc
isn't supported unless it's a kernel mode.

I'd like to write as a driver DLL but not sure about the import/export of
DLL functions under WinCE.

I know assembly and computer arch.  All I want to do is write/read to a
specific memory mapped i/o register.

Are there any simple examples of driver development someone wants to share?

I just want something like: write(Address=0xffff,Value=0x51);

McJonster
I have successfully gotten this to work but it required that I write a GPIO
stream driver.

Now the ports are easily opened with CreateFile and I can Read/Write from
them.  This is the proper way to do it.

Not easy for other people that will try it.

pilamaster
McJonster, can you share your driver with example of usage?
pilamaster[at]gmail.com Thanks a lot.

Mingyu
Hi, McJonster. Could u please share your whole example?
mingyuwang[at]ust.hk
Thanks a million ~~

ergun kucukkose
Hi McJonster 
Can you share your driver with example?

kucukkose@gmail.com

Thanks

RIME
Please share to :

rihardmeznar[at]gmail.com

Thanks a lot

andrés barré
Hi McJonster 
Can you share your driver with example?

andres.e.e.barre@gmail.com

Thanks

muchas gracias!!!!

Mathias Tantau
Hello McJonster,
would you be so kind as to share it with me too please?
I am shure it would spare me a lot of work!
email: mathias.tantau@gmx.de
Thank you

jeangr
is it possible to have this c++ dll compiled to import in c#?

thanks

jeangr@alice.it

alex
Yes, please someone send me the compiled DLL to alex_houben AT hotmail com


Thanks

h4med
For me too, if it is possible, hamed.d AT Gmail dot Com

Thanks

h4med
Trying to compile the DLL, I've got this Error:

1>.\I2CDLL.cpp(1) : fatal error C1083: Cannot open precompiled header file:
'Mini2440-CE6-SDK (ARMV4I)\Debug/I2CDLL.pch': No such file or directory


What is wrong?!

domodom
Try to not use precompiled headers.

h4med
I can not Compile this dll, neither with eVC++ nor with VS2005, please
anyone attach it or upload it here!

Dazzler
Hi McJonster,
Not sure if you're actioning these requests, but just in case, I would love
to see the GPIO stream driver. darren(at)bruning.net.nz
Thanks heaps.

Simoniumly
Hello McJonster, 

Is it possible to ask you to send your whole example also to me please?
svdwcomp@hotmail.com

Thanks in advance.

Peter1122
This is a source code of pwm.dll that I compiled eVC4 to have access to PWM
register (you might control the buzzer on 2440-check it myself - it works)

CPP file

// pwm.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "pwm.h"
#include "s2440.h"
#include <ceddk.h>

extern "C" BOOL VirtualCopy(LPVOID dest, LPVOID src, DWORD size,DWORD
flags);

//-----------------------------------------------------------------------------
BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
           )
{
    switch (ul_reason_for_call)
  {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
      break;
    }
    return TRUE;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static BOOL SetFeq(DWORD Feq)
{
  BOOL ret = FALSE;
  volatile IOPreg   *s2440IOP = 0;
  volatile PWMreg    *s2440PWM = 0;
  do {
    s2440IOP = (volatile IOPreg *)
VirtualAlloc(0,sizeof(IOPreg),MEM_RESERVE, PAGE_NOACCESS);
    if (s2440IOP == 0) break;
    
    s2440PWM = (volatile PWMreg *)
VirtualAlloc(0,sizeof(PWMreg),MEM_RESERVE, PAGE_NOACCESS);
    if (s2440PWM == 0) break;
    

    if
(!VirtualCopy((PVOID)s2440IOP,(PVOID)(IOP_BASE),sizeof(IOPreg),PAGE_READWRITE
| PAGE_NOCACHE )) 
      break;
    

    if
(!VirtualCopy((PVOID)s2440PWM,(PVOID)(PWM_BASE),sizeof(PWMreg),PAGE_READWRITE
| PAGE_NOCACHE )) 
      break;
    

    ret = TRUE;
    if (Feq == 0) 
    {
      // STOP
      s2440PWM->rTCON &= ~1;
      s2440IOP->rGPBCON = (s2440IOP->rGPBCON & ~(0x3<<0x0)) | 0x1;
      s2440IOP->rGPBDAT &= ~1;
    } else 
    {
      // Change
      s2440IOP->rGPBCON = (s2440IOP->rGPBCON & ~(0x3<<0x0)) | 0x2;

      s2440PWM->rTCFG0 = s2440PWM->rTCFG0 & ~0xFF | 49;
      s2440PWM->rTCFG1 = s2440PWM->rTCFG1 & ~0xF  | 0x3;
      s2440PWM->rTCNTB0 = (1000 * 1000) / 16 / Feq;
      s2440PWM->rTCMPB0 = (1000 * 1000) / 16 / Feq / 2;
      s2440PWM->rTCON = s2440PWM->rTCON & ~0x1F | 0x0b;
      s2440PWM->rTCON &= ~2;

    }
  } while (0);

  //RETAILMSG(1,(TEXT("Set FEQ = %u, ret = %d\r\n"), Feq, ret));

  if (s2440IOP) 
  {
    VirtualFree((void*)s2440IOP, sizeof(IOPreg), MEM_RELEASE);
    s2440IOP = 0;
  }
  if (s2440PWM) 
  {
    VirtualFree((void*)s2440PWM, sizeof(PWMreg), MEM_RELEASE);
    s2440PWM = 0;
  }

  return ret;
}
//-----------------------------------------------------------------------------
PWM_API BOOL PWM_IOControl(DWORD hOpenContext, 
           DWORD dwCode, 
           PBYTE pBufIn, 
           DWORD dwLenIn, 
           PBYTE pBufOut, 
           DWORD dwLenOut, 
           PDWORD pdwActualOut)
{
 
  BOOL ret = FALSE;
  switch(dwCode) 
  {
  case PWM_STOP:
    ret = SetFeq(0);
    break;
  case PWM_SET_FEQ:
    {
      DWORD feq;
      if ( dwLenIn != sizeof feq ) 
        break;
      
      feq = * (DWORD *)pBufIn;
      ret = SetFeq(feq);
    }
    break;
 }          
  return ret;
} 
//-----------------------------------------------------------------------------

header
pwm.h

#ifdef PWM_EXPORTS
#define PWM_API extern "C" __declspec(dllexport)
#else
#define PWM_API extern "C" __declspec(dllimport)
#endif


#define PWM_SET_FEQ      1
#define PWM_STOP      0


PWM_API BOOL PWM_IOControl(DWORD hOpenContext,DWORD dwCode,PBYTE
pBufIn,DWORD dwLenIn,
                PBYTE pBufOut,DWORD dwLenOut,PDWORD pdwActualOut);


you need to include to the project s2440.h may find it WinCE-BSP, header
<ceddk.h> is a part of WinCE-SDK which need to be installed before
compilation.
after compilation you need to include pwm.lib and pwm.h to arm aplication
project.
I can upload the source code but I'm new here so don't know where so far.

Peter

Lars Thörnqvist
McJonster, could you please share your project with me as well?

Many thanks in advance.

Lars T

lt20@hotmail.com

Waqas
Can any body can please provide me a complete example on how to access GPIO
using visual studio 2005 for mini2440

john
http://code.google.com/p/friendlyarm/wiki/WinCE_HowToStart
http://www.domodom.fr/spip/A-GPIO-driver-for-mini2440.html?lang=en

elexal
One example is C #?

jorge moreno
good morning

I would like you please publish a short code compiled, and the steps step
by step, how to handle GPIO, both for the mini6410 mini2440 to, we are many
people who could not do this, and it is important your help.

thank you very much. my email is joran06@hotmail.com