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.
Tutorial on howto use GPIO from C# and C++
I forgot to mention that DLLsetPin makes the Pin a output pin automatically, that is why there is no counterpart to DLLmakeInputPin.
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!
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
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"
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.
Hi Max! Do you think you could email me an example where this code is used? Antongildebrand@gmail.com
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);
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.
Hi McJonster Can you share your driver with example? andres.e.e.barre@gmail.com Thanks muchas gracias!!!!
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
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?!
I can not Compile this dll, neither with eVC++ nor with VS2005, please anyone attach it or upload it here!
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.
Hello McJonster, Is it possible to ask you to send your whole example also to me please? svdwcomp@hotmail.com Thanks in advance.
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
McJonster, could you please share your project with me as well? Many thanks in advance. Lars T lt20@hotmail.com
Can any body can please provide me a complete example on how to access GPIO using visual studio 2005 for mini2440
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