Analysis of Wince 6.0 interrupt driver by: hjb
Wiince interrupt stream implementation driver and app 51 WinCE
[Original] wince interrupt driver development practices 51 WinCE
This article mainly summarizes the interrupted development process of Wince in the recent period. This article is only suitable for beginners!
First, let's recall what interruption is. Please read the "interruption analysis" article. Here we will recall that the concept of interruption comes from my own experiences, I graduated from a computer school in a finance-dominated University and majored in software engineering and ERP financial management software development. However, due to work, the main work after graduation is switched to embedded development. The interruption can only be vague, and there is basically no practical experience, I was so frustrated that I was not even interrupted by jokes from my colleagues for a while, but I was determined to learn something in this industry, after all, it is more difficult to change this line.
We have provided some examples and introduction to the above preparations for the wince interruption. Here we will try again to improve the overall development process.
As an embedded OS, the importance of disconnection is self-evident. In the analysis of the wince 6.0 interrupt driver, hjb has clearly introduced the interrupt PROCESS IN wince, readers who are not familiar with this article must read this article carefully. After reading this article many times, I began to write demand analysis, that is, some requirement descriptions in the question "[original] wince interrupt driver development practices.
Next, I will analyze an example of the complete process of Interrupt driver + app testing program development. First, we want to deepen our influence, and second, we want to give some beginners some useful materials. Maybe a beginner's friend has encountered similar experiences and should be ridiculed for not understanding the interruptions. What I want to say is,Although I didn't understand it at the beginning, I can still learn it by myself, that is, wearing others' shoes and taking my own path so that others can find it..
Let's talk less about the subject.
The example of this development is based on WinCE's interrupted development. The specific content is described below:
Hardware environment: 1. the toggle-Toggle potentiometer is used to trigger the interruption and generate some values (similar to the function of pressing keys );
2. MCU, used to generate an interrupt and send the value to the arm subject;
3. Arm, used to accept the subject of MCU interruption and Acceptance value;
4. the LCD screen displays the MCU output value and corresponding processing information after interruption;
Software environment: 1. platformbuilder 5.0 OS wince5.0 System Development
2. vs2005 AP LAYER DEVELOPMENT
Design Concept: 1. Switch the potentiometer (Frisbee) rotation;
2. MCU collects the rotation status of the switch potentiometer and obtains the corresponding rotation value through AD sampling;
3. the MCU generates an interrupt signal, which lowers a pin of the MCU and connects the pin to a pin of the arm;
4. Arm defines the interrupt status of the MCU connection pin and waits for the response to the interrupt command;
5. When the arm responds to the interrupt status of the specified pin, it sends the MCU Communication command to obtain the value collected by the MCU;
6. After obtaining the value, arm reads the value and transmits the value from the driver layer to the AP layer;
7. The AP layer uses API functions to connect to the driver layer and transmit information, and displays the received values;
Actual development:
MCU program development:
We do not describe mcu development too much here. We only need to obtain the AD sampling value when the toggle is triggered and the corresponding IO port is lowered, it is enough to generate an interrupt signal for the arm, but the analysis of the AD sample value is also a relatively large project, which requires some error correction and processing, but here we mainly analyze the interrupted development under wince, and we will not introduce it here;
OS wince driver development:
For interrupt driver development, my step is to first refer to "wince 6.0 interrupt driver analysis" by hjb Daniel. then, refer to the reference code collected on the Internet for the development of key-press interruptions under 2410. For the specific code, see download resources in wiince interrupt stream implementation driver and app.
Through reading the above two articles, we can have a general understanding of the development-driven framework and have an understanding of the process of interrupt-driven development. Here I want to propose four functions: xxx_detectthread, xxx_init, xxx_deinit, and mcl_read, which are required by the project, the xxx here can be understood as defined in MCU or in their respective projects, and can be defined at will. Of course, the last three are fixed formats of stream drivers;
Xxx_detectthread: powerbuttonintrthread is used in the hjb Daniel's article. For details, refer to eintkey_intrthread in driver development in 2410. Here we compromise the name of xxx_detectthread, the main function implemented by this function is to create an endless loop and wait for the model quantity. Here we provide the implementation part of this function, where there is a mcuctl struct, you can define according to your own needs, mainly handle and DWORD types, to prepare for events and priorities in advance, in the program, you can differentiate the content defined in the structure according to the value assignment. Here I will not introduce it one by one, giving you a space to read the program.
1: /////////////////////////////////////////////////////////////////////
2: //=============================================================================
3: //Title : MCU_DetectThread
4: //Detail: Receive INTR EVENT from gpio for mcu communication
5: //Input : PVOID pArg
6: //Output: DWORD
7: //Author: Mercury
8: //Data : 2009-12-26
9: //=============================================================================
10: DWORD MCU_DetectThread(PVOID pArg)
11: {
12: DWORD dwRet, dwAction;
13: MCUCTL*pMcuCtl = (MCUCTL *)pArg;
14: HANDLE rghEvents[2] = {pMcuCtl->hDeinitEvt, pMcuCtl->hGioEvt};
15: unsigned char i = 0;
16: RETAILMSG(1, (TEXT("MCU_DetectThread Enter\r\n")));
17: CeSetThreadPriority(GetCurrentThread(), pMcuCtl->dwPriority256);
18: Sleep(3000);
19: while (1)
20: {
21: dwRet = WaitForMultipleObjects(2, rghEvents, FALSE, INFINITE);
22: if(pMcuCtl->bDeinit)
23: {
24: return 0;
25: }
26: switch(dwRet)
27: {
28: case WAIT_OBJECT_0:
29: RETAILMSG(1, (TEXT("mcu_DetectThread Wait deinit\r\n")));
30: //deinit event
31: return 0;
32: break;
33:
34: case WAIT_OBJECT_0+1: //power key event
35: {
36: //RETAILMSG(1, (TEXT("mcu_DetectThread Wait gio\r\n")));
37: //__try
38: //{
39: operationmcu (read, & sendout [0], 2); // MCU Communication function module
40: #if 1
41: for(i = 0 ; i < 2 ; i++)
42: {
43: RETAILMSG(1,(TEXT("the %d number SentOut Value for point variable is %x\r\n"),i,sendOut[i]));
44: }
45: #endif
46: //}
47: //__except (GetExceptionCode() == STATUS_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
48: //{
49: // RETAILMSG(1, (TEXT("Emcu_DetectThread MCU_gio!!!!\r\n")));
50: // SetLastError(E_FAIL);
51: //}
52: setevent (greadkeyevent [0]);/* notify the READ function, press the External Interrupt button */
53: }
54: break;
55:
56: default:
57: //error
58: dwAction = 0x00;//MCU_STAT_NOCHANGE;
59: break;
60: }
61: //do real action
62: }
63: return 0;
64: }
Here you can pay attention to the usage skills of cesetthreadpriority, waitformultipleobjects and swich.
The xxx_init function plays the same role in reference articles. It initializes the interrupt pin. After initializing the corresponding PIN, it creates a thread for the xxx_detectthread defined above, create two read key events at the end of initialization. The reference code is provided below. Some sensitive parts are replaced by pseudo code.
1: extern "C" DWORD MCL_Init(DWORD Index)
2: {
3: #if 1
4: BOOL bEn;
5: DWORD IDThread, dwPullUpOrDown;
6: IOCTL_INFO ioctl_info;
7: WINCE_GPIO_DEFINE pin;
8: MSGQUEUEOPTIONS msgQueueOptions = {0};
9: MCUCTL *pMcuCtl = NULL;
10: #endif
11:
12: RETAILMSG(1,(TEXT("++MCU_Init!\r\n ")));
13: InitializeCriticalSection(&m_removalLock);//add by mercury for lock and unlock 20090819
14: # If 1 // initialization interruption starts
15: pMcuCtl = (MCUCTL *)LocalAlloc(LPTR, sizeof(MCUCTL));
16: if(pMcuCtl == NULL)
17: {
18: RETAILMSG(1, (TEXT("-MCU_INIT\r\n")));
19: return(0);
20: }
21: memset(pMcuCtl, 0, sizeof(MCUCTL));
22:
23: //set rtc thread to priority to 104
24:
25: pMcuCtl->dwPriority256 = MCU_DEFAULT_THREAD_PRIORITY;
26:
27: pMcuCtl->hGioEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
28: pMcuCtl->hDeinitEvt= CreateEvent(NULL, FALSE, FALSE, NULL);
29: if((pMcuCtl->hGioEvt == NULL) || (pMcuCtl->hDeinitEvt == NULL) ){
30: DEBUGMSG(ZONE_ERROR, (TEXT("MCU_Init::CreateEvent failed\r\n")));
31: RETAILMSG(1,(TEXT("MCU_Init::CreateEvent failed\r\n")));
32: goto Error;
33: }
34:
35: pMcuCtl->hDetectThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) MCU_DetectThread, (LPVOID)pMcuCtl, 0, &IDThread);
36: if (pMcuCtl->hDetectThread == 0)
37: {
38: DEBUGMSG(ZONE_ERROR, (TEXT("MCU_Init::: CreateThread Failed\r\n")));
39: RETAILMSG(1,(TEXT("MCU_Init::: CreateThread Failed\r\n")));
40: goto Error;
41: }
42:
43: pMcuCtl->hGio= CreateFile(L"GIO1:", GENERIC_READ | GENERIC_WRITE,0, NULL, OPEN_EXISTING, 0, NULL);
44: if(pMcuCtl->hGio == INVALID_HANDLE_VALUE) {
45: DEBUGMSG(ZONE_ERROR, (TEXT("MCU_Init::Open gio failed\r\n")));
46: RETAILMSG(1,(TEXT("MCU_Init::: Open gio Failed\r\n")));
47: goto Error;
48: }
49:
50: //init gpio here
51: //power button gpio init start
52: pin = WINCE_DGPIO2;//BSP_GetPowerButtonIO();
53: if(pin == WINCE_NULLIO) {
54: goto Error;
55: }
56:
57: ioctl_info.pin = ( UINT32)pin;
58: //set input
59: ioctl_info.parameter.Setting = GPIO_DIR_INPUT;
60: DeviceIoControl(pMcuCtl->hGio, DRIVER_GPIO_IOCTL_SET_DIRECTION,
61: (LPVOID)&ioctl_info, sizeof(IOCTL_INFO), NULL, 0 ,NULL, NULL);
62:
63: //set event handle
64: ioctl_info.parameter.Setting = (UINT32)pMcuCtl->hGioEvt;
65: DeviceIoControl(pMcuCtl->hGio, DRIVER_GPIO_IOCTL_SET_HANDLE,
66: (LPVOID)&ioctl_info, sizeof(IOCTL_INFO), NULL, 0 ,NULL, NULL);
67: //polarity to trigger interrupt
68: ioctl_info.parameter.Setting = BSP_GetPowerButtonPolarity();
69: DeviceIoControl(pMcuCtl->hGio, DRIVER_GPIO_IOCTL_SET_POLARITY,
70: (LPVOID)&ioctl_info, sizeof(IOCTL_INFO), NULL, 0 ,NULL, NULL);
71: //set change polarity after interrupt
72: ioctl_info.parameter.Setting = GPIO_IO_INT_CHG_POLARITY;
73: DeviceIoControl(pMcuCtl->hGio, DRIVER_GPIO_IOCTL_SET_PINCHANGE,
74: (LPVOID)&ioctl_info, sizeof(IOCTL_INFO), NULL, 0 ,NULL, NULL);
75: //enable pull down
76: dwPullUpOrDown = BSP_GetPowerButtonPullUpDown(&bEn);
77: if(bEn) {
78: ioctl_info.parameter.Setting = TRUE;
79: }
80: else {
81: ioctl_info.parameter.Setting = FALSE;
82: }
83: DeviceIoControl(pMcuCtl->hGio, dwPullUpOrDown,
84: (LPVOID)&ioctl_info, sizeof(IOCTL_INFO), NULL, 0 ,NULL, NULL);
85: //enable power key interrupt
86: ioctl_info.parameter.Setting = GPIO_INTE_ENABLE;
87: DeviceIoControl(pMcuCtl->hGio, DRIVER_GPIO_IOCTL_SET_INTR,
88: (LPVOID)&ioctl_info, sizeof(IOCTL_INFO), NULL, 0 ,NULL, NULL);
89: // initialization ends
90: gReadKeyEvent[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
91: gReadKeyEvent[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
92: RETAILMSG(1,(TEXT("--MCU_Init!\r\n ")));
93: return(DWORD)pMcuCtl;
94:
95: Error:
96: (VOID)MCL_Deinit((DWORD)pMcuCtl);
97: RETAILMSG(1,(TEXT("--error !MCU_Init!\r\n ")));
98: #endif
99: return(0);
100: }
Pay attention to the createthread function, which is required in the interrupt program initialization function. It is used to call the previously defined xxx_detectthread function.
The xxx_deinit function is an initial and final expression. The corresponding processing function of init is defined in the last error section of the init function, when an error occurs during initialization, the process is redirected to the error handling part. We need to destroy a series of created objects, namely deinit, to prevent space occupation and re-startup failure, that is, the last sentence in the hjb article: "When you use the driver debugging assistant to debug the interrupted driver, you must start from the beginning and end; otherwise, the interruption may fail to work normally."
The following code is provided.
1: extern "C" BOOL MCL_Deinit(DWORD dwData)
2: {
3: MCUCTL *pMcuCtl = (MCUCTL *)dwData;
4: WINCE_AK7801_GPIO_DEFINE pin;
5: pMcuCtl->bDeinit = TRUE;
6: RETAILMSG(1,(TEXT("MCU_DeInit!\r\n ")));
7: DeleteCriticalSection(&m_removalLock);
8: //add by mercury xu for lock and unlock si4730 20090819
9: if(pMcuCtl->hGio) {
10: IOCTL_INFO ioctl_info;
11: //disable power button interrupt
12: pin = WINCE_AK7801_DGPIO2;//BSP_GetPowerButtonIO();
13: if(pin != WINCE_AK7801_NULLIO) {
14: ioctl_info.pin = (UINT32)pin;
15: ioctl_info.parameter.Setting = GPIO_INTE_DISABLE;
16: DeviceIoControl(pMcuCtl->hGio, DRIVER_GPIO_IOCTL_SET_INTR,
17: (LPVOID)&ioctl_info, sizeof(IOCTL_INFO), NULL, 0 ,NULL, NULL);
18: }
19: CloseHandle(pMcuCtl->hGio);
20: pMcuCtl->hGio = NULL;
21: }
22:
23: if(pMcuCtl->hDetectThread) {
24: SetEvent(pMcuCtl->hDeinitEvt);
25: WaitForSingleObject(pMcuCtl->hDetectThread, 10000);
26: CloseHandle(pMcuCtl->hDetectThread);
27: pMcuCtl->hDetectThread = NULL;
28: }
29:
30: if(pMcuCtl->hDeinitEvt) {
31: CloseHandle(pMcuCtl->hDeinitEvt);
32: pMcuCtl->hDeinitEvt = NULL;
33: }
34:
35: if(pMcuCtl->hGioEvt) {
36: CloseHandle(pMcuCtl->hGioEvt);
37: pMcuCtl->hGioEvt = NULL;
38: }
39:
40: if(pMcuCtl->hPowerNotify) {
41: StopPowerNotifications(pMcuCtl->hPowerNotify);
42: pMcuCtl->hPowerNotify = NULL;
43: }
44:
45: if(pMcuCtl->hMsgQ) {
46: CloseHandle(pMcuCtl->hMsgQ);
47: pMcuCtl->hMsgQ = NULL;
48: }
49: LocalFree(pMcuCtl);
50: pMcuCtl = NULL;
51: setevent (greadkeyevent [1]);/* notify the thread that calls the READ function. The driver has been disabled */
52: closehandle (greadkeyevent [0]);/* close related events */
53: CloseHandle(gReadKeyEvent[1]);
54: return TRUE;
55: }
The last step is xxx_read, because we use the interrupt method here, iocontrol is not suitable for use in the interrupt mode, and iocontrol is more suitable in the round-robin mode. Here in the xxx_read function, we use pbuf to pass the value to the lpbuffer in the readfile file of the API layer. Here we provide reference for the functions xxx_read and readfile.
Winbaseapi
Bool
Winapi
Readfile (
Handle hfile,
Lpvoid lpbuffer, //-> xxx_read:LpvoidPbuf,
DWORD nnumberofbytestoread,
Lpdword lpnumberofbytesread,
Lpoverlapped
);
DWORD xxx_Read( DWORD dwData, LPVOID pBuf, DWORD dwLen);
The following code is provided.
1: extern "C" DWORD MCL_Read(DWORD dwData,
2: LPVOID pBuf,
3: DWORD Len)
4: {
5: DWORD ret;
6: unsigned char *pReadBuffer = NULL;
7: if ((pBuf == NULL) || (Len <= 0))
8: return 0;
9: pReadBuffer = (unsigned char *)MapPtrToProcess(pBuf, GetCallerProcess());
10: *pReadBuffer = NULL;
11:
12: ret = WaitForMultipleObjects(2, gReadKeyEvent, FALSE, INFINITE);
13: if (ret == WAIT_OBJECT_0)
14: {
15: ResetEvent(gReadKeyEvent[0]);
16: * preadbuffer = sendout [0];/* press the key */
17: return 1;
18: }
19: else if(ret == (WAIT_OBJECT_0 + 1))
20: {
21: ResetEvent(gReadKeyEvent[1]);
22: * preadbuffer = sendout [1];/* disable the driver */
23: return 1;
24: }
25: return(0);
26: }
Corrected a bug in the Code provided by wiince interrupt stream implementation driver and App. preadbuffer = (unsigned char *) mapptrtoprocess (pbuf, getcallerprocess (); here, the mapptrtoprocess function needs to be forcibly converted, because it is described as lpvoid on msdn, And the preadbuffer we use here is unsigned char *.
Here, we also need to wait for the number of greadkeyevent events we created to determine the location of the interrupted execution and handle the event accordingly. So far, the development of the driver layer is complete, the corresponding variables and struct definitions can be defined according to your own needs. Here I only list one structure framework. You can fill in the blank question. This is also in line with the characteristics of Wince development, development by filling in the blanks.
Application layer development:
The development of the application layer is much more flexible than that of the driver layer. Here we take MFC as an example. In fact, whether it is MFC or Win32 API, the main idea is to create a thread during initialization, similar to the relationship between xxx_detectthread and xxx_init in the driver layer, here I define a readkey1thread function as the entry to read events. The specific implementation is as follows:
1: DWORD CMCUReadDlg::ReadKey1Thread(LPVOID lparam)
2: {
3: BYTE status;
4: DWORD actlen;
5: CString strCount;
6: CMCUReadDlg *pDlg = (CMCUReadDlg*)lparam;
7:/* Get the dialog box pointer */
8: CStatic *pCountStatic = (CStatic*)pDlg->GetDlgItem(IDC_NewShow);
9:/* get the text box pointer of the displayed value */
10: while(TRUE)
11: {
12: if (hStr == INVALID_HANDLE_VALUE)
13: break;/* the driver is not enabled, and the thread is exited */
14: if (ReadFile(hStr, &status, 1, &actlen, NULL) == TRUE)
15: {
16: key1count ++;/* counter count */
17: strCount.Format(_T("%d,0x%.2x"), Key1Count,status);
18: pcountstatic-> setwindowtext (strcount);/* display */
19: }
20: else
21: break;/* readfile () execution error */
22: }
23: return 1;
24: }
Here, we should pay attention to the while processing. While, we use readfile for pairing and union at the driver layer to read information from the driver layer and obtain data using variables, implementation is very simple.
After this function is completed, the thread is created in the AP initialization part. The program is very simple:
1: hReadKey1Thread = CreateThread(0, 0, ReadKey1Thread, this, 0, &IDThread);
2:
3: if (hReadKey1Thread == NULL)
4: {
5: CloseHandle(hStr);
6: hStr = INVALID_HANDLE_VALUE;
7: CloseHandle(hReadKey1Thread);
8: return FALSE;
9: }
I will sort out the entire development process from the driver layer to the AP layer for interrupt driver development under wince, hoping to help you.