/* --------------------------------------------------------------------------- * dio.c - v0.1 (c) 2007 Micro-key bv * --------------------------------------------------------------------------- * Micro-key bv * Industrieweg 28, 9804 TG Noordhorn * Postbus 92, 9800 AB Zuidhorn * The Netherlands * Tel: +31 594 503020 * Fax: +31 594 505825 * Email: support@microkey.nl * Web: www.microkey.nl * --------------------------------------------------------------------------- * Description: Digital inputs/outputs interface. * --------------------------------------------------------------------------- * Version(s): 0.1, 28-11-2007, fvds. * Creation. * --------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------- * System include files * --------------------------------------------------------------------------- */ /* Compiler includes */ #include #include /* Hardware Includes */ #include "LPC23xx.h" #include "types.h" /* FreeRTOS includes */ #include "FreeRTOS.h" #include "Task.h" #include "queue.h" /* --------------------------------------------------------------------------- * Application include files * --------------------------------------------------------------------------- */ #include "dio.h" #include "dioISR.h" #include "armVIC.h" #include "ElecStatusCache.h" /* --------------------------------------------------------------------------- * Local constant and macro definitions * --------------------------------------------------------------------------- */ #define PORT0_BASE_ADDR FIO_BASE_ADDR #define PORT1_BASE_ADDR (FIO_BASE_ADDR + 0x20) #define PORT2_BASE_ADDR (FIO_BASE_ADDR + 0x40) #define PORT3_BASE_ADDR (FIO_BASE_ADDR + 0x60) #define PORT4_BASE_ADDR (FIO_BASE_ADDR + 0x80) #define DIR_OFFSET (0x00) #define MASK_OFFSET (0x10) #define PIN_OFFSET (0x14) #define SET_OFFSET (0x18) #define CLR_OFFSET (0x1C) // \MARK NEW PINSETTINGS FOR TESTER (2) #if (PINSET_TESTER == 2) #define DIO_MUX_DIR FIO3DIR #define DIO_MUX_SET FIO3SET #define DIO_MUX_CLR FIO3CLR #define DIN_MUX_BIT BIT(25) #define DIN_MUX_SET (DIO_MUX_SET = DIN_MUX_BIT) #define DIN_MUX_CLR (DIO_MUX_CLR = DIN_MUX_BIT) #define DOUT_MUX_BIT BIT(26) #define DOUT_MUX_SET (DIO_MUX_SET = DOUT_MUX_BIT) #define DOUT_MUX_CLR (DIO_MUX_CLR = DOUT_MUX_BIT) #endif #define DISPATCH_TASK_PRIO (tskIDLE_PRIORITY + 5) #define DISPATCH_QUEUE_SIZE 10 #define Int_queue_time 3 /* In Miliseconds */ /* --------------------------------------------------------------------------- * Global variable definitions * --------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------- * Local variable definitions * --------------------------------------------------------------------------- */ portTickType dechatterStopTime[maxDI_Channels]; /* Contains tickcount Value, where*/ /* dechattering timeout occours */ UINT8 dechatterLockFlag[maxDI_Channels] = /* Channel Lock Flag */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; UINT32 dechatterTime[maxDI_Channels] = /* TimeDelay until dechatter timeout*/ { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 }; // \MARK NEW PINSETTINGS FOR TESTER (2) #if (PINSET_TESTER == 2) t_output outputPins[maxDO_Channels] = { { PORT4_BASE_ADDR, BIT(8) }, // DOUT0 { PORT4_BASE_ADDR, BIT(9) }, // DOUT1 { PORT4_BASE_ADDR, BIT(10) }, // DOUT2 { PORT4_BASE_ADDR, BIT(11) }, // DOUT3 { PORT4_BASE_ADDR, BIT(12) }, // DOUT4 { PORT4_BASE_ADDR, BIT(13) }, // DOUT5 { PORT4_BASE_ADDR, BIT(14) }, // DOUT6 { PORT4_BASE_ADDR, BIT(15) } // DOUT7 }; #else t_output outputPins[maxDO_Channels] = { { PORT1_BASE_ADDR, BIT(0) }, // DOUT0 { PORT1_BASE_ADDR, BIT(1) }, // DOUT1 { PORT1_BASE_ADDR, BIT(4) }, // DOUT2 { PORT1_BASE_ADDR, BIT(8) }, // DOUT3 { PORT1_BASE_ADDR, BIT(9) }, // DOUT4 { PORT1_BASE_ADDR, BIT(10) }, // DOUT5 { PORT1_BASE_ADDR, BIT(14) }, // DOUT6 { PORT1_BASE_ADDR, BIT(15) } // DOUT7 }; #endif t_input inputPins[maxDI_Channels] = { { PORT0_BASE_ADDR, BIT(15), NULL }, // DIN0 { PORT0_BASE_ADDR, BIT(16), NULL }, // DIN1 { PORT0_BASE_ADDR, BIT(17), NULL }, // DIN2 { PORT0_BASE_ADDR, BIT(18), NULL }, // DIN3 { PORT0_BASE_ADDR, BIT(24), NULL }, // DIN4 { PORT0_BASE_ADDR, BIT(25), NULL }, // DIN5 { PORT0_BASE_ADDR, BIT(26), NULL }, // DIN6 { PORT0_BASE_ADDR, BIT(27), NULL }, // DIN7 { PORT0_BASE_ADDR, BIT(28), NULL }, // DIN8 { PORT0_BASE_ADDR, BIT(29), NULL }, // DIN9 { PORT0_BASE_ADDR, BIT(30), NULL } // DIN10 }; BOOLEAN dioActive = FALSE; xQueueHandle dispatchedIsrQueue= 0; /* --------------------------------------------------------------------------- * Local function definitions * --------------------------------------------------------------------------- */ static void setMaskDirRegisters(void); static void enableChannelInterrupt(UINT8 channel); static void disableChannelInterrupt(UINT8 channel); static void isrDispatchTask(void *pvParameters); static BOOLEAN hasTimeoutPast( UINT32 endTick ); static void startDechatterInput (UINT8 ChannelNumber, t_di_mode Edge); static void endDechatterInput (void); static void callAllCallbacks(t_callbackListItem *pCallbackList, t_di_mode detectedEdge); void dioInit(void) { SCS |= (1UL<<0); /* GPIOM in SCS to fast IO */ setMaskDirRegisters(); /* Init channel Registers */ dioActive= TRUE; /* Set global dioAcitve */ /* Create queue and Task for ISR-displatching */ dispatchedIsrQueue = xQueueCreate( DISPATCH_QUEUE_SIZE, sizeof(t_IsrDispatchQueueItem)); xTaskCreate(isrDispatchTask, ( signed portCHAR * ) "dioIsrDispatcher", configMINIMAL_STACK_SIZE, NULL, DISPATCH_TASK_PRIO, NULL); /* Enable GPIO interrupt */ portENTER_CRITICAL(); { VICIntSelect &= ~(VIC_CHAN_TO_MASK(VIC_CHAN_NUM_EINT3)); VICIntEnClr = VIC_CHAN_TO_MASK(VIC_CHAN_NUM_EINT3); VICVectAddr17 = (void *)gpioISR; VICVectCntl7 = 0x01; VICIntEnable = VIC_CHAN_TO_MASK(VIC_CHAN_NUM_EINT3); } portEXIT_CRITICAL(); // \MARK NEW PINSETTINGS FOR TESTER (2) #if (PINSET_TESTER == 2) /* Init both MUX lines as Outputs */ DIO_MUX_DIR |= (DIN_MUX_BIT | DOUT_MUX_BIT); #endif } void dio_inMuxEn (BOOLEAN mode) { // \MARK NEW PINSETTINGS FOR TESTER (2) #if (PINSET_TESTER == 2) if (mode == TRUE) { DIN_MUX_SET; } else { DIN_MUX_CLR; } #endif } void dio_outMuxEn (BOOLEAN mode) { // \MARK NEW PINSETTINGS FOR TESTER (2) #if (PINSET_TESTER == 2) if (mode == TRUE) { DOUT_MUX_SET; } else { DOUT_MUX_CLR; } #endif } BOOLEAN dio_inMuxRB (void) { // \MARK NEW PINSETTINGS FOR TESTER (2) #if (PINSET_TESTER == 2) if (DIO_MUX_SET & DIN_MUX_BIT) { /* MUX of digital input is switched to Extension Board */ return (TRUE); } else { /* MUX of digital input is switched to Main Board */ return (FALSE); } #endif } BOOLEAN dio_outMuxRB (void) { // \MARK NEW PINSETTINGS FOR TESTER (2) #if (PINSET_TESTER == 2) if (DIO_MUX_SET & DOUT_MUX_BIT) { /* MUX of digital input is switched to Extension Board */ return (TRUE); } else { /* MUX of digital input is switched to Main Board */ return (FALSE); } #endif } RESULT dioRegisterCallback (t_dio_callbackfunc pFunc, UINT8 channel, t_di_mode mode) { RESULT result = OK; disableChannelInterrupt(channel); // Create item t_callbackListItem *newCallbackListItem = (t_callbackListItem *) pvPortMalloc(sizeof(t_callbackListItem)); if (newCallbackListItem == NULL) { result = ERROR; } else { if (inputPins[channel].pCallbackList == NULL) { enableChannelInterrupt(channel); } // Fill list item newCallbackListItem->pCallback = pFunc; newCallbackListItem->mode = mode; // Add to front of list newCallbackListItem->next = inputPins[channel].pCallbackList; inputPins[channel].pCallbackList = newCallbackListItem; } enableChannelInterrupt(channel); return result; } /** \brief Remove register callback function for digital input * \retval OK Callback was removed succesfully * \retval ERROR Failed to remove Callback-function from channel */ RESULT dioRemoveCallback(t_dio_callbackfunc pFunc, UINT8 channel) { RESULT result = OK; disableChannelInterrupt(channel); // Check if channel has callback list if (inputPins[channel].pCallbackList == NULL) { result = ERROR; } else { BOOLEAN callbackFound = FALSE; t_callbackListItem * previousItem= NULL; t_callbackListItem * currentItem = inputPins[channel].pCallbackList; // Find callbackfunc while ((!callbackFound) && (currentItem != NULL)) { callbackFound = (currentItem->pCallback == pFunc); if (!callbackFound) { previousItem = currentItem; currentItem = currentItem->next; } } if (callbackFound) { if (previousItem == NULL) { // Remove first item from list inputPins[channel].pCallbackList = currentItem->next; } else { // Remove list item previousItem->next = currentItem->next; } vPortFree(currentItem); // Release resources of listitem } else { result = FALSE; } } if (inputPins[channel].pCallbackList != NULL) { enableChannelInterrupt(channel); } return result; } /** \brief Read digital input.*/ BOOLEAN dioRead(UINT8 device, /**< 0 = Self, 1..32 = Remote device */ UINT8 channel /**< 0..10 = valid, 11..255 = future use */ ) { BOOLEAN Result; volatile UINT32 *gpioRegister; if (device == 0) { gpioRegister = (UINT32 *)(inputPins[channel].portBaseAddr + PIN_OFFSET); // device is now ignored, future implementation // Get result if (*gpioRegister & inputPins[channel].pinMask) { Result = FALSE; } else { Result = TRUE; } } else { Result = bpecDioRead( device, channel ); } return Result; } /** \brief Readback digital outputs.*/ BOOLEAN dioReadBack(UINT8 device, /**< 0 = Self, 1..32 = Remote device */ UINT8 channel /**< 0..7 = valid, 8..255 = future use */ ) { BOOLEAN Result; volatile UINT32 *gpioRegister; if (device == 0) { gpioRegister = (UINT32 *)(outputPins[channel].portBaseAddr + PIN_OFFSET); // device is now ignored, future implementation // Get result // NOTE: Input signal is inverted if (*gpioRegister & outputPins[channel].pinMask) { Result = TRUE; } else { Result = FALSE; } } else { Result = bpecDioReadBack( device, channel ); } return Result; } /** \brief Write digital outputs.*/ void dioWrite(UINT8 device, /**< 0 = Self, 1..32 = Remote device */ UINT8 channel, /**< 0..7 = valid, 8..255 = future use */ BOOLEAN data) { volatile UINT32 *gpioRegister; if (device == 0) { if (data == TRUE) { gpioRegister = (UINT32 *)(outputPins[channel].portBaseAddr + SET_OFFSET); *gpioRegister = outputPins[channel].pinMask; } else { gpioRegister = (UINT32 *)(outputPins[channel].portBaseAddr + CLR_OFFSET); *gpioRegister = outputPins[channel].pinMask; } } else { bpecWriteDioValue(device, channel, data); } } void setMaskDirRegisters() { int i; volatile UINT32 *gpioRegister; for (i=0; i< maxDO_Channels; i++) { // Clear bit in mask Register gpioRegister = (UINT32 *)(outputPins[i].portBaseAddr + MASK_OFFSET); *gpioRegister &= ~(outputPins[i].pinMask); // Set direction bit to output gpioRegister = (UINT32 *)(outputPins[i].portBaseAddr + DIR_OFFSET); *gpioRegister |= outputPins[i].pinMask; } for (i=0; i< maxDI_Channels; i++) { // Clear bit in maskRegister gpioRegister = (UINT32 *)(inputPins[i].portBaseAddr + MASK_OFFSET); *gpioRegister &= ~(inputPins[i].pinMask); // Set direction bit to input gpioRegister = (UINT32 *)(inputPins[i].portBaseAddr + DIR_OFFSET); *gpioRegister &= ~(inputPins[i].pinMask); // Reset callback functions inputPins[i].pCallbackList = NULL; } } void enableChannelInterrupt(UINT8 channel) { if (inputPins[channel].portBaseAddr == PORT0_BASE_ADDR) { IO0_INT_CLR = inputPins[channel].pinMask; IO0_INT_EN_R |= inputPins[channel].pinMask; IO0_INT_EN_F |= inputPins[channel].pinMask; } else { IO2_INT_CLR = inputPins[channel].pinMask; IO2_INT_EN_R |= inputPins[channel].pinMask; IO2_INT_EN_F |= inputPins[channel].pinMask; } } void disableChannelInterrupt(UINT8 channel) { if (inputPins[channel].portBaseAddr == PORT0_BASE_ADDR) { IO0_INT_EN_R &= ~BIT(inputPins[channel].pinMask); IO0_INT_EN_F &= ~BIT(inputPins[channel].pinMask); } else { IO2_INT_EN_R &= ~BIT(inputPins[channel].pinMask); IO2_INT_EN_F &= ~BIT(inputPins[channel].pinMask); } } void isrDispatchTask(void *pvParameters) { t_IsrDispatchQueueItem dispatchItem; UINT32 channelcnt; do /* As long as active */ { /* If an Input Interrupt occures, enter if Statement */ if (xQueueReceive(dispatchedIsrQueue, &dispatchItem, Int_queue_time)) { /* Check for each single Channel, which one is active */ for (channelcnt=0; channelcnt < maxDI_Channels; channelcnt++) { /* Go further if CallBack exists for corresponding Channel */ if (inputPins[channelcnt].pCallbackList != NULL) { /* Check if this bit is active */ int portIndex = (inputPins[channelcnt].portBaseAddr == PORT0_BASE_ADDR ? 0 : 1); /* Check if Interrupt occured from a RISING EDGE */ if (dispatchItem.riseInterrupts[ portIndex ] & inputPins[channelcnt].pinMask) { /* Rising Edge Interrupt detected */ if (dechatterTime[channelcnt] == 0) { /* If dechatterTime is set to ZERO */ /* call Callback directly */ callAllCallbacks( inputPins[channelcnt].pCallbackList, RISING_EDGE); } else /* If there is a dechattering Time */ { /* Start dechattering the Input */ startDechatterInput(channelcnt, RISING_EDGE); } } /* Check if Interrupt occured from a FALLING EDGE */ if (dispatchItem.fallInterrupts[ portIndex ] & inputPins[channelcnt].pinMask) { /* Falling Edge Interrupt detected */ if (dechatterTime[channelcnt] == 0) { /* If dechatterTime is set to ZERO */ /* call Callback directly */ callAllCallbacks( inputPins[channelcnt].pCallbackList, FALLING_EDGE); } else /* if there is a dechattering Time */ { /* Start dechattering the Input */ startDechatterInput(channelcnt, FALLING_EDGE); } } } /* EndIf CallBack exists Check */ } /* End of FOR Loop */ } /* End Check for occuring Interrupt */ endDechatterInput(); /* Call dechatter monitor Function */ } while (dioActive == TRUE); } void startDechatterInput(UINT8 ChannelNumber, t_di_mode Edge) { /* If corresponding Channel is currently not monitored */ if (dechatterLockFlag[ChannelNumber] == 0) { /* Set dechatter Timer */ dechatterStopTime[ChannelNumber] = xTaskGetTickCount() + dechatterTime[ChannelNumber]; /* Handle type of detected Edge in channel Lock Flag */ switch (Edge) { case RISING_EDGE: dechatterLockFlag[ChannelNumber] = 1; break; case FALLING_EDGE: dechatterLockFlag[ChannelNumber] = 2; break; // case BOTH_EDGES: default: break; } } /* If come here, an already monitored Channel causes a new Interrupt * (which means, within the Timer a new Edge occured) * This is a chatter! * Reload the Timer! */ else { dechatterStopTime[ChannelNumber] = xTaskGetTickCount() + dechatterTime[ChannelNumber]; } } void endDechatterInput(void) { UINT32 channelcnt; /* Check every single Channel */ for (channelcnt = 0; channelcnt < maxDI_Channels; channelcnt++) { /* If Timeout for corresponding channel occured */ if (hasTimeoutPast( dechatterStopTime[channelcnt]) ) { /* Switch corresponding to the former detected Edge */ switch (dechatterLockFlag[channelcnt]) { case 1: /* a Rising Edge was dechattered */ /* Is Input still High? If Yes, channel is dechattered */ if (dioRead(0, channelcnt) == TRUE) { /* Call Callback Functions */ callAllCallbacks(inputPins[channelcnt].pCallbackList, RISING_EDGE); } dechatterLockFlag[channelcnt] = 0; /* release Lock Flag */ break; case 2: /* a Falling Edge was dechattered */ /* Is Input still Low? If Yes, channel is dechattered */ if (dioRead(0, channelcnt) == FALSE) { /* Call Callback Functions */ callAllCallbacks(inputPins[channelcnt].pCallbackList, FALLING_EDGE); } dechatterLockFlag[channelcnt] = 0; break; } } } } void callAllCallbacks(t_callbackListItem *pCallbackList, t_di_mode detectedEdge) { while (pCallbackList != NULL) { if ( (pCallbackList->mode == detectedEdge) || (pCallbackList->mode == BOTH_EDGES)) { // Call the callback pCallbackList->pCallback(); } // Check next item pCallbackList = pCallbackList->next; } } void dioSetDechatterTime(UINT8 channel, UINT32 time) { dechatterTime[channel] = time; } BOOLEAN hasTimeoutPast( UINT32 endTick ) { UINT32 nowTick = xTaskGetTickCount(); if (nowTick >= endTick) { if ((nowTick - endTick) > 0x0000FFFF) { // the endTick has gone through 0 point, nowTick is at end of range return FALSE; } else { // nowTick passed endTick. return TRUE; } } else { if ((endTick - nowTick) > 0x0000FFFF) { // the endTick was at end of range, nowTick has gone through 0 point return TRUE; } else { // nowTick still has to pass endTick return FALSE; } } }