STM32F4 - CAN bus transmit succeeds every time, but CAN receive only succeeds on the first call - interrupt

I'm using an STM32F469 Discovery board and I'm trying to use the CAN features.
I understand that on this board CAN1 cannot be used at the same time as the touchscreen. Therefore I need to use CAN2, but in order to enable CAN2, CAN1 needs to be enabled.
My code for configuration/callback is as follows:
/* CAN1 Values */
#define CAN1_CLK_ENABLE() __HAL_RCC_CAN1_CLK_ENABLE()
#define CAN1_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define CAN1_FORCE_RESET() __HAL_RCC_CAN1_FORCE_RESET()
#define CAN1_RELEASE_RESET() __HAL_RCC_CAN1_RELEASE_RESET()
#define CAN1_TX_PIN GPIO_PIN_9
#define CAN1_TX_GPIO_PORT GPIOB
#define CAN1_TX_AF GPIO_AF9_CAN1
#define CAN1_RX_PIN GPIO_PIN_8
#define CAN1_RX_GPIO_PORT GPIOB
#define CAN1_RX_AF GPIO_AF9_CAN1
#define CAN1_RX_IRQn CAN1_RX0_IRQn
#define CAN1_RX_IRQHandler CAN1_RX0_IRQHandler
/* CAN2 Values */
#define CAN2_CLK_ENABLE() __HAL_RCC_CAN2_CLK_ENABLE()
#define CAN2_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define CAN2_FORCE_RESET() __HAL_RCC_CAN2_FORCE_RESET()
#define CAN2_RELEASE_RESET() __HAL_RCC_CAN2_RELEASE_RESET()
#define CAN2_TX_PIN GPIO_PIN_13
#define CAN2_TX_GPIO_PORT GPIOB
#define CAN2_TX_AF GPIO_AF9_CAN2
#define CAN2_RX_PIN GPIO_PIN_5
#define CAN2_RX_GPIO_PORT GPIOB
#define CAN2_RX_AF GPIO_AF9_CAN2
#define CAN2_RX_IRQn CAN2_RX0_IRQn
#define CAN2_RX_IRQHandler CAN2_RX0_IRQHandler
CAN_HandleTypeDef CanHandle1;
CAN_HandleTypeDef CanHandle2;
static uint8_t Message_Data[8];
static void CAN1_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
CAN_FilterConfTypeDef CAN_FilterInitStructure;
static CanTxMsgTypeDef TxMessage;
static CanRxMsgTypeDef RxMessage;
/* CAN1 peripheral clock enable */
CAN1_CLK_ENABLE();
CAN1_GPIO_CLK_ENABLE();
/* CAN1 TX GPIO pin configuration */
GPIO_InitStruct.Pin = CAN1_TX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = CAN1_TX_AF;
HAL_GPIO_Init(CAN1_TX_GPIO_PORT, &GPIO_InitStruct);
/* CAN1 RX GPIO pin configuration */
GPIO_InitStruct.Pin = CAN1_RX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = CAN1_RX_AF;
HAL_GPIO_Init(CAN1_RX_GPIO_PORT, &GPIO_InitStruct);
/* NVIC configuration for CAN1 reception complete interrupt */
HAL_NVIC_SetPriority(CAN1_RX_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(CAN1_RX_IRQn);
CanHandle1.Instance = CAN1;
CanHandle1.pTxMsg = &TxMessage;
CanHandle1.pRxMsg = &RxMessage;
/* CAN peripheral init */
CanHandle1.Init.TTCM = DISABLE;
CanHandle1.Init.ABOM = DISABLE;
CanHandle1.Init.AWUM = DISABLE;
CanHandle1.Init.NART = DISABLE;
CanHandle1.Init.RFLM = DISABLE;
CanHandle1.Init.TXFP = DISABLE;
CanHandle1.Init.Mode = CAN_MODE_LOOPBACK;
CanHandle1.Init.SJW = CAN_SJW_1TQ;
CanHandle1.Init.BS1 = CAN_BS1_6TQ;
CanHandle1.Init.BS2 = CAN_BS2_8TQ;
CanHandle1.Init.Prescaler = 2;
HAL_CAN_Init(&CanHandle1);
/* CAN filter init */
CAN_FilterInitStructure.FilterNumber = 0;
CAN_FilterInitStructure.FilterMode = CAN_FILTERMODE_IDMASK;
CAN_FilterInitStructure.FilterScale = CAN_FILTERSCALE_32BIT;
CAN_FilterInitStructure.FilterIdHigh = 0x0000;
CAN_FilterInitStructure.FilterIdLow = 0x0000;
CAN_FilterInitStructure.FilterMaskIdHigh = 0x0000;
CAN_FilterInitStructure.FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.FilterFIFOAssignment = 0;
CAN_FilterInitStructure.FilterActivation = ENABLE;
CAN_FilterInitStructure.BankNumber = 0;
HAL_CAN_ConfigFilter(&CanHandle1, &CAN_FilterInitStructure);
/* Configure transmission */
CanHandle1.pTxMsg->StdId = 0x7DF;
CanHandle1.pTxMsg->ExtId = 0x7DF;
CanHandle1.pTxMsg->RTR = CAN_RTR_DATA;
CanHandle1.pTxMsg->IDE = CAN_ID_STD;
CanHandle1.pTxMsg->DLC = 8;
}
static void CAN2_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
CAN_FilterConfTypeDef CAN_FilterInitStructure;
static CanTxMsgTypeDef TxMessage;
static CanRxMsgTypeDef RxMessage;
/* CAN2 peripheral clock enable */
CAN2_CLK_ENABLE();
CAN2_GPIO_CLK_ENABLE();
/* CAN2 TX GPIO pin configuration */
GPIO_InitStruct.Pin = CAN2_TX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = CAN2_TX_AF;
HAL_GPIO_Init(CAN2_TX_GPIO_PORT, &GPIO_InitStruct);
/* CAN2 RX GPIO pin configuration */
GPIO_InitStruct.Pin = CAN2_RX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = CAN2_RX_AF;
HAL_GPIO_Init(CAN2_RX_GPIO_PORT, &GPIO_InitStruct);
/* NVIC configuration for CAN2 reception complete interrupt */
HAL_NVIC_SetPriority(CAN2_RX_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(CAN2_RX_IRQn);
CanHandle2.Instance = CAN2;
CanHandle2.pTxMsg = &TxMessage;
CanHandle2.pRxMsg = &RxMessage;
/* CAN peripheral init */
CanHandle2.Init.TTCM = DISABLE;
CanHandle2.Init.ABOM = DISABLE;
CanHandle2.Init.AWUM = DISABLE;
CanHandle2.Init.NART = DISABLE;
CanHandle2.Init.RFLM = DISABLE;
CanHandle2.Init.TXFP = DISABLE;
CanHandle2.Init.Mode = CAN_MODE_LOOPBACK;
CanHandle2.Init.SJW = CAN_SJW_1TQ;
CanHandle2.Init.BS1 = CAN_BS1_6TQ;
CanHandle2.Init.BS2 = CAN_BS2_8TQ;
CanHandle2.Init.Prescaler = 2;
HAL_CAN_Init(&CanHandle2);
/* CAN filter init */
CAN_FilterInitStructure.FilterNumber = 0; //14 enables CAN1;
CAN_FilterInitStructure.FilterMode = CAN_FILTERMODE_IDMASK;
CAN_FilterInitStructure.FilterScale = CAN_FILTERSCALE_32BIT;
CAN_FilterInitStructure.FilterIdHigh = 0x0000;
CAN_FilterInitStructure.FilterIdLow = 0x0000;
CAN_FilterInitStructure.FilterMaskIdHigh = 0x0000;
CAN_FilterInitStructure.FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.FilterFIFOAssignment = 0;
CAN_FilterInitStructure.FilterActivation = ENABLE;
CAN_FilterInitStructure.BankNumber = 0; // 14 enables CAN1
HAL_CAN_ConfigFilter(&CanHandle2, &CAN_FilterInitStructure);
/* Configure transmission */
CanHandle2.pTxMsg->StdId = 0x7DF;
CanHandle2.pTxMsg->ExtId = 0x7DF;
CanHandle2.pTxMsg->RTR = CAN_RTR_DATA;
CanHandle2.pTxMsg->IDE = CAN_ID_STD;
CanHandle2.pTxMsg->DLC = 8;
}
void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* CanHandle)
{
EwBspYellowLedOn();
Message_Data[0] = CanHandle->pRxMsg->Data[0];
Message_Data[1] = CanHandle->pRxMsg->Data[1];
Message_Data[2] = CanHandle->pRxMsg->Data[2];
Message_Data[3] = CanHandle->pRxMsg->Data[3];
Message_Data[4] = CanHandle->pRxMsg->Data[4];
Message_Data[5] = CanHandle->pRxMsg->Data[5];
Message_Data[6] = CanHandle->pRxMsg->Data[6];
Message_Data[7] = CanHandle->pRxMsg->Data[7];
if (HAL_CAN_Receive_IT(CanHandle, CAN_FIFO0) != HAL_OK)
{
EwBspRedLedOn();
}
}
CAN_Transmit_Message(void)
{
CanHandle2.pTxMsg->StdId = 0x7DF;
CanHandle2.pTxMsg->ExtId = 0x7DF;
CanHandle2.pTxMsg->Data[0] = 0x02;
CanHandle2.pTxMsg->Data[1] = 0x01;
CanHandle2.pTxMsg->Data[2] = 0x0D;
CanHandle2.pTxMsg->Data[3] = 0x55;
CanHandle2.pTxMsg->Data[4] = 0x55;
CanHandle2.pTxMsg->Data[5] = 0x55;
CanHandle2.pTxMsg->Data[6] = 0x55;
CanHandle2.pTxMsg->Data[7] = 0x55;
if (HAL_CAN_Transmit(&CanHandle, 10) != HAL_OK)
{
EwBspOrangeLedOn();
}
HAL_Delay(10);
}
I then run the following in my main function to configure the CAN1, CAN2 and the interrupt:
/* Configure interrupt for CAN transmission */
CAN1_Config();
CAN2_Config();
HAL_CAN_Receive_IT(&CanHandle2, CAN_FIFO0);
And then I run the CAN_Transmit_Message().
When doing this I have verified the message successfully transmits (the orange LED does not turn on), the receive interrupt handler is then executed (yellow LED turns on) and the message is successfully received (red LED does not turn on).
However, on the second transmission of a message (another call to CAN_Transmit_Message()), the transmit once again succeeds, but the receive fails (red LED turns on).
I created this code by following the structure in the CAN_Networking example code, but I cannot figure out why it is failing at the HAL_CAN_Receive_IT function on the second message (after the first message is successfully received).
Note: After reading the stm32f4xx_HAL_CAN library file, I noticed there are two types of receive/transmit:
HAL_CAN_Transmit_IT/HAL_CAN_Receive_IT
HAL_CAN_Transmit/HAL_CAN_Receive
It says that 1. is nonblocking - I take it this means that another interrupt can be triggered whilst this transmit/receive is still running?
In my case I want to make sure that I receive the response data after sending the transmit to request it, so should I use function 2.? I.e. I would call HAL_CAN_Transmit with a suitable timeout, and then after it completes call HAL_CAN_Receive, again with a suitable timeout.

Do you call HAL_CAN_Receive_IT each time you get a response?
It's one shot. To keep receiving, call it again in your interrupt handler.
From the Reference Manual:
When a message has been received, it is available to the software in the FIFO output mailbox. Once the software has handled the message (e.g. read it) the software must release the FIFO output mailbox by means of the RFOM bit in the CAN_RFR register to make the next incoming message available.
HAL_CAN_Receive_IT contains lines that enable the interrupts and release the FIFO...
/* Enable interrupts: */
/* - Enable Error warning Interrupt */
/* - Enable Error passive Interrupt */
/* - Enable Bus-off Interrupt */
/* - Enable Last error code Interrupt */
/* - Enable Error Interrupt */
/* - Enable Transmit mailbox empty Interrupt */
__HAL_CAN_ENABLE_IT(hcan, CAN_IT_EWG |
CAN_IT_EPV |
CAN_IT_BOF |
CAN_IT_LEC |
CAN_IT_ERR |
CAN_IT_TME );
/* Process unlocked */
__HAL_UNLOCK(hcan);
if (FIFONumber == CAN_FIFO0)
{
/* Enable FIFO 0 message pending Interrupt */
__HAL_CAN_ENABLE_IT(hcan, CAN_IT_FMP0);
}
else
{
/* Enable FIFO 1 message pending Interrupt */
__HAL_CAN_ENABLE_IT(hcan, CAN_IT_FMP1);
}

Related

how do I select an STM32 for low external interrupt time?

I have a circuit which needs to respond in around 0.5uS to an external interrupt. I built the circuit with an STM32F031K6 and a 20MHz oscillator set to run on the 2x PLL, giving a 40MHz clock. I was surprised to see that although one clock cycle would be 25nS, i could only toggle a pin at 300nS - im not exactly sure why it takes so long, i have some experience with 8 bit AVRs and although I wouldn't expect it to run in one clock cycle, 12 seems slow. The external interrupt takes 3uS to respond. how can i choose a chip to meet my requirement of 0.5uS?
I'm just assuming that i need to change the chip, if anyone has advice on how i might reduce the response time that would also be great
my full code is here, this is a blank program generated by cube, i stripped out some of the generated commenting to make it easier to read
int main(void)
{
MX_GPIO_Init();
MX_ADC_Init();
while (1)
{
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI14|RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSI14State = RCC_HSI14_ON;
RCC_OscInitStruct.HSI14CalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL2;
RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
}
static void MX_ADC_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.DMAContinuousRequests = DISABLE;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_1;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5
|GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);
/*Configure GPIO pins : PA2 PA11 */
GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PA3 PA4 PA12 PA15 */
GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_12|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PA6 PA7 */
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM3;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pin : PB0 */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM3;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pins : PB1 PB3 PB4 PB5
PB6 PB7 */
GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5
|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pins : PA8 PA9 PA10 */
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI2_3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI2_3_IRQn);
HAL_NVIC_SetPriority(EXTI4_15_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);
}
void Error_Handler(void)
{
}
#ifdef USE_FULL_ASSERT
/**
* #brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* #param file: pointer to the source file name
* #param line: assert_param error line source number
* #retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
void EXTI2_3_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
}
void EXTI4_15_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_11);
GPIOB->ODR ^= 1<<1;
}
First of all, I recommend having a look at this ARM blog post for an in-depth introduction to interrupt latency of ARM Cortex-M processors.
As mentioned by #Colin the interrupt latency of a STM32F0 MCU with a Cortex-M0 core is 16 clock cycles starting when the signal on the EXTI line is asserted until entering the IRQ Handler with code reacting to the event. This clock cycle count cannot be reduced by firmware.
When selecting a MCU with a Cortex-M3 or M4 core (e.g. a STM32F3), this required number of clock cycles drops to 12. The resulting latency still depends on the clock frequency of the core. Selecting a STM32 MCU with higher max. clock rate allows for faster reaction times:
STM32F0: up to 48 MHz Cortex M0 => ISR enter latency 333 ns
STM32G0: up to 64 MHz Cortex M0+ => ISR enter latency 234 ns
STM32F3: up to 72 Mhz Cortex M4 => ISR enter latency 166 ns
STM32G4: up to 170 Mhz Cortex M4 => ISR enter latency 70 ns
These calculation do not solve your problem though, because we have to consider as well what happens after the MCU entered the service routine. Several things come to my mind here:
Backup of additional registers depending on the complexity of the ISR
Code for an application specific reaction to the event (e.g. toggling an GPIO)
Wait-states for FLASH / RAM / Peripheral accesses. The higher the core clock, the more wait-states are typically needed because external parts are clocked at lower frequency.
Code for acknowledging / clearing the interrupt request
The last point can be postponed behind the application specific response an thus does not necessarily count to the reaction time, but all other points can have a significant impact. In order to fulfill your requirement with a cost-efficient STM32 MCU (I suppose you have selected the STM32F0 for this reason) you need to have good control over the number of instructions in the ISR. I do not recommend to use assembler here, but you should not rely on the CubeMX HAL implementation.
Since you say you need only little code in the ISR lets do a quick estimation:
Saving two additional registers on the stack => 2 instructions
Toggle a GPIO with a read-modify-write sequence => 3 instructions
Let's assume that each instruction takes 2 cycles, we need another 10 clock cycles.
Using this best-case scenario we can have a look at our list with rather low-cost STM32 MCU's again:
STM32F0: 16 + 10 cycles at 48 MHz => 541 ns
STM32G0: 15 + 10 cycles at 64 MHz => 390 ns
STM32F3: 12 + 10 cycles at 72 MHz => 300 ns
STM32G4: 12 + 10 cycles at 170 MHz => 130 ns
With these numbers, a Cortex M0/M0+ looks not like the right choice. You will better go for a M3/M4 core with at least 64 MHz clock rate. I think the new G4 could be a good solution.
Anyway, I strongly recommend evaluating the real-world performance with the real-world requirement, since there are too many factors that can affect above latency calculations.
The Cortex-M processors push a stack frame on exception entry, for your Cortex M0 the minimum time from assertion of the exception to running the first instruction of the interrupt handler is 16 clock cycles (assuming zero wait state memory).
The only way to make this take less time is to use a higher clock speed.

STM32F407 Uart Rx inturrupts misses some characters

UART sometimes lost some characters while communicate with all other devices.
I tried communication with arduino, serial monitor(At PC, with serial-usb converter), HM-10 Bluetooth module.
In every case, my stm32f407 misses some characters when receive data.
(I checked it with LCD. I don't send any data with uart.)
For example, when I send send "This is test string" from PC,
The incoming string is like "Ths is tt sring".
There are no bad characters. It only misses some characters.
In handler, I commented USART_Cmd(...), moved USART_ClearITPendingBit(USART3, USART_IT_RXNE); to the bottom, and changed priority to 0,1,2,3.
However, nothing changed.
My clock is 168MHz and uart uses 9600, or 115200 baudrate.
Both have same problem.
Below is my code.
For some reasons, I can't use HAL driver.
void Uart_Init(long uart_baudrate) {
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* GPIO Periph clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
// USART1 Port Configuration (TX - 9, RX - 10)
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Connect USART pins to AF */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART3);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_USART3);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
USART_StructInit(&USART_InitStructure);
USART_InitStructure.USART_BaudRate = uart_baudrate;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART3, &USART_InitStructure);
USART_Cmd(USART3, ENABLE);
}
#define UART_BUF_LEN 64
volatile static char uartBuffer[UART_BUF_LEN];
volatile static int uartBufferIn = 0;
volatile static int uartBufferOut = 0;
//Uart rx handler
void USART3_IRQHandler(void){
unsigned int st1;
st1 = __disable_interrupts();
if(USART_GetITStatus(USART3, USART_IT_RXNE) == SET){
USART_ClearITPendingBit(USART3, USART_IT_RXNE);
uartBuffer[uartBufferIn] = USART_ReceiveData(USART3);
uartBufferIn++;
if(uartBufferIn==uartBufferOut)uartBufferOut++;
uartBufferIn&=0x3F;
uartBufferOut&=0x3F;
}
USART_Cmd(USART3, ENABLE);
__restore_interrupts(st1);
}
//Enable uart rx interrupt
void Uart_Start(void){
unsigned int st1;
st1 = __disable_interrupts();
NVIC_InitTypeDef NVIC_InitStructure;
VectorTable->USART3_IRQHANDLER = USART3_IRQHandler;
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_Cmd(USART3, ENABLE);
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
__restore_interrupts(st1);
}

Input Capture mapping using PPS

I am trying to configure input capture module (IC1) on dsPIC33EP32MC204 to use it for duty cycle measurement. As input I would like to use RP35 pin. To test the correctness of the configuration I set up an experiment: I connected a pulse generator with 1Hz PWM pulses and set up the input capture to capture every rising edge. I reckoned that these rising edges will trigger the input capture interrupt and each call of the interrupt will cause the LED to blink. Unfortunatelly it is not working. I would be grateful if someone could tell me where is the problem. The code:
void Init_InputCapture(void)
{
IC1CON1bits.ICSIDL = 0;
IC1CON1bits.ICTSEL = 0b111; // Peripheral clock (FP) is the clock source of the ICx
IC1CON1bits.ICI = 0b00; //
IC1CON1bits.ICM = 0b011; //Capture mode every edge rising
IC1CON2bits.ICTRIG = 0; // = Input source used to trigger the input capture timer (Trigger mode)
IC1CON2bits.SYNCSEL = 0b00000; //IC1 module synchronizes or triggers ICx
IC1CON2bits.IC32 = 0; // 16 bit mode only
// Enable Capture Interrupt And Timer2
IPC0bits.IC1IP = 1; // Setup IC1 interrupt priority level
IFS0bits.IC1IF = 0; // Clear IC1 Interrupt Status Flag
IEC0bits.IC1IE = 1; // Enable IC1 interrupt
}
The interrupt:
void __attribute__((__interrupt__, no_auto_psv)) _IC1Interrupt(void)
{
LATBbits.LATB9 = ~LATBbits.LATB9;
IFS0bits.IC1IF = 0;
}
And the related code from the main():
__builtin_write_OSCCONL(OSCCON & ~(1<<6));
RPINR7 = 0x23; ; // IC1 mapped to RP35
__builtin_write_OSCCONL(OSCCON | (1<<6));
During the setup I followed the instuctions from the family reference manual.
The code you have posted could be more complete. For dsPIC controllers it is essential to show exactly how all of the configuration bits are set and how the system clock is initialized.
This is your code corrected with the minimum required setup code:
/*
* file: main.c
* target: dsPIC33EP32MC204
* IDE: MPLABX v4.05
* Compiler: XC16 v1.35
*
* Description:
* Use Input Capture 1 module to catch a 1Hz pulse on GPIO RP35 and toggle LED on RB9.
*
*/
#pragma config ICS = PGD2 // ICD Communication Channel Select bits (Communicate on PGEC2 and PGED2)
#pragma config JTAGEN = OFF // JTAG Enable bit (JTAG is disabled)
#pragma config ALTI2C1 = OFF // Alternate I2C1 pins (I2C1 mapped to SDA1/SCL1 pins)
#pragma config ALTI2C2 = OFF // Alternate I2C2 pins (I2C2 mapped to SDA2/SCL2 pins)
#pragma config WDTWIN = WIN25 // Watchdog Window Select bits (WDT Window is 25% of WDT period)
#pragma config WDTPOST = PS32768 // Watchdog Timer Postscaler bits (1:32,768)
#pragma config WDTPRE = PR128 // Watchdog Timer Prescaler bit (1:128)
#pragma config PLLKEN = ON // PLL Lock Enable bit (Clock switch to PLL source will wait until the PLL lock signal is valid.)
#pragma config WINDIS = OFF // Watchdog Timer Window Enable bit (Watchdog Timer in Non-Window mode)
#pragma config FWDTEN = OFF // Watchdog Timer Enable bit (Watchdog timer enabled/disabled by user software)
#pragma config POSCMD = NONE // Primary Oscillator Mode Select bits (Primary Oscillator disabled)
#pragma config OSCIOFNC = OFF // OSC2 Pin Function bit (OSC2 is clock output)
#pragma config IOL1WAY = OFF // Peripheral pin select configuration (Allow multiple reconfigurations)
#pragma config FCKSM = CSECMD // Clock Switching Mode bits (Clock switching is enabled,Fail-safe Clock Monitor is disabled)
#pragma config FNOSC = FRC // Oscillator Source Selection (Internal Fast RC (FRC))
#pragma config PWMLOCK = ON // PWM Lock Enable bit (Certain PWM registers may only be written after key sequence)
#pragma config IESO = OFF // Two-speed Oscillator Start-up Enable bit (Start up with user-selected oscillator source)
#pragma config GWRP = OFF // General Segment Write-Protect bit (General Segment may be written)
#pragma config GCP = OFF // General Segment Code-Protect bit (General Segment Code protect is Disabled)
#include <xc.h>
/* Setup the clock to run at about 60 MIPS */
#define FOSC (7372800L) /* nominal fast RC frequency */
#define PLL_N1 (2L) /* PLLPRE CLKDIV<4:0> range 2 to 33 */
#define PLL_M (65L) /* PLLDIV PLLFBD<8:0> range 2 to 513 */
#define PLL_N2 (2L) /* PLLPOST CLKDIV<7:6> range 2, 4 or 8 */
#define FSYS (FOSC*PLL_M/(PLL_N1*PLL_N2))
#define FCYC (FSYS/2L)
/*
* Global constant data
*/
const unsigned long gInstructionCyclesPerSecond = FCYC;
/*
* Initialize this PIC
*/
void PIC_Init( void )
{
unsigned int ClockSwitchTimeout;
/*
** Disable all interrupt sources
*/
__builtin_disi(0x3FFF); /* disable interrupts for 16383 cycles */
IEC0 = 0;
IEC1 = 0;
IEC2 = 0;
IEC3 = 0;
IEC4 = 0;
IEC5 = 0;
IEC6 = 0;
IEC8 = 0;
IEC9 = 0;
__builtin_disi(0x0000); /* enable interrupts */
CLKDIV = 0; /* Disable DOZE mode */
if(!OSCCONbits.CLKLOCK) /* if primary oscillator switching is unlocked */
{
/* Select primary oscillator as FRC */
__builtin_write_OSCCONH(0b000);
/* Request switch primary to new selection */
__builtin_write_OSCCONL(OSCCON | (1 << _OSCCON_OSWEN_POSITION));
/* wait, with timeout, for clock switch to complete */
for(ClockSwitchTimeout=10000; --ClockSwitchTimeout && OSCCONbits.OSWEN;);
/* Configure PLL prescaler, PLL postscaler, PLL divisor */
PLLFBD=PLL_M-2; /* M=65 */
#if PLL_N2==2
CLKDIVbits.PLLPOST=0; /* N2=2 */
#elif PLL_N2==4
CLKDIVbits.PLLPOST=1; /* N2=4 */
#elif PLL_N2==8
CLKDIVbits.PLLPOST=3; /* N2=8 */
#else
#error invalid PLL_N2 paramenter
#endif
CLKDIVbits.PLLPRE=PLL_N1-2; /* N1=2 */
/* Select primary oscillator as FRCPLL */
__builtin_write_OSCCONH(0b001);
/* Request switch primary to new selection */
__builtin_write_OSCCONL(OSCCON | (1 << _OSCCON_OSWEN_POSITION));
/* wait, with timeout, for clock switch to complete */
for(ClockSwitchTimeout=10000; --ClockSwitchTimeout && OSCCONbits.OSWEN;);
/* wait, with timeout, for the PLL to lock */
for(ClockSwitchTimeout=10000; --ClockSwitchTimeout && !OSCCONbits.LOCK;);
/* at this point the system oscillator should be 119.808MHz */
}
/* make all inputs digital I/O */
ANSELA = 0x00;
ANSELB = 0x00;
ANSELC = 0x00;
/* Unlock PPS Registers */
__builtin_write_OSCCONL(OSCCON & ~(_OSCCON_IOLOCK_MASK));
/* map all PPS pins */
RPINR7bits.IC1R = 35; // Select RP35 (RB3/PGED1) as input for IC1
/* Lock PPS Registers */
__builtin_write_OSCCONL(OSCCON | (_OSCCON_IOLOCK_MASK));
}
/*
* Initialize Input Capture 1
*/
void Init_InputCapture( void )
{
IC1CON1bits.ICSIDL = 0;
IC1CON1bits.ICTSEL = 0b111; // Peripheral clock (FP) is the clock source of the ICx
IC1CON1bits.ICI = 0b00; //
IC1CON1bits.ICM = 0b011; //Capture mode every edge rising
IC1CON2bits.ICTRIG = 0; // = Input source used to trigger the input capture timer (Trigger mode)
IC1CON2bits.SYNCSEL = 0b00000; //IC1 module synchronizes or triggers ICx
IC1CON2bits.IC32 = 0; // 16 bit mode only
// Enable Capture Interrupt And Timer2
IPC0bits.IC1IP = 4; // Setup IC1 interrupt priority level
IFS0bits.IC1IF = 0; // Clear IC1 Interrupt Status Flag
IEC0bits.IC1IE = 1; // Enable IC1 interrupt
}
/*
* Interrupt Service Routine for IC1
*/
void __attribute__((__interrupt__, no_auto_psv)) _IC1Interrupt(void)
{
IFS0bits.IC1IF = 0;
LATBbits.LATB9 ^= 1; // toggle LED
}
/*
* Main process loop
*/
int main( void )
{
PIC_Init();
TRISBbits.TRISB9 = 0; // make RB9 an output
LATBbits.LATB9 = 0; // turn off LED
Init_InputCapture();
for(;;)
{
/* process loop */
}
return 0;
}

STM32 SPI Receive DMA is getting garbage data

In my project, I am using Master SPI communication to get analog data from external ADC. My MCU is STM32F746ZGTX. My system needs to work real time so I used SPI DMA Receive and Transmit functions.
I am reading all external ADC data correctly with SPI polling without using DMA. In SPI polling, I am first sending control byte to external ADC, in that time program is waiting in while(SPI_Ready) loop and then starts to receive all ADC data. This scenario works perfectly.
But I do not want to wait in while(SPI_Ready) loop in my every ADC reading. Because It affects my real time calculations. That's why I switched my functions to DMA.
My new algorithm is like that in below:
Generate External GPIO Interrupt with falling edge trigger to sense data ready output of external ADC.
Make chip select pin low to start communication with external ADC
Send read command to External ADC with HAL_SPI_Transmit_DMA() function.
In HAL_SPI_TxCpltCallback function, trigger HAL_SPI_Receive_DMA()
In HAL_SPI_RxCpltCallback function, buffer received ADC data and make chip select pin high to terminate communication.
When I use this algorithm, I am getting always 0xFF values in my ADC buffer. It seems like even if ADC is not sending raw data, because of triggering receive DMA, My MCU sends clock and sense all logic high signal as a received data.
I am sharing my codes in below. If you have any sugggestion where I am wrong, please share your opinions.
/* SPI1 init function */
static void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
}
}
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(hspi->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspInit 0 */
/* USER CODE END SPI1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_SPI1_CLK_ENABLE();
/**SPI1 GPIO Configuration
PA5 ------> SPI1_SCK
PA6 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* SPI1 DMA Init */
/* SPI1_RX Init */
hdma_spi1_rx.Instance = DMA2_Stream0;
hdma_spi1_rx.Init.Channel = DMA_CHANNEL_3;
hdma_spi1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_spi1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi1_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_spi1_rx.Init.Mode = DMA_NORMAL;
hdma_spi1_rx.Init.Priority = DMA_PRIORITY_LOW;
hdma_spi1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK)
{
}
__HAL_LINKDMA(hspi,hdmarx,hdma_spi1_rx);
/* SPI1_TX Init */
hdma_spi1_tx.Instance = DMA2_Stream3;
hdma_spi1_tx.Init.Channel = DMA_CHANNEL_3;
hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_spi1_tx.Init.Mode = DMA_NORMAL;
hdma_spi1_tx.Init.Priority = DMA_PRIORITY_LOW;
hdma_spi1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK)
{
}
__HAL_LINKDMA(hspi,hdmatx,hdma_spi1_tx);
/* SPI1 interrupt Init */
HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SPI1_IRQn);
/* USER CODE BEGIN SPI1_MspInit 1 */
/* USER CODE END SPI1_MspInit 1 */
/* USER CODE BEGIN SPI1_MspInit 1 */
/* USER CODE END SPI1_MspInit 1 */
}
}
void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi)
{
if(hspi->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspDeInit 0 */
/* USER CODE END SPI1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_SPI1_CLK_DISABLE();
/**SPI1 GPIO Configuration
PA5 ------> SPI1_SCK
PA6 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5|GPIO_PIN_6);
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_5);
/* USER CODE BEGIN SPI1_MspDeInit 1 */
/* Peripheral DMA DeInit*/
HAL_DMA_DeInit(hspi->hdmarx);
HAL_DMA_DeInit(hspi->hdmatx);
/* Peripheral interrupt Deinit*/
HAL_NVIC_DisableIRQ(SPI2_IRQn);
/* USER CODE END SPI1_MspDeInit 1 */
}
}
/* External Interrupt for data ready output of ADC */
void EXTI15_10_IRQHandler(void)
{
/* USER CODE BEGIN EXTI15_10_IRQn 0 */
/* USER CODE END EXTI15_10_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15);
/* USER CODE BEGIN EXTI15_10_IRQn 1 */
adc_selectADC(); /* Make Chip Select pin low */
HAL_SPI_Transmit_DMA (&hspi1, &controlByte, 1);
}
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
if (hspi->Instance == hspi1.Instance)
{
/* Transmit is completed */
/* Trigger receive DMA to get raw data from external ADC */
HAL_SPI_Receive_DMA (&hspi1, (uint8_t*)adcRecBuffer, 24);
}
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
if (hspi->Instance == hspi1.Instance)
{
/* Receive is completed */
adc_deselectADC(); /* Make Chip Select pin high */
}
}
***Working Algorithm without using DMA or Interrupt:***
/* External Interrupt for data ready output of ADC */
void EXTI15_10_IRQHandler(void)
{
/* USER CODE BEGIN EXTI15_10_IRQn 0 */
/* USER CODE END EXTI15_10_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15);
/* USER CODE BEGIN EXTI15_10_IRQn 1 */
adc_selectADC(); /* Make Chip Select pin low */
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
HAL_SPI_Transmit(&hspi1, &controlByte, 1,1);
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
HAL_SPI_Receive(&hspi1, (uint8_t*)adcRecBuffer, 24, 1);
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
adc_deselectADC(); /* Make Chip Select pin high*/
}
SPI normally is full-duplex, meaning that "reading" is actually master generating clocks and transmitting zeroes. In STM HAL implementation, "receive" function just sends the data you have in read buffer. Might be zeroes, might be garbage, which your ADC interprets as some commands and enters some sort of bad state.
Try doing TransmitReceive of 2 25 byte buffers with your command ID first ("control byte"), followed by 24 zero bytes in TX buffer. In response you should get RX buffer of size 25, where first byte can be discarded. This way you need to handle only RXCplt interrupt, where you release ADC CS pin.

dsPIC33EP128MC202 UART receiver doesn't work

I wrote the code for UART communication. TX is working fine, but RX is not working. I have searched a lot but found no solution.
I am transmitting character x to PC with time interval, and I am able see the data. But when transmits data pic is not receiving any thing.
Below are pins used for uart
//PGEC1/AN4/C1IN1+/RPI34/RB2 for receiver
RPINR18bits.U1RXR = 34;
//RP36/RB4 for transmitting data
RPOR1bits.RP36R = 1;
I am able transmit data to pc but PIC doesn't receive any charector from pc.
Thanks in advance
Here is my code.
DSPIC33EP128MC202
/*
* File: newmainXC16.c
* Author:
*
* Created on 27 December, 2017, 4:21 PM
*/
// FICD
#pragma config ICS = PGD3 // ICD Communication Channel Select bits (Communicate on PGEC3 and PGED3)
#pragma config JTAGEN = OFF // JTAG Enable bit (JTAG is disabled)
// FPOR
#pragma config ALTI2C1 = OFF // Alternate I2C1 pins (I2C1 mapped to SDA1/SCL1 pins)
#pragma config ALTI2C2 = OFF // Alternate I2C2 pins (I2C2 mapped to SDA2/SCL2 pins)
#pragma config WDTWIN = WIN25 // Watchdog Window Select bits (WDT Window is 25% of WDT period)
// FWDT
#pragma config WDTPOST = PS32768 // Watchdog Timer Postscaler bits (1:32,768)
#pragma config WDTPRE = PR128 // Watchdog Timer Prescaler bit (1:128)
#pragma config PLLKEN = ON // PLL Lock Enable bit (Clock switch to PLL source will wait until the PLL lock signal is valid.)
#pragma config WINDIS = OFF // Watchdog Timer Window Enable bit (Watchdog Timer in Non-Window mode)
#pragma config FWDTEN = OFF // Watchdog Timer Enable bit (Watchdog timer enabled/disabled by user software)
// FOSC
#pragma config POSCMD = NONE // Primary Oscillator Mode Select bits (Primary Oscillator disabled)
#pragma config OSCIOFNC = ON // OSC2 Pin Function bit (OSC2 is clock output)
#pragma config IOL1WAY = ON // Peripheral pin select configuration (Allow only one reconfiguration)
#pragma config FCKSM = CSDCMD // Clock Switching Mode bits (Both Clock switching and Fail-safe Clock Monitor are disabled)
// FOSCSEL
#pragma config FNOSC = FRCPLL // Oscillator Source Selection (Fast RC Oscillator with divide-by-N with PLL module (FRCPLL) )
#pragma config PWMLOCK = ON // PWM Lock Enable bit (Certain PWM registers may only be written after key sequence)
#pragma config IESO = ON // Two-speed Oscillator Start-up Enable bit (Start up device with FRC, then switch to user-selected oscillator source)
// FGS
#pragma config GWRP = OFF // General Segment Write-Protect bit (General Segment may be written)
#pragma config GCP = OFF // General Segment Code-Protect bit (General Segment Code protect is Disabled)
#include "xc.h"
#include <stdint.h>
#include <p33EP128MC202.h>
#define FP 60000000
#define BAUDRATE 115200
#define BRGVAL ((FP/BAUDRATE)/16)-1
#define DELAY_100uS asm volatile ("REPEAT, #5400"); Nop(); // 100uS delay
int main(void)
{
uint8_t c;
int i,j,a,b;
// Configure Oscillator to operate the device at 60Mhz
// Fosc= Fin*M/(N1*N2), Fcy=Fosc/2
// Fosc= 8M*60/(2*2)=120Mhz for 8M input clock == 8*60/4 = 120/2 = 60
CLKDIVbits.PLLPOST=0; // PLLPOST (N1) 0=/2
while(!OSCCONbits.LOCK); // wait for PLL ready
PLLFBD = 58; // M=60
CLKDIVbits.PLLPOST = 0; // N1=2
CLKDIVbits.PLLPRE = 0; // N2=2
OSCTUN = 0; // Tune FRC oscillator, if FRC is used
/*Initialize the Ports */
TRISA = 0x00;
LATA = 0x0000;
PORTA = 0x0000;
ANSELAbits.ANSA1 = 0;
ANSELAbits.ANSA0 = 0;
__builtin_write_OSCCONL(OSCCON & ~(1<<6));
RPINR18bits.U1RXR = 34;
RPOR1bits.RP36R = 1;
__builtin_write_OSCCONL(OSCCON | (1<<6));
U1MODEbits.STSEL = 0;
U1MODEbits.PDSEL = 0;
U1MODEbits.ABAUD = 0;
U1MODEbits.BRGH = 0;
U1BRG = 30;
U1MODEbits.UARTEN = 1;
IEC0bits.U1TXIE = 0;
U1STAbits.UTXEN = 1;
U1STAbits.URXISEL = 0;
DELAY_100uS;
DELAY_100uS;
DELAY_100uS;
U1TXREG = 'X';
char ReceivedChar;
while(1){
//If data is received send data to TX
if(U1STAbits.URXDA == 1) {
ReceivedChar = U1RXREG + 2;
U1TXREG = 10;
U1TXREG = 13;
}
for(i=0;i<1000;i++){
a = a + 1;
for(j=0;j<2000;j++){
b = b + 1;
}
}
//LED Blink code for programme check
if(c == 0){
c = 1;
}else{
c = 0;
}
LATAbits.LATA0 = c;
LATAbits.LATA1 = c;
U1TXREG = 'x';
U1TXREG = 10;
U1TXREG = 13;
}
}
void __attribute__((interrupt, no_auto_psv)) _U1TXInterrupt( void )
{
IFS0bits.U1TXIF = 0; // Clear TX Interrupt flag
}
void __attribute__((interrupt, no_auto_psv)) _U1RXInterrupt( void )
{
IFS0bits.U1RXIF = 0; // Clear RX Interrupt flag
//cntr++;
U1TXREG='a';
U1TXREG = 10;
U1TXREG = 13;
}
I looked into the code, It works fine for me.
Please check the connection with your peripherals and their pins. If there is an extra connection to that pin remove it and try it again.
RB2 should be an an Input.
TRISB = 0x04;