static __interrupt void prvUSCI_A1_ISR( void ) { signed char cChar; portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; while( ( UCA1IFG & UCRXIFG ) != 0 ) { /* Get the character from the UART and post it on the queue of Rxed characters. */ cChar = UCA1RXBUF; xQueueSendFromISR( xRxedChars, &cChar, &xHigherPriorityTaskWoken ); } /* If there is a Tx interrupt pending and the tx interrupts are enabled. */ if( ( UCA1IFG & UCTXIFG ) != 0 ) { /* The previous character has been transmitted. See if there are any further characters waiting transmission. */ if( xQueueReceiveFromISR( xCharsForTx, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE ) { /* There was another character queued - transmit it now. */ UCA1TXBUF = cChar; } else { /* There were no other characters to transmit - disable the Tx interrupt. */ UCA1IE &= ~UCTXIE; } } __bic_SR_register_on_exit( SCG1 + SCG0 + OSCOFF + CPUOFF ); /* If writing to a queue caused a task to unblock, and the unblocked task has a priority equal to or above the task that this interrupt interrupted, then lHigherPriorityTaskWoken will have been set to pdTRUE internally within xQueuesendFromISR(), and portEND_SWITCHING_ISR() will ensure that this interrupt returns directly to the higher priority unblocked task. THIS MUST BE THE LAST THING DONE IN THE ISR. */ portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); }
/* Serial port ISR. This can cause a context switch so is not defined as a standard ISR using the __irq keyword. Instead a wrapper function is defined within serialISR.s79 which in turn calls this function. See the port documentation on the FreeRTOS.org website for more information. */ __arm void vSerialISR( void ) { unsigned short usStatus; signed char cChar; portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; /* What caused the interrupt? */ usStatus = UART_FlagStatus( UART0 ); if( usStatus & UART_TxHalfEmpty ) { /* The interrupt was caused by the THR becoming empty. Are there any more characters to transmit? */ if( xQueueReceiveFromISR( xCharsForTx, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE ) { /* A character was retrieved from the queue so can be sent to the THR now. */ UART0->TxBUFR = cChar; } else { /* Queue empty, nothing to send so turn off the Tx interrupt. */ serINTERRUPT_OFF(); } } if( usStatus & UART_RxBufFull ) { /* The interrupt was caused by a character being received. Grab the character from the RHR and place it in the queue of received characters. */ cChar = UART0->RxBUFR; xQueueSendFromISR( xRxedChars, &cChar, &xHigherPriorityTaskWoken ); } /* If a task was woken by either a character being received or a character being transmitted then we may need to switch to another task. */ portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); /* End the interrupt in the EIC. */ portCLEAR_EIC(); }
/* Serial port ISR. This can cause a context switch so is not defined as a standard ISR using the __irq keyword. Instead a wrapper function is defined within serialISR.s79 which in turn calls this function. See the port documentation on the FreeRTOS.org website for more information. */ __arm void vSerialISR( void ) { unsigned long ulStatus; signed char cChar; portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; /* What caused the interrupt? */ ulStatus = serCOM0->US_CSR &= serCOM0->US_IMR; if( ulStatus & AT91C_US_TXRDY ) { /* The interrupt was caused by the THR becoming empty. Are there any more characters to transmit? */ if( xQueueReceiveFromISR( xCharsForTx, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE ) { /* A character was retrieved from the queue so can be sent to the THR now. */ serCOM0->US_THR = cChar; } else { /* Queue empty, nothing to send so turn off the Tx interrupt. */ vInterruptOff(); } } if( ulStatus & AT91C_US_RXRDY ) { /* The interrupt was caused by a character being received. Grab the character from the RHR and place it in the queue or received characters. */ cChar = serCOM0->US_RHR; xQueueSendFromISR( xRxedChars, &cChar, &xHigherPriorityTaskWoken ); } /* If a task was woken by either a character being received or a character being transmitted then we may need to switch to another task. */ portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); /* End the interrupt in the AIC. */ AT91C_BASE_AIC->AIC_EOICR = 0; }
void vSoftwareInterruptHandler( void ) { portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; static unsigned long ulReceivedNumber; /* The strings are declared static const to ensure they are not allocated to the interrupt service routine stack, and exist even when the interrupt service routine is not executing. */ static const char *pcStrings[] = { "String 0\n", "String 1\n", "String 2\n", "String 3\n" }; /* Loop until the queue is empty. */ while( xQueueReceiveFromISR( xIntegerQueue, &ulReceivedNumber, &xHigherPriorityTaskWoken ) != errQUEUE_EMPTY ) { /* Truncate the received value to the last two bits (values 0 to 3 inc.), then send the string that corresponds to the truncated value to the other queue. */ ulReceivedNumber &= 0x03; xQueueSendToBackFromISR( xStringQueue, &pcStrings[ ulReceivedNumber ], &xHigherPriorityTaskWoken ); } /* Clear the software interrupt bit using the interrupt controllers Clear Pending register. */ mainCLEAR_INTERRUPT(); /* xHigherPriorityTaskWoken was initialised to pdFALSE. It will have then been set to pdTRUE only if reading from or writing to a queue caused a task of equal or greater priority than the currently executing task to leave the Blocked state. When this is the case a context switch should be performed. In all other cases a context switch is not necessary. NOTE: The syntax for forcing a context switch within an ISR varies between FreeRTOS ports. The portEND_SWITCHING_ISR() macro is provided as part of the Cortex M3 port layer for this purpose. taskYIELD() must never be called from an ISR! */ portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); }
static void prvReceiveFromQueueInSetFromISR( void ) { QueueSetMemberHandle_t xActivatedQueue; uint32_t ulReceived; /* See if any of the queues in the set contain data. */ xActivatedQueue = xQueueSelectFromSetFromISR( xQueueSet ); if( xActivatedQueue != NULL ) { /* Reading from the queue for test purposes only. */ if( xQueueReceiveFromISR( xActivatedQueue, &ulReceived, NULL ) != pdPASS ) { /* Data should have been available as the handle was returned from xQueueSelectFromSetFromISR(). */ xQueueSetTasksStatus = pdFAIL; } /* Ensure the value received was the value expected. */ prvCheckReceivedValue( ulReceived ); } }
/* * UART Tx interrupt service routine. */ __interrupt void UART0_TxISR( void ) { signed char cChar; signed portBASE_TYPE xTaskWoken = pdFALSE; /* The previous character has been transmitted. See if there are any further characters waiting transmission. */ if( xQueueReceiveFromISR(xCharsForTx, &cChar, &xTaskWoken) == pdTRUE ) { /* There was another character queued - transmit it now. */ TDR0 = cChar; } else { /* There were no other characters to transmit. */ sTHREEmpty = pdTRUE; /* Disable transmit interrupts */ SSR0_TIE = 0; } }
bool CAN_rx (can_t can, can_msg_t *pCanMsg, uint32_t timeout_ms) { bool ok = false; if (CAN_VALID(can) && pCanMsg) { if (taskSCHEDULER_RUNNING == xTaskGetSchedulerState()) { ok = xQueueReceive(g_can_rx_qs[CAN_INDEX(can)], pCanMsg, OS_MS(timeout_ms)); } else { uint64_t msg_timeout = sys_get_uptime_ms() + timeout_ms; while (! (ok = xQueueReceiveFromISR(g_can_rx_qs[CAN_INDEX(can)], pCanMsg, NULL))) { if (sys_get_uptime_ms() > msg_timeout) { break; } } } } return ok; }
/* * This code has been adapted from the ST Microelectronics CDC * Example, which is covered under the V2 Liberty License: * http://www.st.com/software_license_agreement_liberty_v2 */ static void usb_handle_transfer(void) { portBASE_TYPE hpta = false; xQueueHandle queue = serial_get_tx_queue(usb_state.serial); uint8_t *buff = usb_state.USB_Tx_Buffer; size_t len = 0; for (; len < VIRTUAL_COM_PORT_DATA_SIZE; ++len) if (!xQueueReceiveFromISR(queue, buff + len, &hpta)) break; /* Check if we actually have something to send */ if (len) { UserToPMABufferCopy(usb_state.USB_Tx_Buffer, ENDP1_TXADDR, len); SetEPTxCount(ENDP1, len); SetEPTxValid(ENDP1); } portEND_SWITCHING_ISR(hpta); }
// DMA interrupt handler. It is called each time a DMA block is finished processing. static void dma_isr_handler(void) { portBASE_TYPE task_awoken = pdFALSE; if (i2s_dma_is_eof_interrupt()) { dma_descriptor_t *descr = i2s_dma_get_eof_descriptor(); if (xQueueIsQueueFullFromISR(dma_queue)) { // List of empty blocks is full. Sender don't send data fast enough. int dummy; underrun_counter++; // Discard top of the queue xQueueReceiveFromISR(dma_queue, &dummy, &task_awoken); } // Push the processed buffer to the queue so sender can refill it. xQueueSendFromISR(dma_queue, (void*)(&descr->buf_ptr), &task_awoken); } i2s_dma_clear_interrupt(); portEND_SWITCHING_ISR(task_awoken); }
__arm void vSerialISR( void ) { signed char cChar; portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; do { if( UART0->MIS & UART_IT_Transmit ) { /* The interrupt was caused by the THR becoming empty. Are there any more characters to transmit? */ if( xQueueReceiveFromISR( xCharsForTx, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE ) { /* A character was retrieved from the queue so can be sent to the THR now. */ UART0->DR = cChar; } else { xQueueEmpty = pdTRUE; } UART_ClearITPendingBit( UART0, UART_IT_Transmit ); } if( UART0->MIS & UART_IT_Receive ) { /* The interrupt was caused by a character being received. Grab the character from the RHR and place it in the queue of received characters. */ cChar = UART0->DR; xQueueSendFromISR( xRxedChars, &cChar, &xHigherPriorityTaskWoken ); UART_ClearITPendingBit( UART0, UART_IT_Receive ); } } while( UART0->MIS ); /* If a task was woken by either a character being received or a character being transmitted then we may need to switch to another task. */ portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); }
static portBASE_TYPE xComPortISR( xComPort * const pxPort ) { unsigned short usStatusRegister; char cChar; portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; /* NOTE: THIS IS NOT AN EFFICIENT ISR AS IT IS DESIGNED SOLELY TO TEST THE SCHEDULER FUNCTIONALITY. REAL APPLICATIONS SHOULD NOT USE THIS FUNCTION. */ usStatusRegister = portINPUT_WORD( pxPort->usStatusReg ); if( usStatusRegister & serRX_READY ) { cChar = ( char ) portINPUT_WORD( pxPort->usRxReg ); xQueueSendFromISR( pxPort->xRxedChars, &cChar, &xHigherPriorityTaskWoken ); /* Also release the semaphore - this does nothing interesting and is just a test. */ xSemaphoreGiveFromISR( pxPort->xTestSem, &xHigherPriorityTaskWoken ); } else if( pxPort->sTxInterruptOn && ( usStatusRegister & serTX_EMPTY ) ) { if( xQueueReceiveFromISR( pxPort->xCharsForTx, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE ) { portOUTPUT_WORD( pxPort->usTxReg, ( unsigned short ) cChar ); } else { /* Queue empty, nothing to send */ vInterruptOff( pxPort, serTX_HOLD_EMPTY_INT ); } } serRESET_PIC( pxPort->usIRQVector ); /* If posting to the queue woke a task that was blocked on the queue we may want to switch to the woken task - depending on its priority relative to the task interrupted by this ISR. */ return xHigherPriorityTaskWoken; }
/* This is too slow to use */ unsigned char* BPL_AllocMessageBufferFromISR(void) { unsigned char * pBuffer = NULL; signed portBASE_TYPE HigherPriorityTaskWoken; // params are: queue handle, ptr to the msg buffer, ticks to wait if( pdTRUE != xQueueReceiveFromISR(QueueHandles[FREE_QINDEX], &pBuffer, &HigherPriorityTaskWoken )) { PrintString2("@ Alloc Buf frm Isr", CR); SetBufferPoolFailureBit(); } if ( HigherPriorityTaskWoken == pdTRUE ) { portYIELD(); } return pBuffer; }
void USART1_IRQHandler(void) { static signed portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; if ( USART_GetITStatus(USART1, USART_IT_RXNE) ) { if ( xSemaphoreRx != NULL ) { if (xQueueReceiveFromISR( xSemaphoreRx, NULL, NULL)) { // The first rx buffer is available xSemaphoreGiveFromISR( xSemaphoreRx, &xHigherPriorityTaskWoken ); rb_putc(&rx_buf1, USART1->DR); } else { // The first rx buffer is locked by the reader task // so we have to use the second buffer which is of course available rb_putc(&rx_buf2, USART1->DR); uart_stats.rx_buff1_busy++; } } uart_stats.rx_bytes++; } }
/// Get a mail from a queue /// \param[in] queue_id mail queue ID obtained with \ref osMailCreate. /// \param[in] millisec timeout value or 0 in case of no time-out /// \return event that contains mail information or error code. /// \note MUST REMAIN UNCHANGED: \b osMailGet shall be consistent in every CMSIS-RTOS. osEvent osMailGet (osMailQId queue_id, uint32_t millisec) { portBASE_TYPE taskWoken; portTickType ticks; osEvent event; event.def.mail_id = queue_id; if (queue_id == NULL) { event.status = osErrorParameter; return event; } taskWoken = pdFALSE; ticks = millisec_to_ticks(millisec); if (inHandlerMode()) { if (xQueueReceiveFromISR(queue_id->handle, &event.value.p, &taskWoken) == pdTRUE) { /* We have mail */ event.status = osEventMail; } else { event.status = osOK; } portEND_SWITCHING_ISR(taskWoken); } else { if (xQueueReceive(queue_id->handle, &event.value.p, ticks) == pdTRUE) { /* We have mail */ event.status = osEventMail; } else { event.status = (ticks == 0) ? osOK : osEventTimeout; } } return event; }
//--------------------------------------------------------------------------------------------- void debug_port_interrupt_handler(void) { char c; if (USART_GetITStatus(USART1, USART_IT_TXE) == SET) { if (xQueueReceiveFromISR(txchars_queue, &c, NULL) == pdPASS) { USART_SendData(USART1, c); } else { USART_ITConfig(USART1, USART_IT_TXE, DISABLE); } } if (USART_GetITStatus(USART1, USART_IT_RXNE)) { c = USART_ReceiveData(USART1); cmd_echo(c); } }
/* * UART Tx interrupt service routine. */ void vTxISR( void ) __interrupt[ UART1TX_VECTOR ] { signed char cChar; portBASE_TYPE xTaskWoken = pdFALSE; /* The previous character has been transmitted. See if there are any further characters waiting transmission. */ if( xQueueReceiveFromISR( xCharsForTx, &cChar, &xTaskWoken ) == pdTRUE ) { /* There was another character queued - transmit it now. */ U1TXBUF = cChar; } else { /* There were no other characters to transmit. */ sTHREEmpty = pdTRUE; } /* Make sure any low power mode bits are clear before leaving the ISR. */ __bic_SR_register_on_exit( SCG1 + SCG0 + OSCOFF + CPUOFF ); }
void vCOM_1_Tx_ISR( void ) { /* This can cause a context switch so this macro must be the first line in the function. */ portENTER_SWITCHING_ISR(); /* As this is a switching ISR the local variables must be declared as static. */ static char cTxByte; static signed portBASE_TYPE xTaskWokenByTx; /* This variable is static so must be explicitly reinitialised each time the function executes. */ xTaskWokenByTx = pdFALSE; /* The interrupt was caused by the THR becoming empty. Are there any more characters to transmit? Note whether or not the Tx interrupt has woken a task. */ if( xQueueReceiveFromISR( xCharsForTx, &cTxByte, &xTaskWokenByTx ) == pdTRUE ) { /* A character was retrieved from the queue so can be sent to the THR now. */ TDR1 = cTxByte; /* Clear the interrupt. */ SSR1 &= ~serTX_INTERRUPT; } else { /* Queue empty, nothing to send so turn off the Tx interrupt. */ serTX_INTERRUPT_OFF(); } /* This must be the last line in the function. We pass cTaskWokenByTx so a context switch will occur if the Tx'ed character woke a task that has a priority higher than the task we interrupted. */ portEXIT_SWITCHING_ISR( xTaskWokenByTx ); }
void USART1_IRQHandler(void) { /* if(USART_GetITStatus(USART1, USART_IT_RXNE)) { u8 RxData = (u8)USART_ReceiveData(USART1); test[testlen++] = RxData; } DisplayString(0, 0, (u8*)test); */ portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; portCHAR cChar; if( USART_GetITStatus( USART1, USART_IT_TXE ) == SET ) { /* The interrupt was caused by the THR becoming empty. Are there any more characters to transmit? */ if( xQueueReceiveFromISR( xCharsForTx, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE ) { /* A character was retrieved from the queue so can be sent to the THR now. */ USART_SendData( USART1, cChar ); } else { USART_ITConfig( USART1, USART_IT_TXE, DISABLE ); } } if( USART_GetITStatus( USART1, USART_IT_RXNE ) == SET ) { cChar = USART_ReceiveData( USART1 ); xQueueSendFromISR( xRxedChars, &cChar, &xHigherPriorityTaskWoken ); } portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); }
/* Tx interrupt handler. This is called from the asm file wrapper. */ void vUARTTxISRHandler( void ) { char cChar; portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; /* Are there any more characters queue to transmit? */ if( xQueueReceiveFromISR( xCharsForTx, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE ) { /* Send the next character. */ UD0TX = cChar; } else { /* The UART is no longer active. */ ulTxInProgress = pdFALSE; } /* If reading a character from the Rx queue caused a task to unblock, and the unblocked task has a priority higher than the currently running task, then xHigherPriorityTaskWoken will have been set to true and a context switch should occur now. */ portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); }
static void prvTxHandler( void ) { signed char cChar; portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; /* The interrupt was caused by the transmit fifo having space for at least one character. Are there any more characters to transmit? */ if( xQueueReceiveFromISR( xCharsForTx, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE ) { /* A character was retrieved from the queue so can be sent to the uart now. */ uart1->tx_data = cChar; } else { /* Queue empty, nothing to send so turn off the Tx interrupt. */ uart1->tx_mask = 0; } /* If an event caused a task to unblock then we call "Yield from ISR" to ensure that the unblocked task is the task that executes when the interrupt completes if the unblocked task has a priority higher than the interrupted task. */ portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); }
void USART1_IRQHandler( void ) { long xHigherPriorityTaskWoken = pdFALSE; char cChar; if( USART_GetITStatus( USART1, USART_IT_TXE ) == SET ) { /* The interrupt was caused by the THR becoming empty. Are there any more characters to transmit? */ if( xQueueReceiveFromISR( xCharsForTx[ 0 ], &cChar, &xHigherPriorityTaskWoken ) ) { /* A character was retrieved from the buffer so can be sent to the THR now. */ USART_SendData( USART1, cChar ); } else { USART_ITConfig( USART1, USART_IT_TXE, DISABLE ); } } if( USART_GetITStatus( USART1, USART_IT_RXNE ) == SET ) { cChar = USART_ReceiveData( USART1 ); xQueueSendFromISR( xRxedChars[ 0 ], &cChar, &xHigherPriorityTaskWoken ); } portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); }
void usart1_isr(void) { long xHigherPriorityTaskWoken = pdFALSE; char cChar; // ----- transmission complete: if (usart_get_flag(USART1, USART_SR_TC) == true) { gpio_clear(GPIOA, GPIO12); // clear RTS gpio_clear(GPIOA, GPIO_USART1_TX); // clear DI USART_SR(USART1) &= ~USART_SR_TC; // reset flag TC usart_disable_tx_interrupt(USART1); } if (usart_get_flag(USART1, USART_SR_TXE) == true) { /* The interrupt was caused by the THR becoming empty. Are there any more characters to transmit? */ if (xQueueReceiveFromISR(xCharsForTx[0], &cChar, &xHigherPriorityTaskWoken)) { /* A character was retrieved from the buffer so can be sent to the THR now. */ gpio_set(GPIOA, GPIO12); // set RTS usart_send(USART1, (uint8_t) cChar); } else { // gpio_clear(GPIOA, GPIO12); // clear RTS // usart_disable_tx_interrupt(USART1); // USART_SR(USART1) &= ~USART_SR_TXE; // reset flag TXE // USART_CR1(USART1) |= USART_CR1_TCIE; } } if (usart_get_flag(USART1, USART_SR_RXNE) == true) { cChar = (char) usart_recv(USART1); xQueueSendFromISR(xRxedChars[0], &cChar, &xHigherPriorityTaskWoken); } portEND_SWITCHING_ISR(xHigherPriorityTaskWoken); }
/** @brief Serial ISR This ISR handles both UART0 and 1. The ISR handles data that wouldn't fit in the ISR by writing the data written to the queue. The received data is put onto the queue. Error interrupts are not handled, the interrupts are just cleared. @note Use the wrapper function instead so the context gets saved. */ void vUART_ISR_Handler( void ) { signed char cChar; portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; /* What caused the interrupt? */ switch( U0IIR & serINTERRUPT_SOURCE_MASK ) { case serSOURCE_ERROR : /* Not handling this, but clear the interrupt. */ cChar = U0LSR; break; case serSOURCE_THRE : /* The THRE is empty. If there is another character in the Tx queue, send it now. */ if( xQueueReceiveFromISR( xCharsForTx[0], &cChar, &xHigherPriorityTaskWoken ) == pdTRUE ) { U0THR = cChar; } else { /* There are no further characters queued to send so we can indicate that the THRE is available. */ lTHREEmpty[0] = pdTRUE; } break; case serSOURCE_RX_TIMEOUT : case serSOURCE_RX : /* A character was received. Place it in the queue of received characters. */ cChar = U0RBR; xQueueSendFromISR( xRxedChars[0], &cChar, &xHigherPriorityTaskWoken ); break; default : /* There is nothing to do, leave the ISR. */ break; } switch( U1IIR & serINTERRUPT_SOURCE_MASK ) { case serSOURCE_ERROR : /* Not handling this, but clear the interrupt. */ cChar = U1LSR; break; case serSOURCE_THRE : /* The THRE is empty. If there is another character in the Tx queue, send it now. */ if( xQueueReceiveFromISR( xCharsForTx[1], &cChar, &xHigherPriorityTaskWoken ) == pdTRUE ) { U1THR = cChar; } else { /* There are no further characters queued to send so we can indicate that the THRE is available. */ lTHREEmpty[1] = pdTRUE; } break; case serSOURCE_RX_TIMEOUT : case serSOURCE_RX : /* A character was received. Place it in the queue of received characters. */ cChar = U1RBR; xQueueSendFromISR( xRxedChars[1], &cChar, &xHigherPriorityTaskWoken ); break; default : /* There is nothing to do, leave the ISR. */ break; } if( xHigherPriorityTaskWoken ) { portYIELD_FROM_ISR(); } /* Clear the ISR in the VIC. */ VICVectAddr = serCLEAR_VIC_INTERRUPT; }
/** * Interrupt service routine * Basically handles the entire protocol */ void __attribute__((noinline)) pca9665_dsr(portBASE_TYPE * task_woken) { static int handle, len; static uint8_t state; static uint8_t dest; /* Loop through number of devices */ for (handle = 0; handle < pca9665_device_count; handle++) { /* Check for interrupt flag in device status register */ if (!(pca9665_read_reg(handle, I2CCON) & CON_SI)) continue; /* We have an interrupt, read the status register */ state = pca9665_read_reg(handle, I2CSTA); /* The I2C driver is one _big_ state-machine */ driver_debug(DEBUG_I2C, "I2C ISR %u %x\n\r", handle, state); switch (state) { /** * MASTER IRQ's */ /* START: is the first ISR that appears for outgoing frames */ case STA_M_REPEATED_START_SENDT: case STA_M_START_SENDT: /* Mark as busy, so start flag is not sent from task context while transmission is active */ device[handle].is_busy = 1; /* If this is the beginning of a new frame, dequeue */ if (device[handle].tx.frame == NULL && device[handle].rx.frame == NULL) { /* Try do dequeue element, if it fails, stop transmission */ xQueueReceiveFromISR(device[handle].tx.queue, &device[handle].tx.frame, task_woken); if (device[handle].tx.frame == NULL) { pca9665_try_tx_from_isr(handle, task_woken); break; } /* If TX len > 0, go for master transmit */ if (device[handle].tx.frame->len) { device[handle].mode = DEVICE_MODE_M_T; device[handle].tx.next_byte = 0; /* If TX len == 0 and RX len > 0, go for master receive */ } else if (device[handle].tx.frame->len_rx) { device[handle].mode = DEVICE_MODE_M_R; device[handle].rx.frame = device[handle].tx.frame; device[handle].tx.frame = NULL; device[handle].rx.frame->len = device[handle].rx.frame->len_rx; device[handle].rx.next_byte = 0; /* Well, this should not happen */ } else { csp_buffer_free_isr(device[handle].tx.frame); device[handle].tx.frame = NULL; pca9665_try_tx_from_isr(handle, task_woken); break; } } /* If mode is master receiver then set the read-bit in the address field */ if (device[handle].mode == DEVICE_MODE_M_R) { dest = (device[handle].rx.frame->dest << 1) | 0x01; device[handle].rx.next_byte = 0; /* Do first part of frame here */ if (device[handle].rx.frame->len > PCA9665_MAX_BUF) { pca9665_write_reg(handle, I2CCOUNT, PCA9665_MAX_BUF); } else { pca9665_write_reg(handle, I2CCOUNT, device[handle].rx.frame->len | 0x80); } pca9665_write_data(handle, &dest, 1); } else { dest = device[handle].tx.frame->dest << 1; device[handle].tx.next_byte = 0; /* Do first part of frame here */ if (device[handle].tx.frame->len + 1 > PCA9665_MAX_BUF) { pca9665_write_reg(handle, I2CCOUNT, PCA9665_MAX_BUF); pca9665_write_data(handle, &dest, 1); pca9665_write_data(handle, &device[handle].tx.frame->data[device[handle].tx.next_byte], PCA9665_MAX_BUF - 1); device[handle].tx.next_byte += PCA9665_MAX_BUF - 1; } else { pca9665_write_reg(handle, I2CCOUNT, device[handle].tx.frame->len + 1); pca9665_write_data(handle, &dest, 1); pca9665_write_data(handle, &device[handle].tx.frame->data[device[handle].tx.next_byte], device[handle].tx.frame->len); device[handle].tx.next_byte += device[handle].tx.frame->len; } } /* Let the hardware continue */ pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA); break; /* WRITE ACK: A node is ready to be written to */ case STA_M_SLAW_SENDT_ACKED: case STA_M_DATA_SENDT_ACKED: /* Safety first */ if (device[handle].tx.frame == NULL) goto isr_error; /* Calculate remaining length */ len = device[handle].tx.frame->len - device[handle].tx.next_byte; /* Transmit next chunk */ if (len > 0) { if (len > PCA9665_MAX_BUF) { pca9665_write_reg(handle, I2CCOUNT, PCA9665_MAX_BUF); pca9665_write_data(handle, &device[handle].tx.frame->data[device[handle].tx.next_byte], PCA9665_MAX_BUF); device[handle].tx.next_byte += PCA9665_MAX_BUF; } else { pca9665_write_reg(handle, I2CCOUNT, len); pca9665_write_data(handle, &device[handle].tx.frame->data[device[handle].tx.next_byte], len); device[handle].tx.next_byte += len; } pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA); break; /* Or, Change from master transmit, to master read if wanted */ } else if (device[handle].tx.frame->len_rx) { device[handle].mode = DEVICE_MODE_M_R; device[handle].rx.frame = device[handle].tx.frame; device[handle].tx.frame = NULL; device[handle].rx.frame->len = device[handle].rx.frame->len_rx; /* We need to send a repeated start now! */ pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA | CON_STA); break; /* Or, We are done */ } else { csp_buffer_free_isr(device[handle].tx.frame); device[handle].tx.frame = NULL; pca9665_try_tx_from_isr(handle, task_woken); } break; /* WRITE ERROR: A write has failed */ case STA_M_SLAW_SENDT_NACKED: case STA_M_DATA_SENDT_LAST_NACKED: if (device[handle].tx.frame != NULL) { driver_debug(DEBUG_I2C, "I2C SLA+W NACK: 0x%02"PRIx8"\n\r", device[handle].tx.frame->dest); csp_buffer_free_isr(device[handle].tx.frame); device[handle].tx.frame = NULL; } pca9665_try_tx_from_isr(handle, task_woken); break; /* ARBITRATION LOST: Start condition failed */ case STA_M_ARBITRATION_LOST: /* Restart transmission by resetting next_byte and preserving tx_frame pointer */ device[handle].tx.next_byte = 0; pca9665_try_tx_from_isr(handle, task_woken); break; /* READ ACK: A node is ready to be read from */ case STA_M_SLAR_SENT_ACKED: case STA_M_DATA_RECEIVED_ACKED: case STA_M_DATA_RECEIVED_NACKED: /* Safety first */ if (device[handle].rx.frame == NULL) goto isr_error; if (device[handle].rx.queue == NULL) goto isr_error; pca9665_read_data_to_buffer(handle); int remaining = device[handle].rx.frame->len - device[handle].rx.next_byte; driver_debug(DEBUG_I2C, "RX: Remaining %u\r\n", remaining); /* If no more to receive */ if (remaining == 0) { if (xQueueSendToBackFromISR(device[handle].rx.queue, &device[handle].rx.frame, task_woken) == pdFALSE) { driver_debug(DEBUG_I2C, "I2C rx queue full - freeing\n\r"); csp_buffer_free_isr(device[handle].rx.frame); } device[handle].rx.frame = NULL; pca9665_try_tx_from_isr(handle, task_woken); break; } /* If more than a full PCA9665 buffer remains */ if (remaining > PCA9665_MAX_BUF) { pca9665_write_reg(handle, I2CCOUNT, PCA9665_MAX_BUF); /* Otherwise, this is the last bit, set NACK on final slave read */ } else { pca9665_write_reg(handle, I2CCOUNT, remaining | 0x80); } pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA); break; /* READ ERROR: A read has failed */ case STA_M_SLAR_SENT_NACKED: /* Safety first */ if (device[handle].rx.frame == NULL) goto isr_error; driver_debug(DEBUG_I2C, "I2C SLA+R nacked\n\r"); csp_buffer_free_isr(device[handle].rx.frame); device[handle].rx.frame = NULL; /* Start up again */ pca9665_try_tx_from_isr(handle, task_woken); break; /** * SLAVE RECEIVER BUFFERED MODE */ /* START: Lost the arbitration and is addressed as a slave receiver */ case STA_S_ARB_LOST_SLAW_RECEIVED: case STA_S_ARB_LOST_GC_RECEIVED: /* Preserve TX frame active, so the START flag will be re-set when the * reception is completed */ /* Deliberate Fallthrough */ /* START: Addressed as a slave receiver */ case STA_S_SLAW_RECEIVED_ACKED: case STA_S_GC_RECEIVED: /* Check if RX frame was started */ if (device[handle].rx.frame != NULL) goto isr_error; /* Allocate new frame */ device[handle].rx.frame = csp_buffer_get_isr(I2C_MTU); if (device[handle].rx.frame == NULL) goto isr_error; device[handle].is_busy = 1; device[handle].rx.next_byte = 0; device[handle].rx.frame->len = 0; device[handle].rx.frame->dest = device[handle].slave_addr; pca9665_write_reg(handle, I2CCOUNT, PCA9665_MAX_BUF); pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA); break; /* READ: Data received. */ case STA_S_DATA_RECEIVED_SLA_ACKED: case STA_S_DATA_RECEIVED_GC_ACKED: /* Safety first */ if (device[handle].rx.frame == NULL) goto isr_error; /* Receive data, if any */ pca9665_read_data_to_buffer(handle); /* Limit incoming bytes */ pca9665_write_reg(handle, I2CCOUNT, (device[handle].rx.next_byte + PCA9665_MAX_BUF > I2C_MTU) ? (I2C_MTU - device[handle].rx.next_byte) | 0x80 : PCA9665_MAX_BUF); pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA); break; /* STOP or NACK: No more data to receive */ case STA_S_STOP_REP_RECEIVED: case STA_S_DATA_RECEIVED_SLA_NACKED: case STA_S_DATA_RECEIVED_GC_NACKED: /* Safety first */ if (device[handle].rx.frame == NULL) goto isr_error; /* Receive data, if any */ pca9665_read_data_to_buffer(handle); /* Queue up frame */ device[handle].rx.frame->len = device[handle].rx.next_byte; if (device[handle].rx.queue != NULL) { if (xQueueSendToBackFromISR(device[handle].rx.queue, &device[handle].rx.frame, task_woken) == pdFALSE) { driver_debug(DEBUG_I2C, "I2C RX queue full\n\r"); csp_buffer_free_isr(device[handle].rx.frame); } } else if (device[handle].callback != NULL) { device[handle].callback(device[handle].rx.frame, task_woken); } else { csp_buffer_free_isr(device[handle].rx.frame); } /* The frame has been freed now */ device[handle].rx.frame = NULL; /* Set back to master mode */ pca9665_try_tx_from_isr(handle, task_woken); break; /** * Other IRQ's, typically indicates a hardware or protcol error * The IDLE status is considered an error if asserted at the same time as the Serial Interrupt flag */ case STA_IDLE: default: isr_error: /* Soft reset the device */ driver_debug(DEBUG_I2C, "I2C ERR 0x%02X\n\r", state); pca9665_init_registers(handle); /* Clean up RX */ if (device[handle].rx.frame != NULL) { csp_buffer_free_isr(device[handle].rx.frame); device[handle].rx.frame = NULL; } /* Clean up TX */ if (device[handle].tx.frame != NULL) { csp_buffer_free_isr(device[handle].tx.frame); device[handle].tx.frame = NULL; } /* Start up again */ pca9665_try_tx_from_isr(handle, task_woken); break; } } /**< END Switch/Case */ }
//This is run in interrupt context and apart from initialization and destruction, this is the only code //touching the host (=spihost[x]) variable. The rest of the data arrives in queues. That is why there are //no muxes in this code. static void IRAM_ATTR spi_intr(void *arg) { BaseType_t r; BaseType_t do_yield = pdFALSE; spi_slave_transaction_t *trans = NULL; spi_slave_t *host = (spi_slave_t *)arg; #ifdef DEBUG_SLAVE dumpregs(host->hw); if (host->dmadesc_rx) dumpll(&host->dmadesc_rx[0]); #endif //Ignore all but the trans_done int. if (!host->hw->slave.trans_done) return; if (host->cur_trans) { if (host->dma_chan == 0 && host->cur_trans->rx_buffer) { //Copy result out uint32_t *data = host->cur_trans->rx_buffer; for (int x = 0; x < host->cur_trans->length; x += 32) { uint32_t word; int len = host->cur_trans->length - x; if (len > 32) len = 32; word = host->hw->data_buf[(x / 32)]; memcpy(&data[x / 32], &word, (len + 7) / 8); } } else if (host->dma_chan != 0 && host->cur_trans->rx_buffer) { int i; //In case CS goes high too soon, the transfer is aborted while the DMA channel still thinks it's going. This //leads to issues later on, so in that case we need to reset the channel. The state can be detected because //the DMA system doesn't give back the offending descriptor; the owner is still set to DMA. for (i = 0; host->dmadesc_rx[i].eof == 0 && host->dmadesc_rx[i].owner == 0; i++) ; if (host->dmadesc_rx[i].owner) { spicommon_dmaworkaround_req_reset(host->dma_chan, spi_slave_restart_after_dmareset, host); } } if (host->cfg.post_trans_cb) host->cfg.post_trans_cb(host->cur_trans); //Okay, transaction is done. //Return transaction descriptor. xQueueSendFromISR(host->ret_queue, &host->cur_trans, &do_yield); host->cur_trans = NULL; } if (host->dma_chan != 0) { spicommon_dmaworkaround_idle(host->dma_chan); if (spicommon_dmaworkaround_reset_in_progress()) { //We need to wait for the reset to complete. Disable int (will be re-enabled on reset callback) and exit isr. esp_intr_disable(host->intr); if (do_yield) portYIELD_FROM_ISR(); return; } } //Grab next transaction r = xQueueReceiveFromISR(host->trans_queue, &trans, &do_yield); if (!r) { //No packet waiting. Disable interrupt. esp_intr_disable(host->intr); } else { //We have a transaction. Send it. host->hw->slave.trans_done = 0; //clear int bit host->cur_trans = trans; if (host->dma_chan != 0) { spicommon_dmaworkaround_transfer_active(host->dma_chan); host->hw->dma_conf.val |= SPI_OUT_RST | SPI_IN_RST | SPI_AHBM_RST | SPI_AHBM_FIFO_RST; host->hw->dma_out_link.start = 0; host->hw->dma_in_link.start = 0; host->hw->dma_conf.val &= ~(SPI_OUT_RST | SPI_IN_RST | SPI_AHBM_RST | SPI_AHBM_FIFO_RST); host->hw->dma_conf.out_data_burst_en = 0; host->hw->dma_conf.indscr_burst_en = 0; host->hw->dma_conf.outdscr_burst_en = 0; //Fill DMA descriptors if (trans->rx_buffer) { host->hw->user.usr_miso_highpart = 0; spicommon_setup_dma_desc_links(host->dmadesc_rx, ((trans->length + 7) / 8), trans->rx_buffer, true); host->hw->dma_in_link.addr = (int)(&host->dmadesc_rx[0]) & 0xFFFFF; host->hw->dma_in_link.start = 1; } if (trans->tx_buffer) { spicommon_setup_dma_desc_links(host->dmadesc_tx, (trans->length + 7) / 8, trans->tx_buffer, false); host->hw->user.usr_mosi_highpart = 0; host->hw->dma_out_link.addr = (int)(&host->dmadesc_tx[0]) & 0xFFFFF; host->hw->dma_out_link.start = 1; } host->hw->slave.sync_reset = 1; host->hw->slave.sync_reset = 0; } else { //No DMA. Turn off SPI and copy data to transmit buffers. host->hw->cmd.usr = 0; host->hw->slave.sync_reset = 1; host->hw->slave.sync_reset = 0; host->hw->user.usr_miso_highpart = 0; host->hw->user.usr_mosi_highpart = 0; if (trans->tx_buffer) { const uint32_t *data = host->cur_trans->tx_buffer; for (int x = 0; x < trans->length; x += 32) { uint32_t word; memcpy(&word, &data[x / 32], 4); host->hw->data_buf[(x / 32)] = word; } } } host->hw->slv_rd_bit.slv_rdata_bit = 0; host->hw->slv_wrbuf_dlen.bit_len = trans->length - 1; host->hw->slv_rdbuf_dlen.bit_len = trans->length - 1; host->hw->mosi_dlen.usr_mosi_dbitlen = trans->length - 1; host->hw->miso_dlen.usr_miso_dbitlen = trans->length - 1; host->hw->user.usr_mosi = (trans->tx_buffer == NULL) ? 0 : 1; host->hw->user.usr_miso = (trans->rx_buffer == NULL) ? 0 : 1; //Kick off transfer host->hw->cmd.usr = 1; if (host->cfg.post_setup_cb) host->cfg.post_setup_cb(trans); } if (do_yield) portYIELD_FROM_ISR(); }
void vI2C_ISR_Handler( void ) { /* Holds the current transmission state. */ static I2C_STATE eCurrentState = eSentStart; static long lMessageIndex = -i2cBUFFER_ADDRESS_BYTES; /* There are two address bytes to send prior to the data. */ portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; long lBytesLeft; /* The action taken for this interrupt depends on our current state. */ switch( eCurrentState ) { case eSentStart : /* We sent a start bit, if it was successful we can go on to send the slave address. */ if( ( I2C_I2STAT == i2cSTATUS_START_TXED ) || ( I2C_I2STAT == i2cSTATUS_REP_START_TXED ) ) { /* Send the slave address. */ I2C_I2DAT = pxCurrentMessage->ucSlaveAddress; if( pxCurrentMessage->ucSlaveAddress & i2cREAD ) { /* We are then going to read bytes back from the slave. */ eCurrentState = eSentAddressForRead; /* Initialise the buffer index so the first byte goes into the first buffer position. */ lMessageIndex = 0; } else { /* We are then going to write some data to the slave. */ eCurrentState = eSentAddressForWrite; /* When writing bytes we first have to send the two byte buffer address so lMessageIndex is set negative, when it reaches 0 it is time to send the actual data. */ lMessageIndex = -i2cBUFFER_ADDRESS_BYTES; } } else { /* Could not send the start bit so give up. */ i2cEND_TRANSMISSION( pdFAIL ); } I2C_I2CONCLR = i2cSTA_BIT; break; case eSentAddressForWrite : /* We sent the address of the slave we are going to write to. If this was acknowledged we can go on to send the data. */ if( I2C_I2STAT == i2cSTATUS_TX_ADDR_ACKED ) { /* Start the first byte transmitting which is the first byte of the buffer address to which the data will be sent. */ I2C_I2DAT = pxCurrentMessage->ucBufferAddressHighByte; eCurrentState = eSentData; } else { /* Address was not acknowledged so give up. */ i2cEND_TRANSMISSION( pdFAIL ); } break; case eSentAddressForRead : /* We sent the address of the slave we are going to read from. If this was acknowledged we can go on to read the data. */ if( I2C_I2STAT == i2cSTATUS_RX_ADDR_ACKED ) { eCurrentState = eReceiveData; if( pxCurrentMessage->lMessageLength > i2cJUST_ONE_BYTE_TO_RX ) { /* Don't ack the last byte of the message. */ I2C_I2CONSET = i2cAA_BIT; } } else { /* Something unexpected happened - give up. */ i2cEND_TRANSMISSION( pdFAIL ); } break; case eReceiveData : /* We have just received a byte from the slave. */ if( ( I2C_I2STAT == i2cSTATUS_DATA_RXED ) || ( I2C_I2STAT == i2cSTATUS_LAST_BYTE_RXED ) ) { /* Buffer the byte just received then increment the index so it points to the next free space. */ pxCurrentMessage->pucBuffer[ lMessageIndex ] = I2C_I2DAT; lMessageIndex++; /* How many more bytes are we expecting to receive? */ lBytesLeft = pxCurrentMessage->lMessageLength - lMessageIndex; if( lBytesLeft == ( unsigned long ) 0 ) { /* This was the last byte in the message. */ i2cEND_TRANSMISSION( pdPASS ); /* If xMessageCompleteSemaphore is not null then there is a task waiting for this message to complete and we must 'give' the semaphore so the task is woken.*/ if( pxCurrentMessage->xMessageCompleteSemaphore ) { xSemaphoreGiveFromISR( pxCurrentMessage->xMessageCompleteSemaphore, &xHigherPriorityTaskWoken ); } /* Are there any other messages to transact? */ if( xQueueReceiveFromISR( xMessagesForTx, &pxCurrentMessage, &xHigherPriorityTaskWoken ) == pdTRUE ) { /* Start the next message - which was retrieved from the queue. */ I2C_I2CONSET = i2cSTA_BIT; } else { /* No more messages were found to be waiting for transaction so the bus is free. */ ulBusFree = ( unsigned long ) pdTRUE; } } else { /* There are more bytes to receive but don't ack the last byte. */ if( lBytesLeft <= i2cJUST_ONE_BYTE_TO_RX ) { I2C_I2CONCLR = i2cAA_BIT; } } } else { /* Something unexpected happened - give up. */ i2cEND_TRANSMISSION( pdFAIL ); } break; case eSentData : /* We sent a data byte, if successful send the next byte in the message. */ if( I2C_I2STAT == i2cSTATUS_DATA_TXED ) { /* Index to the next byte to send. */ lMessageIndex++; if( lMessageIndex < 0 ) { /* lMessage index is still negative so we have so far only sent the first byte of the buffer address. Send the second byte now, then initialise the buffer index to zero so the next byte sent comes from the actual data buffer. */ I2C_I2DAT = pxCurrentMessage->ucBufferAddressLowByte; } else if( lMessageIndex < pxCurrentMessage->lMessageLength ) { /* Simply send the next byte in the tx buffer. */ I2C_I2DAT = pxCurrentMessage->pucBuffer[ lMessageIndex ]; } else { /* No more bytes in this message to be send. Finished sending message - send a stop bit. */ i2cEND_TRANSMISSION( pdPASS ); /* If xMessageCompleteSemaphore is not null then there is a task waiting for this message to be sent and the semaphore must be 'given' to wake the task. */ if( pxCurrentMessage->xMessageCompleteSemaphore ) { xSemaphoreGiveFromISR( pxCurrentMessage->xMessageCompleteSemaphore, &xHigherPriorityTaskWoken ); } /* Are there any other messages to transact? */ if( xQueueReceiveFromISR( xMessagesForTx, &pxCurrentMessage, &xHigherPriorityTaskWoken ) == pdTRUE ) { /* Start the next message from the Tx queue. */ I2C_I2CONSET = i2cSTA_BIT; } else { /* No more message were queues for transaction so the bus is free. */ ulBusFree = ( unsigned long ) pdTRUE; } } } else { /* Something unexpected happened, give up. */ i2cEND_TRANSMISSION( pdFAIL ); } break; default : /* Should never get here. */ eCurrentState = eSentStart; break; } /* Clear the interrupt. */ I2C_I2CONCLR = i2cSI_BIT; VICVectAddr = i2cCLEAR_VIC_INTERRUPT; if( xHigherPriorityTaskWoken ) { portYIELD_FROM_ISR(); } }
//This is run in interrupt context and apart from initialization and destruction, this is the only code //touching the host (=spihost[x]) variable. The rest of the data arrives in queues. That is why there are //no muxes in this code. static void IRAM_ATTR spi_intr(void *arg) { int i; BaseType_t r; BaseType_t do_yield=pdFALSE; spi_trans_priv *trans_buf=NULL; spi_transaction_t *trans=NULL; spi_host_t *host=(spi_host_t*)arg; //Ignore all but the trans_done int. if (!host->hw->slave.trans_done) return; /*------------ deal with the in-flight transaction -----------------*/ if (host->cur_cs != NO_CS) { spi_transaction_t *cur_trans = host->cur_trans_buf.trans; //Okay, transaction is done. if (host->cur_trans_buf.buffer_to_rcv && host->dma_chan == 0 ) { //Need to copy from SPI regs to result buffer. for (int x=0; x < cur_trans->rxlength; x+=32) { //Do a memcpy to get around possible alignment issues in rx_buffer uint32_t word=host->hw->data_buf[x/32]; int len=cur_trans->rxlength-x; if (len>32) len=32; memcpy(&host->cur_trans_buf.buffer_to_rcv[x/32], &word, (len+7)/8); } } //Call post-transaction callback, if any if (host->device[host->cur_cs]->cfg.post_cb) host->device[host->cur_cs]->cfg.post_cb(cur_trans); //Return transaction descriptor. xQueueSendFromISR(host->device[host->cur_cs]->ret_queue, &host->cur_trans_buf, &do_yield); host->cur_cs = NO_CS; } //Tell common code DMA workaround that our DMA channel is idle. If needed, the code will do a DMA reset. if (host->dma_chan) spicommon_dmaworkaround_idle(host->dma_chan); /*------------ new transaction starts here ------------------*/ //ToDo: This is a stupidly simple low-cs-first priority scheme. Make this configurable somehow. - JD for (i=0; i<NO_CS; i++) { if (host->device[i]) { r=xQueueReceiveFromISR(host->device[i]->trans_queue, &host->cur_trans_buf, &do_yield); trans_buf = &host->cur_trans_buf; //Stop looking if we have a transaction to send. if (r) break; } } if (i==NO_CS) { //No packet waiting. Disable interrupt. esp_intr_disable(host->intr); #ifdef CONFIG_PM_ENABLE //Release APB frequency lock esp_pm_lock_release(host->pm_lock); #endif } else { host->hw->slave.trans_done=0; //clear int bit //We have a transaction. Send it. spi_device_t *dev=host->device[i]; trans = trans_buf->trans; host->cur_cs=i; //We should be done with the transmission. assert(host->hw->cmd.usr == 0); //Reconfigure according to device settings, but only if we change CSses. if (i!=host->prev_cs) { int apbclk=APB_CLK_FREQ; int effclk=dev->clk_cfg.eff_clk; spi_set_clock(host->hw, dev->clk_cfg.reg); //Configure bit order host->hw->ctrl.rd_bit_order=(dev->cfg.flags & SPI_DEVICE_RXBIT_LSBFIRST)?1:0; host->hw->ctrl.wr_bit_order=(dev->cfg.flags & SPI_DEVICE_TXBIT_LSBFIRST)?1:0; //Configure polarity //SPI iface needs to be configured for a delay in some cases. int nodelay=0; int extra_dummy=0; if (host->no_gpio_matrix) { if (effclk >= apbclk/2) { nodelay=1; } } else { if (effclk >= apbclk/2) { nodelay=1; extra_dummy=1; //Note: This only works on half-duplex connections. spi_bus_add_device checks for this. } else if (effclk >= apbclk/4) { nodelay=1; } } if (dev->cfg.mode==0) { host->hw->pin.ck_idle_edge=0; host->hw->user.ck_out_edge=0; host->hw->ctrl2.miso_delay_mode=nodelay?0:2; } else if (dev->cfg.mode==1) { host->hw->pin.ck_idle_edge=0; host->hw->user.ck_out_edge=1; host->hw->ctrl2.miso_delay_mode=nodelay?0:1; } else if (dev->cfg.mode==2) { host->hw->pin.ck_idle_edge=1; host->hw->user.ck_out_edge=1; host->hw->ctrl2.miso_delay_mode=nodelay?0:1; } else if (dev->cfg.mode==3) { host->hw->pin.ck_idle_edge=1; host->hw->user.ck_out_edge=0; host->hw->ctrl2.miso_delay_mode=nodelay?0:2; } //configure dummy bits host->hw->user.usr_dummy=(dev->cfg.dummy_bits+extra_dummy)?1:0; host->hw->user1.usr_dummy_cyclelen=dev->cfg.dummy_bits+extra_dummy-1; //Configure misc stuff host->hw->user.doutdin=(dev->cfg.flags & SPI_DEVICE_HALFDUPLEX)?0:1; host->hw->user.sio=(dev->cfg.flags & SPI_DEVICE_3WIRE)?1:0; host->hw->ctrl2.setup_time=dev->cfg.cs_ena_pretrans-1; host->hw->user.cs_setup=dev->cfg.cs_ena_pretrans?1:0; host->hw->ctrl2.hold_time=dev->cfg.cs_ena_posttrans-1; host->hw->user.cs_hold=(dev->cfg.cs_ena_posttrans)?1:0; //Configure CS pin host->hw->pin.cs0_dis=(i==0)?0:1; host->hw->pin.cs1_dis=(i==1)?0:1; host->hw->pin.cs2_dis=(i==2)?0:1; } host->prev_cs = i; //Reset SPI peripheral host->hw->dma_conf.val |= SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST; host->hw->dma_out_link.start=0; host->hw->dma_in_link.start=0; host->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); host->hw->dma_conf.out_data_burst_en=1; //Set up QIO/DIO if needed host->hw->ctrl.val &= ~(SPI_FREAD_DUAL|SPI_FREAD_QUAD|SPI_FREAD_DIO|SPI_FREAD_QIO); host->hw->user.val &= ~(SPI_FWRITE_DUAL|SPI_FWRITE_QUAD|SPI_FWRITE_DIO|SPI_FWRITE_QIO); if (trans->flags & SPI_TRANS_MODE_DIO) { if (trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR) { host->hw->ctrl.fread_dio=1; host->hw->user.fwrite_dio=1; } else { host->hw->ctrl.fread_dual=1; host->hw->user.fwrite_dual=1; } host->hw->ctrl.fastrd_mode=1; } else if (trans->flags & SPI_TRANS_MODE_QIO) { if (trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR) { host->hw->ctrl.fread_qio=1; host->hw->user.fwrite_qio=1; } else { host->hw->ctrl.fread_quad=1; host->hw->user.fwrite_quad=1; } host->hw->ctrl.fastrd_mode=1; } //Fill DMA descriptors if (trans_buf->buffer_to_rcv) { host->hw->user.usr_miso_highpart=0; if (host->dma_chan == 0) { //No need to setup anything; we'll copy the result out of the work registers directly later. } else { spicommon_dmaworkaround_transfer_active(host->dma_chan); //mark channel as active spicommon_setup_dma_desc_links(host->dmadesc_rx, ((trans->rxlength+7)/8), (uint8_t*)trans_buf->buffer_to_rcv, true); host->hw->dma_in_link.addr=(int)(&host->dmadesc_rx[0]) & 0xFFFFF; host->hw->dma_in_link.start=1; } } else { //DMA temporary workaround: let RX DMA work somehow to avoid the issue in ESP32 v0/v1 silicon if (host->dma_chan != 0 ) { host->hw->dma_in_link.addr=0; host->hw->dma_in_link.start=1; } } if (trans_buf->buffer_to_send) { if (host->dma_chan == 0) { //Need to copy data to registers manually for (int x=0; x < trans->length; x+=32) { //Use memcpy to get around alignment issues for txdata uint32_t word; memcpy(&word, &trans_buf->buffer_to_send[x/32], 4); host->hw->data_buf[(x/32)+8]=word; } host->hw->user.usr_mosi_highpart=1; } else { spicommon_dmaworkaround_transfer_active(host->dma_chan); //mark channel as active spicommon_setup_dma_desc_links(host->dmadesc_tx, (trans->length+7)/8, (uint8_t*)trans_buf->buffer_to_send, false); host->hw->user.usr_mosi_highpart=0; host->hw->dma_out_link.addr=(int)(&host->dmadesc_tx[0]) & 0xFFFFF; host->hw->dma_out_link.start=1; host->hw->user.usr_mosi_highpart=0; } } host->hw->mosi_dlen.usr_mosi_dbitlen=trans->length-1; if ( dev->cfg.flags & SPI_DEVICE_HALFDUPLEX ) { host->hw->miso_dlen.usr_miso_dbitlen=trans->rxlength-1; } else { //rxlength is not used in full-duplex mode host->hw->miso_dlen.usr_miso_dbitlen=trans->length-1; } //Configure bit sizes, load addr and command int cmdlen; if ( trans->flags & SPI_TRANS_VARIABLE_CMD ) { cmdlen = ((spi_transaction_ext_t*)trans)->command_bits; } else { cmdlen = dev->cfg.command_bits; } int addrlen; if ( trans->flags & SPI_TRANS_VARIABLE_ADDR ) { addrlen = ((spi_transaction_ext_t*)trans)->address_bits; } else { addrlen = dev->cfg.address_bits; } host->hw->user1.usr_addr_bitlen=addrlen-1; host->hw->user2.usr_command_bitlen=cmdlen-1; host->hw->user.usr_addr=addrlen?1:0; host->hw->user.usr_command=cmdlen?1:0; // output command will be sent from bit 7 to 0 of command_value, and then bit 15 to 8 of the same register field. uint16_t command = trans->cmd << (16-cmdlen); //shift to MSB host->hw->user2.usr_command_value = (command>>8)|(command<<8); //swap the first and second byte // shift the address to MSB of addr (and maybe slv_wr_status) register. // output address will be sent from MSB to LSB of addr register, then comes the MSB to LSB of slv_wr_status register. if (addrlen>32) { host->hw->addr = trans->addr >> (addrlen- 32); host->hw->slv_wr_status = trans->addr << (64 - addrlen); } else {
int csp_queue_dequeue_isr(csp_queue_handle_t handle, void * buf, CSP_BASE_TYPE * task_woken) { return xQueueReceiveFromISR(handle, buf, (signed CSP_BASE_TYPE *)task_woken); }
signed portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, signed char cOutChar, TickType_t xBlockTime ) { signed portBASE_TYPE xReturn; char cChar; portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; if( xQueueSend( xCharsForTx, &cOutChar, xBlockTime ) == pdPASS ) { xReturn = pdPASS; if( xQueueReceiveFromISR( xCharsForTx, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE ) { /* A character was retrieved from the queue so can be sent to the THR now. */ S_UART_SendData( cChar ); } } else { xReturn = pdFAIL; } return xReturn; }
void USART3_IRQHandler( void ) { portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; char cChar; if( USART_GetITStatus( USART3, USART_IT_TXE ) == SET ) { /* The interrupt was caused by the TX register becoming empty. Are there any more characters to transmit? */ if( xQueueReceiveFromISR( xCharsForTx, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE ) { /* A character was retrieved from the queue so can be sent to the USART now. */ USART_SendData( USART3, cChar ); } else { USART_ITConfig( USART3, USART_IT_TXE, DISABLE ); } } if( USART_GetITStatus( USART3, USART_IT_RXNE ) == SET ) { /* A character has been received on the USART, send it to the Rx handler task. */ cChar = USART_ReceiveData( USART3 ); xQueueSendFromISR( xRxedChars, &cChar, &xHigherPriorityTaskWoken ); } /* If sending or receiving from a queue has caused a task to unblock, and the unblocked task has a priority equal to or higher than the currently running task (the task this ISR interrupted), then xHigherPriorityTaskWoken will have automatically been set to pdTRUE within the queue send or receive function. portEND_SWITCHING_ISR() will then ensure that this ISR returns directly to the higher priority unblocked task. */ portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); }