xNetworkBufferDescriptor_t *pxNetworkBufferGetFromISR( size_t xRequestedSizeBytes ) { xNetworkBufferDescriptor_t *pxReturn = NULL; UBaseType_t uxSavedInterruptStatus; /*_RB_ The current implementation only has a single size memory block, so the requested size parameter is not used (yet). */ ( void ) xRequestedSizeBytes; /* If there is a semaphore available then there is a buffer available, but, as this is called from an interrupt, only take a buffer if there are at least ipINTERRUPT_BUFFER_GET_THRESHOLD buffers remaining. This prevents, to a certain degree at least, a rapidly executing interrupt exhausting buffer and in so doing preventing tasks from continuing. */ if( uxQueueMessagesWaitingFromISR( ( xQueueHandle ) xNetworkBufferSemaphore ) > ipINTERRUPT_BUFFER_GET_THRESHOLD ) { if( xSemaphoreTakeFromISR( xNetworkBufferSemaphore, NULL ) == pdPASS ) { /* Protect the structure as it is accessed from tasks and interrupts. */ uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); { pxReturn = ( xNetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &xFreeBuffersList ); uxListRemove( &( pxReturn->xBufferListItem ) ); } portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); iptraceNETWORK_BUFFER_OBTAINED_FROM_ISR( pxReturn ); } } if( pxReturn == NULL ) { iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER_FROM_ISR(); } return pxReturn; }
/** * @brief Wait until a Semaphore token becomes available * @param semaphore_id semaphore object referenced with \ref osSemaphore. * @param millisec timeout value or 0 in case of no time-out. * @retval number of available tokens, or -1 in case of incorrect parameters. * @note MUST REMAIN UNCHANGED: \b osSemaphoreWait shall be consistent in every CMSIS-RTOS. */ int32_t osSemaphoreWait (osSemaphoreId semaphore_id, uint32_t millisec) { TickType_t ticks; portBASE_TYPE taskWoken = pdFALSE; if (semaphore_id == NULL) { return osErrorParameter; } ticks = 0; if (millisec == osWaitForever) { ticks = portMAX_DELAY; } else if (millisec != 0) { ticks = millisec / portTICK_PERIOD_MS; if (ticks == 0) { ticks = 1; } } if (inHandlerMode()) { if (xSemaphoreTakeFromISR(semaphore_id, &taskWoken) != pdTRUE) { return osErrorOS; } portEND_SWITCHING_ISR(taskWoken); } else if (xSemaphoreTake(semaphore_id, ticks) != pdTRUE) { return osErrorOS; } return osOK; }
/** * * @brief Takes binary semaphore from ISR context. * * @param[in] sema pointer to instance of @p struct pios_semaphore * @param[out] woken pointer to bool which will be set true if a context switch is required * * @returns true on success or false on timeout or failure * */ bool PIOS_Semaphore_Take_FromISR(struct pios_semaphore *sema, bool *woken) { PIOS_Assert(sema != NULL); #if defined(PIOS_INCLUDE_FREERTOS) PIOS_Assert(woken != NULL); signed portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; bool result = xSemaphoreTakeFromISR(sema->sema_handle, &xHigherPriorityTaskWoken) == pdTRUE; *woken = *woken || xHigherPriorityTaskWoken == pdTRUE; return result; #else bool result = true; PIOS_IRQ_Disable(); if (sema->sema_count != 0) --sema->sema_count; else result = false; PIOS_IRQ_Enable(); return result; #endif }
// guts of equistack_Push, with ISR option // exactly the same as equistack_Stage_helper but calls equistack_Push_Unsafe static void* equistack_Push_helper(equistack* S, void* data, bool from_isr) { void *staged_pointer = NULL; bool got_mutex; // take mutex using appropriate function if (from_isr) { got_mutex = xSemaphoreTakeFromISR(S->mutex, NULL); } else { got_mutex = xSemaphoreTake(S->mutex, (TickType_t) EQUISTACK_MUTEX_WAIT_TIME_TICKS); } if (got_mutex) { // actually get staged pointer with normal function staged_pointer = equistack_Push_Unsafe(S, data); // give mutex using appropriate function if (from_isr) { xSemaphoreGiveFromISR(S->mutex, NULL); } else { xSemaphoreGive(S->mutex); } } else { // log error if mutex can't be obtained, and give pointer to previously staged // struct (the one currently at the top index) log_error(ELOC_EQUISTACK_PUT, ECODE_EQUISTACK_MUTEX_TIMEOUT, false); staged_pointer = ((uint8_t*) S->data) + (S->data_size)*((S->top_index + 1) % S->max_size); } return staged_pointer; }
/** * @brief Transmits data as a master to a slave, used in ISR * @param DevAddress: Address for the slave device * @param Data: Pointer to a buffer where data will be stored * @param Size: Size of the amount of data to receive * @retval None */ void I2C2_ReceiveFromISR(uint8_t DevAddress, uint8_t* pBuffer, uint16_t Size) { /* Try to take the semaphore in case some other process is using the device */ if (xSemaphoreTakeFromISR(xSemaphore, NULL) == pdTRUE) { HAL_I2C_Master_Receive(&I2C_Handle, (uint16_t)(DevAddress << 1), pBuffer, Size, 500); /* TODO: Check timeout value */ xSemaphoreGiveFromISR(xSemaphore, NULL); } }
void ADCIntHandler(void) { while(!ADCIntStatus(MODULE_ADC_BASE, MODULE_ADC_SEQUENCE_NUM, false)); ADCIntClear(MODULE_ADC_BASE, MODULE_ADC_SEQUENCE_NUM); xSemaphoreTakeFromISR(adc_mutex, pdFALSE); ADCSequenceDataGet(MODULE_ADC_BASE, MODULE_ADC_SEQUENCE_NUM, adc_vals); xSemaphoreGiveFromISR(adc_mutex, pdTRUE); ADCProcessorTrigger(MODULE_ADC_BASE, MODULE_ADC_SEQUENCE_NUM); }
/** * @brief Write one byte to the FLASH. Can be called from an ISR * @note Addresses to be written must be in the erased state * @param WriteAddress: FLASH's internal address to write to. * @param Byte: the data to be written. * @retval None */ void SPI_FLASH_WriteByteFromISR(uint32_t WriteAddress, uint8_t Byte) { /* Try to take the semaphore in case some other process is using the device */ if (xSemaphoreTakeFromISR(xSemaphore, NULL) == pdTRUE) { prvSPI_FLASH_WriteByte(WriteAddress, Byte); /* Give back the semaphore */ xSemaphoreGiveFromISR(xSemaphore, NULL); } }
void FreeMsgFromISR(Msg* pMsg, portBASE_TYPE* pWoken) { assert_param(pMsg); // Lock xSemaphoreTakeFromISR(s_ArrayMutex, pWoken); pMsg->Id = MSG_UNUSED; s_MsgNumber--; // Unlock xSemaphoreGiveFromISR(s_ArrayMutex, pWoken); }
/***************************************************************************** Prototype : DMA1_Channel3_IRQHandler Description : uart3 DMA-Rx handler Input : void Output : None Return Value : Calls : Called By : History : 1.Date : 2015/9/7 Author : qiuweibo Modification : Created function *****************************************************************************/ void DMA1_Channel3_IRQHandler(void) { BaseType_t xHigherPriorityTaskWoken, xResult; // xHigherPriorityTaskWoken must be initialised to pdFALSE. xHigherPriorityTaskWoken = pdFALSE; DMA_ClearITPendingBit(DMA1_IT_TC3); DMA_Cmd(DMA1_Channel3, DISABLE); //close DMA incase receive data while handling if( pdTRUE != xSemaphoreTakeFromISR( xSerialRxHandleLock, &xHigherPriorityTaskWoken)) { return; } if (uart3_rx_dma_buf.IdleBufferIndex) //buf1 busy, buf2 idle { //buffer1 finished recevied mission (full), switch to buffer2 uart3_rx_dma_buf.nBuff1Offset = uart3_rx_dma_buf.nBuff1MaxLength; DMA1_Channel3->CMAR = (uint32_t)uart3_rx_dma_buf.pPingPongBuff2; DMA1_Channel3->CNDTR = uart3_rx_dma_buf.nBuff2MaxLength; uart3_rx_dma_buf.IdleBufferIndex = 0; } else //buf2 busy, buf1 idle { //buffer2 finished recevied mission (full), switch to buffer1 uart3_rx_dma_buf.nBuff2Offset = uart3_rx_dma_buf.nBuff2MaxLength; DMA1_Channel3->CMAR = (uint32_t)uart3_rx_dma_buf.pPingPongBuff1; DMA1_Channel3->CNDTR = uart3_rx_dma_buf.nBuff1MaxLength; uart3_rx_dma_buf.IdleBufferIndex = 1; } xSemaphoreGiveFromISR( xSerialRxHandleLock ,&xHigherPriorityTaskWoken); DMA_Cmd(DMA1_Channel3, ENABLE); //open DMA after handled //boardcast message to handle xResult = xEventGroupSetBitsFromISR( xUart3RxEventGroup, // The event group being updated. UART_DMA_RX_COMPLETE_EVENT_BIT, // The bits being set. &xHigherPriorityTaskWoken ); // Was the message posted successfully? if( xResult == pdPASS ) { // If xHigherPriorityTaskWoken is now set to pdTRUE then a context // switch should be requested. The macro used is port specific and // will be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() - // refer to the documentation page for the port being used. portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); } }
void logFromISR(String text) { // ToDo: Add other debug outputs if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) { if (xSemaphoreTakeFromISR(serialPortMutex, NULL) == pdTRUE) { DEBUG_SERIAL.println(text); BaseType_t xHigherPriorityTaskWoken; xSemaphoreGiveFromISR(serialPortMutex, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } else { DEBUG_SERIAL.println(text); } }
Msg* MallocMsgFromISR(portBASE_TYPE* pWoken) { Msg* pMsg = NULL; // Lock xSemaphoreTakeFromISR(s_ArrayMutex, pWoken); pMsg = GetBlankMsg(); // Unlock xSemaphoreGiveFromISR(s_ArrayMutex, pWoken); return pMsg; }
static void button_handler(uint32_t portId, uint32_t mask) { portBASE_TYPE xHigherTaskWoken = pdFALSE; xSemaphoreTakeFromISR(g_rtos_button_data.mutex, &xHigherTaskWoken); rtos_button_pio_port_t* my_port = &g_rtos_button_data.ports[portId - ID_PIOA]; Pio* calculated_pio = (Pio*)(((uint32_t)PIOA) + PIO_DELTA * (portId - ID_PIOA)); my_port->last_update_mask = mask; my_port->last_update_data = calculated_pio->PIO_PDSR; my_port->flags |= RTOS_BUTTON_PORT_UPDATED_MASK; xSemaphoreGiveFromISR(g_rtos_button_data.mutex, &xHigherTaskWoken); xHigherTaskWoken = pdFALSE; xSemaphoreGiveFromISR(g_rtos_button_data.rtos_internal_task_semaphore, &xHigherTaskWoken); portEND_SWITCHING_ISR(xHigherTaskWoken); }
void uart_rx(int length) { if (!sock_connected) { uart_rx_one_char(UART); return; } int i = 0; unsigned char c; bool rb_full = false; if (xSemaphoreTakeFromISR(ringbuf_mutex, NULL)) { while (ringbuf_get(&ringbuf_t, &c) == 0) { if (ringbuf_owr(&ringbuf_m, c)) { rb_full = true; } } for (i = 0; i < length; i++) { if (ringbuf_owr(&ringbuf_m, uart_rx_one_char(UART0))) { rb_full = true; } } xSemaphoreGiveFromISR(ringbuf_mutex, NULL); } else { DBG("uart_rx: failed to take semphr"); // ringbuf is taken by send task // all uart data is copied in temporary ringbuffer that will be copied in main ringbuffer once it is available for (i = 0; i < length; i++) { if (ringbuf_owr(&ringbuf_t, uart_rx_one_char(UART0))) { rb_full = true; } } } //DBG("uart_rx: %d b", length); // notify send task that there is data to be sent xQueueSendToBackFromISR(tx_queue, &length, NULL); if (rb_full) { // not every byte could be put in one of the two ringbuffers // we can just print a message about it DBG("uart_rx: ringbuff overflow"); } }
/** * @brief Write one byte to the EEPROM. Can be called from an ISR * @param WriteAddress: EEPROM's internal address to write to. * @param Byte: the data to be written. * @retval None */ void I2C_EEPROM_WriteByteFromISR(uint32_t WriteAddress, uint8_t Byte) { if (WriteAddress <= I2C_EEPROM_LAST_ADDRESS && !prvWriteProtected) { /* Try to take the semaphore in case some other process is using the device */ if (xSemaphoreTakeFromISR(xSemaphore, NULL) == pdTRUE) { /* Wait until some time has passed since the last write */ vTaskDelayUntil(&xNextWriteTime, I2C_EEPROM_MS_BETWEEN_WRITES / portTICK_PERIOD_MS); /* Transmit the address + data byte*/ uint8_t tempData[3] = {(WriteAddress >> 8) & 0x7F, WriteAddress & 0xFF, Byte}; I2C3_Transmit(I2C_EEPROM_BUS_ADDRESS, tempData, 3); /* Give back the semaphore */ xSemaphoreGive(xSemaphore); } } }
/** Lock a mutex * @param mutex the mutex to lock */ void sys_mutex_lock( sys_mutex_t *pxMutex ) { BaseType_t xGotSemaphore; BaseType_t xHigherPriorityTaskWoken = pdFALSE; if( xInsideISR == 0 ) { while( xSemaphoreTake( *pxMutex, portMAX_DELAY ) != pdPASS ); } else { xGotSemaphore = xSemaphoreTakeFromISR( *pxMutex, &xHigherPriorityTaskWoken ); configASSERT( xGotSemaphore ); portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); /* Prevent compiler warnings if configASSERT() is not defined. */ ( void ) xGotSemaphore; } }
bool I2C_Base::transfer(char deviceAddress, char firstReg, char* pData, unsigned int transferSize) { bool status = false; if(mDisableOperation || !pData) { return status; } // If scheduler not running, perform polling transaction if(taskSCHEDULER_RUNNING != xTaskGetSchedulerState()) { i2cKickOffTransfer(deviceAddress, firstReg, pData, transferSize); // Wait for transfer to finish const uint64_t timeout = sys_get_uptime_ms() + I2C_TIMEOUT_MS; while (!xSemaphoreTakeFromISR(mTransferCompleteSignal, NULL)) { if (sys_get_uptime_ms() > timeout) { break; } } status = (0 == mTransaction.error); } else if (xSemaphoreTake(mI2CMutex, OS_MS(I2C_TIMEOUT_MS))) { // Clear potential stale signal and start the transfer xSemaphoreTake(mTransferCompleteSignal, 0); i2cKickOffTransfer(deviceAddress, firstReg, pData, transferSize); // Wait for transfer to finish and copy the data if it was read mode if (xSemaphoreTake(mTransferCompleteSignal, OS_MS(I2C_TIMEOUT_MS))) { status = (0 == mTransaction.error); } xSemaphoreGive(mI2CMutex); } return status; }
bool MTD_FLASHMEM Mutex::lockFromISR() { signed portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; return xSemaphoreTakeFromISR(m_handle, &xHigherPriorityTaskWoken); }
static int32_t IRAM_ATTR semphr_take_from_isr_wrapper(void *semphr, void *hptw) { return (int32_t)xSemaphoreTakeFromISR(semphr, hptw); }
/***************************************************************************** Prototype : DMA1_Channel2_IRQHandler Description : uart3 DMA-Tx Handler Input : void Output : None Return Value : Calls : Called By : History : 1.Date : 2015/9/7 Author : qiuweibo Modification : Created function *****************************************************************************/ void DMA1_Channel2_IRQHandler(void) { BaseType_t xHigherPriorityTaskWoken, xResult; // xHigherPriorityTaskWoken must be initialised to pdFALSE. xHigherPriorityTaskWoken = pdFALSE; DMA_ClearITPendingBit(DMA1_IT_TC2); DMA_Cmd(DMA1_Channel2, DISABLE); // close DMA xResult = xSemaphoreTakeFromISR( xSerialTxHandleLock, &xHigherPriorityTaskWoken); if( pdTRUE != xResult) { return; } //After finish this send mission, need to do: clear status --> check next mission if (uart3_tx_dma_buf.IdleBufferIndex) { // reset buffer1 uart3_tx_dma_buf.nBuff1Offset = 0; // check for buffer2 if (uart3_tx_dma_buf.nBuff2Offset > 0) { DMA1_Channel2->CMAR = (uint32_t)uart3_tx_dma_buf.pPingPongBuff2; DMA1_Channel2->CNDTR = uart3_tx_dma_buf.nBuff2Offset; uart3_tx_dma_buf.IdleBufferIndex = 0; DMA_Cmd(DMA1_Channel2, ENABLE); // open DMA } else { uart3_tx_dma_buf.IsDMAWroking = 0; } } else { //reset buffer2 uart3_tx_dma_buf.nBuff2Offset = 0; // check for buffer1 if (uart3_tx_dma_buf.nBuff1Offset > 0) { DMA1_Channel2->CMAR = (uint32_t)uart3_tx_dma_buf.pPingPongBuff1; DMA1_Channel2->CNDTR = uart3_tx_dma_buf.nBuff1Offset; uart3_tx_dma_buf.IdleBufferIndex = 1; DMA_Cmd(DMA1_Channel2, ENABLE); // open DMA } else { uart3_tx_dma_buf.IsDMAWroking = 0; } } xResult = xSemaphoreGiveFromISR( xSerialTxHandleLock , &xHigherPriorityTaskWoken); if( xResult == pdPASS ) { // If xHigherPriorityTaskWoken is now set to pdTRUE then a context // switch should be requested. The macro used is port specific and // will be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() - // refer to the documentation page for the port being used. portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); } }
void USART3_IRQHandler(void) { volatile u32 tem_reg; volatile u16 u16BufferUsedLen = 0; BaseType_t xHigherPriorityTaskWoken, xResult; // xHigherPriorityTaskWoken must be initialised to pdFALSE. xHigherPriorityTaskWoken = pdFALSE; // error happen if(USART_GetITStatus(USART3, USART_IT_PE) != RESET) { USART_ClearITPendingBit(USART3, USART_IT_PE); xSerialRxParityFlag = DMA_UART_PACKET_PARITY_ERR; } // uart idle interrupt if(USART_GetITStatus(USART3, USART_IT_IDLE) != RESET) { USART_ClearITPendingBit(USART3, USART_IT_IDLE); DMA_ClearFlag(DMA1_FLAG_GL3);//clear all interrupt flags DMA_Cmd(DMA1_Channel3, DISABLE); //close DMA incase receive data while handling xResult = xSemaphoreTakeFromISR( xSerialRxHandleLock, &xHigherPriorityTaskWoken); if( pdTRUE == xResult) { if (uart3_rx_dma_buf.IdleBufferIndex) //buf1 busy, buf2 idle { u16BufferUsedLen = uart3_rx_dma_buf.nBuff1MaxLength - DMA_GetCurrDataCounter(DMA1_Channel3); if (u16BufferUsedLen > 0) { uart3_rx_dma_buf.nBuff1Offset = u16BufferUsedLen; DMA1_Channel3->CMAR = (uint32_t)uart3_rx_dma_buf.pPingPongBuff2; DMA1_Channel3->CNDTR = uart3_rx_dma_buf.nBuff2MaxLength; uart3_rx_dma_buf.IdleBufferIndex = 0; } } else { u16BufferUsedLen = uart3_rx_dma_buf.nBuff2MaxLength - DMA_GetCurrDataCounter(DMA1_Channel3); if (u16BufferUsedLen > 0) { uart3_rx_dma_buf.nBuff2Offset = u16BufferUsedLen; DMA1_Channel3->CMAR = (uint32_t)uart3_rx_dma_buf.pPingPongBuff1; DMA1_Channel3->CNDTR = uart3_rx_dma_buf.nBuff1MaxLength; uart3_rx_dma_buf.IdleBufferIndex = 1; } } xResult = xSemaphoreGiveFromISR( xSerialRxHandleLock ,&xHigherPriorityTaskWoken); if (u16BufferUsedLen > 0) { //boardcast message to handle xResult = xEventGroupSetBitsFromISR( xUart3RxEventGroup, // The event group being updated. UART_DMA_RX_INCOMPLETE_EVENT_BIT,// The bits being set. &xHigherPriorityTaskWoken ); } //End if u16BufferUsedLen > 0 }// End if pdTRUE == xSemaphoreTakeFromISR DMA_Cmd(DMA1_Channel3, ENABLE); //open DMA after handled //clear Idle flag by read SR and DR tem_reg = USART3->SR; tem_reg = USART3->DR; tem_reg = tem_reg; // slove warning }// End if USART_IT_IDLE if(USART_GetITStatus(USART3, USART_IT_FE | USART_IT_NE) != RESET) { USART_ClearITPendingBit(USART3, USART_IT_FE | USART_IT_NE); } if( xResult == pdPASS ) { // If xHigherPriorityTaskWoken is now set to pdTRUE then a context // switch should be requested. The macro used is port specific and // will be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() - // refer to the documentation page for the port being used. portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); } }
static void configure_time_trigger_for_ssc(uint32_t ssc_trigger_hz) { #if 0 gpio_configure_pin(PIO_PA12_IDX, PIO_PERIPH_B); pmc_enable_periph_clk(ID_PWM); /* Disable PWM channels */ pwm_channel_disable(PWM, PWM_CHANNEL_1); /* Set PWM clock A as PWM_FREQUENCY*PERIOD_VALUE (clock B is not used) */ pwm_clock_t clock_setting = { .ul_clka = 1000 * 100, .ul_clkb = 0, .ul_mck = sysclk_get_cpu_hz() }; pwm_init(PWM, &clock_setting); pwm_channel_t g_pwm_channel; /* Period is left-aligned */ g_pwm_channel.alignment = PWM_ALIGN_LEFT; /* Output waveform starts at a low level */ g_pwm_channel.polarity = PWM_LOW; /* Use PWM clock A as source clock */ g_pwm_channel.ul_prescaler = PWM_CMR_CPRE_CLKA; /* Period value of output waveform */ g_pwm_channel.ul_period = 100; /* Duty cycle value of output waveform */ g_pwm_channel.ul_duty = 50; g_pwm_channel.channel = PWM_CHANNEL_1; pwm_channel_init(PWM, &g_pwm_channel); /* Disable channel counter event interrupt */ pwm_channel_disable_interrupt(PWM, PWM_CHANNEL_1, 0); pwm_channel_enable(PWM, PWM_CHANNEL_1); #endif uint32_t ul_div = 0; uint32_t ul_tc_clks = 0; uint32_t ul_sysclk = sysclk_get_cpu_hz(); pmc_set_writeprotect(false); // Enable peripheral clock. pmc_enable_periph_clk(CONF_SSC_CLOCK_SOURCE_ID); // TIOA configuration // gpio_configure_pin(PIN_TC0_TIOA0, PIN_TC0_TIOA0_FLAGS); // tc_set_writeprotect(TC2, true); tc_set_writeprotect(CONF_SSC_CLOCK_TC, false); // Configure TC for a 1Hz frequency and trigger on RC compare. tc_find_mck_divisor(ssc_trigger_hz, ul_sysclk, &ul_div, &ul_tc_clks, ul_sysclk); tc_init(CONF_SSC_CLOCK_TC, CONF_SSC_CLOCK_CHANNEL, ul_tc_clks | TC_CMR_WAVSEL_UP_RC | TC_CMR_WAVE | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET); uint32_t tmp_val = (ul_sysclk / ul_div) / ssc_trigger_hz; tc_write_ra(CONF_SSC_CLOCK_TC, CONF_SSC_CLOCK_CHANNEL, tmp_val / 2); tc_write_rc(CONF_SSC_CLOCK_TC, CONF_SSC_CLOCK_CHANNEL, tmp_val); // Start the Timer. tc_start(CONF_SSC_CLOCK_TC, CONF_SSC_CLOCK_CHANNEL); } void tm_stick_init(uint32_t bus_speed_hz) { if(g_tm_stick_data.mutex == NULL) { g_tm_stick_data.mutex = xSemaphoreCreateMutex(); } if(g_tm_stick_data.rtos_task_semaphore == NULL) { vSemaphoreCreateBinary(g_tm_stick_data.rtos_task_semaphore); } configure_time_trigger_for_ssc(1000); sysclk_enable_peripheral_clock(ID_SSC); ssc_reset(SSC); // Do not go over 1MHz, the pulse will be too long and extend over to the next clock's rising edge (The spec. sheet of 4021BCM does not really list the operation times of 3.3V operation. // I tested a couple of speeds.... at 1.25MHz, I found that it sometimes misses some pulses. if(bus_speed_hz > TM_STICK_MAX_BUS_SPEED){ bus_speed_hz = TM_STICK_MAX_BUS_SPEED; } ssc_set_clock_divider(SSC, bus_speed_hz, sysclk_get_cpu_hz()); clock_opt_t rx_clk_opt = { .ul_cks = SSC_RCMR_CKS_MCK, .ul_cko = SSC_RCMR_CKO_TRANSFER, // TODO: Fix the SSC clock for some reason shift 1/2 clock pulse position. // This makes the button results shift one position to the right. So we have to change rising/falling edge to "correct" it (oh, well, "hack" around it). // Don't know why. // One possible patch up is to use a pin to signal which one is desired to determine which of the following settings to use.. #if defined(CONF_BOARD_ARDUINO_DUE) .ul_cki = 0, //SSC_RCMR_CKI, #else .ul_cki = SSC_RCMR_CKI, #endif .ul_ckg = SSC_RCMR_CKG_CONTINUOUS, .ul_start_sel = SSC_RCMR_START_RF_FALLING, .ul_period = 0, .ul_sttdly = 0 }; data_frame_opt_t rx_data_frame_opt = { .ul_datlen = 23, .ul_msbf = 0, //SSC_RFMR_MSBF, .ul_fsos = SSC_RFMR_FSOS_NONE, .ul_datnb = 0, .ul_fsedge = SSC_RFMR_FSEDGE_NEGATIVE, .ul_fslen = 0, .ul_fslen_ext = 0 }; ssc_set_receiver(SSC, &rx_clk_opt, &rx_data_frame_opt); ssc_enable_interrupt(SSC, SSC_IER_RXRDY); // It is VERY IMPORTANT to set the priority of the interrupt to conform to what FreeRTOS needs because we are calling FreeRTOS interrupt-safe APIs (those *FromISR) from within interrupt handlers. // If we don't do this, if would work at the beginning, and then eventually the whole thing will come crashing down. // And it's not gonna crash even after millions of interrupts are handled. It will come crashing down in some very weird place after you start using another interrupt handler, like the pio handlers, and only after some handlling... // And, it will appear random. You press the button a couple of times, it dies. Then, at other times, you press and hold the button, etc. etc. Each time will be different. // You would be suspecting your stack overflowed, your code does a wild pointer, etc. etc. It's very difficult to debug. Trust me, you don't wanna go there. NVIC_ClearPendingIRQ(SSC_IRQn); NVIC_SetPriority(SSC_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); NVIC_EnableIRQ(SSC_IRQn); ssc_enable_rx(SSC); } void SSC_Handler( void ) { portBASE_TYPE xHigherTaskWoken = pdFALSE; uint32_t* in_data_buf = (uint32_t*)g_tm_stick_data.data; uint32_t in_data = 0; static uint32_t previous_in_data = 0; static bool is_first_time = true; // calculate the mask needed to wipe off extra high bit junk. static uint32_t mask = 0xFFFFFFFF; if(is_first_time) { for(int i = 4; i > TM_STICK_NUM_DATA_BYTES; i--) { mask = mask >> 8; } } if(ssc_is_rx_ready(SSC) == SSC_RC_YES) { xSemaphoreTakeFromISR(g_tm_stick_data.mutex, &xHigherTaskWoken); in_data = SSC->SSC_RHR; // in_data >>= 1; // No idea why it always reads 25bits (one too many bit at the end LSB), instead of 24 I told it to. Therefore, we shift it off. in_data = ~in_data; // reverse it. So, now 1 is on, 0 is off. in_data &= mask; // Glitch filtering below. if(is_first_time || (previous_in_data ^ in_data) == 0) { is_first_time = false; *in_data_buf = in_data; } previous_in_data = in_data; xSemaphoreGiveFromISR(g_tm_stick_data.mutex, &xHigherTaskWoken); xHigherTaskWoken = pdFALSE; xSemaphoreGiveFromISR(g_tm_stick_data.rtos_task_semaphore, &xHigherTaskWoken); // if there is an RTOS waiting for the ADC task mutex, wake it up. portEND_SWITCHING_ISR(xHigherTaskWoken); } } #ifdef __cplusplus }