No Data Receiving form UART with Interrupt, STM32F4, HAL drivers - interrupt

When I send a request across the UART port from the PC (serial monitor) to the STM32F4 discovery board the signal will not be received. The board should normally answer with the same request which was received before (UART mirroring). I used an interrupt (without DMA) to send or to receive a message. In the interrupt service routine, interrupt flag has been set. This flag will be read in the main loop. I am not using a callback function.
Without the interrupt service (with only HAL_UART_Transmit(...) and HAL_UART_Receive(...) in the main loop) everything works fine. But I want a communication with interrupt.
In the RUN- Mode I have enable breakpoints in the ISR and the two if statements.
I would like to know, whether there is an issue with the ISR routine. But the ISR Routine works as it should be. By a request from the PC the ISR and the receive if statement is called.
The Problem is, that the receive register stay empty. And if this remains empty the controller will not send the request message.
What is wrong? Where is the problem and can you please help me? Is the configuration of the UART Port right?
Thanks for your help & support!
volatile enum RxStates {
UART_RX, DUMMY_RX, WAIT_RX
} stateRx;
volatile enum TxStates {
UART_TX_READY, DUMMY_TX, WAIT_TX
} stateTx;
static UART_HandleTypeDef s_UARTHandle;
GPIO_InitTypeDef GPIOC2_InitStruct; //GPIO
uint8_t empfang[8]; //buffer
void UART_ini(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__USART2_CLK_ENABLE();
//PIN TX
GPIOC2_InitStruct.Pin = GPIO_PIN_2;
GPIOC2_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIOC2_InitStruct.Alternate = GPIO_AF7_USART2;
GPIOC2_InitStruct.Speed = GPIO_SPEED_FAST;
GPIOC2_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIOC2_InitStruct);
//PIN RX
GPIOC2_InitStruct.Pin = GPIO_PIN_3;
GPIOC2_InitStruct.Mode = GPIO_MODE_AF_OD;
HAL_GPIO_Init(GPIOA, &GPIOC2_InitStruct);
//USART2
s_UARTHandle.Instance = USART2;
s_UARTHandle.Init.BaudRate = 9600;
s_UARTHandle.Init.WordLength = UART_WORDLENGTH_8B;
s_UARTHandle.Init.StopBits = UART_STOPBITS_1;
s_UARTHandle.Init.Parity = UART_PARITY_NONE;
s_UARTHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
s_UARTHandle.Init.Mode = UART_MODE_TX_RX;
HAL_UART_Init(&s_UARTHandle);
__HAL_UART_ENABLE_IT(&s_UARTHandle, UART_IT_RXNE | UART_IT_TC);
HAL_NVIC_SetPriority(USART2_IRQn, 15, 15);
HAL_NVIC_EnableIRQ(USART2_IRQn); // Enable Interrupt
}//UART
int main(int argc, char* argv[])
{
//initialization of the interrupt flags
stateRx = WAIT_RX;
stateTx = WAIT_TX;
UART_ini(); //initialization UART
while (1)
{
//receive interrupt flag
if (stateRx == UART_RX)
{
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_SET); //set LED
HAL_UART_Receive(&s_UARTHandle, empfang, 2, 10000000); //receive message
stateRx = WAIT_RX; //RESET flag
}
//transmit interrupt flag
if (stateTx == UART_TX_READY)
{
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_SET); //set LED
HAL_UART_Transmit(&s_UARTHandle, empfang, 2, 10000); //send message
stateTx = WAIT_TX; //RESET flag
}
//RESET LED
if (stateTx != UART_TX_READY && stateRx != UART_RX)
{
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, RESET); //RESET LED
}
}//while
}//main
void USART2_IRQHandler()
{
if (__HAL_UART_GET_FLAG(&s_UARTHandle, UART_FLAG_RXNE) == SET)
{
__HAL_UART_CLEAR_FLAG(&s_UARTHandle, UART_FLAG_RXNE); //clear ISR flag
stateRx = UART_RX; //set RX flag
}
if (__HAL_UART_GET_FLAG(&s_UARTHandle, UART_FLAG_TC) == SET)
{
__HAL_UART_CLEAR_FLAG(&s_UARTHandle, UART_FLAG_TC); //clear ISR flag
stateTx = UART_TX_READY; //set TX flag
}
}//ISR
//EOF

Look into HAL_UART_Receive function, this function waits for UART_FLAG_RXNE flag but you clear this flag in USART2_IRQHandler
Usually, when you use RX interrupt, you have to save received data into user buffer and then parse it.

Related

ZYNQ-7000 UART issue - no interrupt raised when receiving data from PC

I'm trying to UART transceiver on my ZYNQ-7000 board using interrupts. Basically, it just take data from the serial terminal and send back to it.
After initialization, a message Uart Initialization Successful! is sent to and shown on the terminal which confirms that the ZYNQ can send data to the PC. But whenever I input some random letters, it seems that interrupt handler function, called void Handler(), was never called. It seems there is no interrupt generated at all. I've looked up a lot but cannot slove the problem. Could any one please help me with this, please.
Here is my code,
#include "xparameters.h"
#include "xuartps.h"
#include "xil_printf.h"
#include "xscugic.h"
#include "stdio.h"
// serial device ID
#define UART_DEVICE_ID XPAR_PS7_UART_1_DEVICE_ID
// interrupt ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
// serial port interrupt id
#define UART_INT_IRQ_ID XPAR_XUARTPS_1_INTR
// interrupt controller driver instance
XScuGic Intc;
// serial port driver instance
XUartPs Uart_Ps;
// data buffer size
#define MAX_LEN 512
u8 ReceivedBuffer[MAX_LEN];
volatile u32 ReceivedByteNum;
XUartPsFormat UartFormat = {
115200,
XUARTPS_FORMAT_8_BITS,
XUARTPS_FORMAT_NO_PARITY,
XUARTPS_FORMAT_1_STOP_BIT
};
// function declaration
int UartInit(XUartPs *uart_ps);
// interrupt handler
void Handler(void *call_back_ref);
int UartIntrInit(XScuGic *intc, XUartPs *uart_ps);
// main function
int main(void){
int status;
// initialize the serial port
status = UartInit(&Uart_Ps);
if(status == XST_FAILURE){
xil_printf("Uart Initialization Failed\r\n");
return XST_FAILURE;
}
// interrupt initialization
status = UartIntrInit(&Intc, &Uart_Ps);
if(status == XST_FAILURE){
xil_printf("Uart Initialization Failed\r\n");
return XST_FAILURE;
}
xil_printf("Uart Initialization Successful!\r\n");
// main loop
while (1) {};
return status;
}
int UartInit(XUartPs *uart_ps){
int status;
XUartPs_Config *uart_cfg;
uart_cfg = XUartPs_LookupConfig(UART_DEVICE_ID);
if(NULL == uart_cfg) return XST_FAILURE;
status = XUartPs_CfgInitialize(uart_ps, uart_cfg, uart_cfg->BaseAddress);
if(status != XST_SUCCESS) return XST_FAILURE;
// UART self test
status = XUartPs_SelfTest(uart_ps);
if(status != XST_SUCCESS) return XST_FAILURE;
XUartPs_SetOperMode(uart_ps, XUARTPS_OPER_MODE_NORMAL);
XUartPs_SetDataFormat(uart_ps, &UartFormat);
XUartPs_SetFifoThreshold(uart_ps, 32);
XUartPs_SetRecvTimeout(uart_ps, 8);
return XST_SUCCESS;
};
// UART Interrupt handler service
void Handler(void *call_back_ref){
xil_printf("Enter INTR\r\n");
XUartPs *uart_instance_ptr = (XUartPs *) call_back_ref;
u32 ReceivedCount = 0;
u32 IsrValue;
IsrValue = XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_IMR_OFFSET);
IsrValue &= XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_ISR_OFFSET);
// if interrupt is asserted
if( IsrValue & ((u32) XUARTPS_IXR_RXOVR) ){
XUartPs_WriteReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_ISR_OFFSET, XUARTPS_IXR_RXOVR);
ReceivedCount = XUartPs_Recv(&Uart_Ps, ReceivedBuffer, MAX_LEN);
ReceivedByteNum += ReceivedCount;
}
else if( IsrValue & ((u32) XUARTPS_IXR_TOUT) ){
// Rx FIFO timeout / idle
XUartPs_WriteReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_ISR_OFFSET, XUARTPS_IXR_TOUT);
ReceivedCount = XUartPs_Recv(&Uart_Ps, ReceivedBuffer, MAX_LEN);
ReceivedByteNum += ReceivedCount;
// send out
for(u32 sendByte=0;sendByte<ReceivedByteNum;sendByte++){
XUartPs_SendByte(XPAR_PS7_UART_1_BASEADDR, ReceivedBuffer[sendByte]);
}
ReceivedByteNum = 0;
}
}
// UART Interrupt init
int UartIntrInit(XScuGic *intc, XUartPs *uart_ps){
int status;
// initialize the interrupt controller
XScuGic_Config *intc_cfg;
intc_cfg = XScuGic_LookupConfig(INTC_DEVICE_ID);
if(NULL == intc_cfg) return XST_FAILURE;
status = XScuGic_CfgInitialize(intc, intc_cfg, intc_cfg->CpuBaseAddress);
if(status != XST_SUCCESS) return XST_FAILURE;
// set and enable interrupt exception handle function
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler, (void *) intc);
// set interrupt handler for interrupt
XScuGic_Connect(intc, UART_INT_IRQ_ID, (Xil_ExceptionHandler) Handler, (void *) uart_ps);
// set interrupt trigger mode
XUartPs_SetInterruptMask(uart_ps, XUARTPS_IXR_RXOVR | XUARTPS_IXR_TOUT);
Xil_ExceptionEnable();
XScuGic_Enable(intc, UART_INT_IRQ_ID);
return XST_SUCCESS;
}
I'm using UART1 controller at MIO 48, 49 which is confirmed at both the Vivado and the schematic.
There are few problems of your program.
Receive function
It seems you have not called the UART receive function (XUartPs_Recv).
In interrupt mode, the UART controller will start receiving after you called XUartPs_Recv, this function is non-blocking. When all data received, the UART controller will generate an interrupt, and all data has been written to the receive buffer by the interrupt handler function.
XUartPs_Recv(uart_ps, RecvBuffer, SIZE_IN_BYTE);
Interrupt Handler
UARTPS library provided the interrupt handler function (XUartPs_InterruptHandler). So you need to bind it to the XScuGic
XScuGic_Connect(intc, UART_INT_IRQ_ID,
(XInterruptHandler) XUartPs_InterruptHandler, uart_ps);
This interrupt handler can help you receive the data from UART FIFO and write to the receive buffer.
Custom Callback
If you want to do something when the interrupt occoured, you don't need to write a new handler function by yourself, but a Callback function is needed.
XUartPs_SetHandler(uart_ps, (XUartPs_Handler)Handler, uart_ps);
Use this function, your custom handler will be called from the
interrupt context (XUartPs_InterruptHandler) when data has been sent or received.
Addition
You may set the receiver timeout. If it is not set, and the last few bytes of data do not trigger the over-water or full interrupt, the bytes will not be received. By default it is disabled.
XUartPs_SetRecvTimeout(uart_ps, 8);
Reference
Please refer the official example by Xilinx on GitHub.

UART Transmit failing after UART Receive thread starts in STM32 HAL Library

I am using STM32F1 (STM32F103C8T6) in order to develop a project using FreeRTOS.
The following is my GPIO and USART1 interface configuration:
__GPIOA_CLK_ENABLE();
__USART1_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;//115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
HAL_UART_Init(&huart1);
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
The question is: Why does UART transmit work before threads start but not after threads started or from threads? I want to transmit data from threads. i.e
int main(void)
{
Initializations();
//THIS WORKS!!
uart_transmit_buffer[0] = 'H';
uart_transmit_buffer[1] = 'R';
uart_transmit_buffer[2] = '#';
uint8_t nums_in_tr_buf = 0;
nums_in_tr_buf = sizeof(uart_transmit_buffer)/sizeof(uint8_t);
state = HAL_UART_Transmit(&huart1, uart_transmit_buffer, nums_in_tr_buf, 5000);
StartAllThreads();
osKernelStart();
for (;;);
}
static void A_Random_Thread(void const *argument)
{
for(;;)
{
if (conditionsMet()) //Executed once when a proper response received.
{
//BUT NOT THIS :(!!
uart_transmit_buffer[0] = 'H';
uart_transmit_buffer[1] = 'R';
uart_transmit_buffer[2] = '#';
uint8_t nums_in_tr_buf = 0;
nums_in_tr_buf = sizeof(uart_transmit_buffer)/sizeof(uint8_t);
state = HAL_UART_Transmit(&huart1, uart_transmit_buffer, nums_in_tr_buf, 5000);
}
}
}
I have made sure no thread is in deadlock. The problem is UART_HAL_Transmit gives HAL_BUSY state.
Furthermore, I have dedicated one thread to receiving and parsing information from UART RX and I suspect this might be a cause of the problem. The following is the code:
static void UART_Receive_Thread(void const *argument)
{
uint32_t count;
(void) argument;
int j = 0, word_length = 0;
for (;;)
{
if (uart_line_ready == 0)
{
HAL_UART_Receive(&huart1, uart_receive_buffer, UART_RX_BUFFER_SIZE, 0xFFFF);
if (uart_receive_buffer[0] != 0)
{
if (uart_receive_buffer[0] != END_OF_WORD_CHAR)
{
uart_line_buffer[k] = uart_receive_buffer[0];
uart_receive_buffer[0] = 0;
k++;
}
else
{
uart_receive_buffer[0] = 0;
uart_line_ready = 1;
word_length = k;
k = 0;
}
}
}
if (uart_line_ready == 1)
{
//osThreadSuspend(OLEDThreadHandle);
for (j = 0; j <= word_length; j++)
{
UART_RECEIVED_COMMAND[j] = uart_line_buffer[j];
}
for (j = 0; j <= word_length; j++)
{
uart_line_buffer[j] = 0;
}
uart_line_ready = 0;
RECEIVED_COMMAND = ParseReceivedCommand(UART_RECEIVED_COMMAND);
if (RECEIVED_COMMAND != _ID_)
{
AssignReceivedData (word_length); //Results in uint8_t * RECEIVED_DATA
}
//osThreadResume(OLEDThreadHandle);
}
//Should be no delay in order not to miss any data..
}
}
Another cause to the problem I suspect could be related to interrupts of the system (Also please notice initialization part, I configured NVIC):
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart1);
}
Any help or guidance to this issue would be highly appreciated. Thanks in advance.
From what I can see HAL_UART_Transmit would've worked with the F4 HAL (v1.4.2) if it weren't for __HAL_LOCK(huart). The RX thread would lock the handle and then the TX thread would try to lock as well and return HAL_BUSY. HAL_UART_Transmit_IT and HAL_UART_Receive_IT don't lock the handle for the duration of the transmit/receive.
Which may cause problems with the State member, as it is non-atomically updated by the helper functions UART_Receive_IT and UART_Transmit_IT. Though I don't think it would affect operation.
You could modify the function to allow simultaneous RX and TX. You'd have to update this every time they release a new version of the HAL.
The problem is that the ST HAL isn't meant to be used with an RTOS. It says so in the definition of the macro __HAL_LOCK. Redefining it to use the RTOS's mutexes might worth trying as well. Same with HAL_Delay() to use the RTOS's thread sleep function.
In general though, sending via a blocking function in a thread should be fine, but I would not receive data using a blocking function in a thread. You are bound to experience overrun errors that way.
Likewise, if you put too much processing in the receive interrupt you might also run into overrun errors. I prefer using DMA for reception, followed by interrupts if I've run out of DMA streams. The interrupt only copies the data to a buffer, similarly to the DMA. A processRxData thread is then used to process the actual data.
When using FreeRTOS, you have to set interrupt priority to 5 or above, because below 5 is reserved for the OS.
So change your code to set the priority to:
HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
The problem turned out to be something to do with blocking statements.
Since UART_Receive_Thread has HAL_UART_Receive inside and that is blocking the thread until something is received, that results in a busy HAL (hence, the HAL_BUSY state).
The solution was using non-blocking statements without changing anything else.
i.e. using HAL_UART_Receive_IT and HAL_UART_Transmit_IT at the same time and ignoring blocking statements worked.
Thanks for all suggestions that lead to this solution.

STM32F4: SD-Card using FatFs and USB fails

(also asked on SE: Electrical Engineering)
In my application, I've set up a STM32F4, SD-Card and USB-CDC (all with CubeMX).
Using a PC, I send commands to the STM32, which then does things on the SD-Card.
The commands are handled using a "communicationBuffer" (implemented by me) which waits for commands over USB, UART, ... and sets a flag, when a \n character was received. The main loop polls for this flag and if it is set, a parser handles the command. So far, so good.
When I send commands via UART, it works fine, and I can get a list of the files on the SD-Card or perform other access via FatFs without a problem.
The problem occurs, when I receive a command via USB-CDC. The parser works as expected, but FatFs claims FR_NO_FILESYSTEM (13) in f_opendir.
Also other FatFs commands fail with this error-code.
After one failed USB-command, commands via UART will also fail. It seems, as if the USB somehow crashes the initialized SD-Card-driver.
Any idea how I can resolve this behaviour? Or a starting point for debugging?
My USB-Implementation:
I'm using CubeMX, and therefore use the prescribed way to initialize the USB-CDC interface:
main() calls MX_USB_DEVICE_Init(void).
In usbd_conf.c I've got:
void HAL_PCD_MspInit(PCD_HandleTypeDef* pcdHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(pcdHandle->Instance==USB_OTG_FS)
{
/* USER CODE BEGIN USB_OTG_FS_MspInit 0 */
/* USER CODE END USB_OTG_FS_MspInit 0 */
/**USB_OTG_FS GPIO Configuration
PA11 ------> USB_OTG_FS_DM
PA12 ------> USB_OTG_FS_DP
*/
GPIO_InitStruct.Pin = OTG_FS_DM_Pin|OTG_FS_DP_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* Peripheral clock enable */
__HAL_RCC_USB_OTG_FS_CLK_ENABLE();
/* Peripheral interrupt init */
HAL_NVIC_SetPriority(OTG_FS_IRQn, 7, 1);
HAL_NVIC_EnableIRQ(OTG_FS_IRQn);
/* USER CODE BEGIN USB_OTG_FS_MspInit 1 */
/* USER CODE END USB_OTG_FS_MspInit 1 */
}
}
and the receive-process is implemented in usbd_cdc_if.c as follows:
static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
{
/* USER CODE BEGIN 6 */
mRootObject->mUsbBuffer->fillBuffer(Buf, *Len);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
return (USBD_OK);
/* USER CODE END 6 */
}
fillBuffer is implemented as follows (I use the same implementation for UART and USB transfer - with separate instances for the respective interfaces. mBuf is an instance-variable of type std::vector<char>):
void commBuf::fillBuffer(uint8_t *buf, size_t len)
{
// Check if last fill has timed out
if(SystemTime::getMS() - lastActionTime > timeout) {
mBuf.clear();
}
lastActionTime = SystemTime::getMS();
// Fill new content
mBuf.insert(mBuf.end(), buf, buf + len);
uint32_t done = 0;
while(!done) {
for(auto i = mBuf.end() - len, ee = mBuf.end(); i != ee; ++i) {
if(*i == '\n') {
newCommand = true;
myCommand = std::string((char*) &mBuf[0],i - mBuf.begin() + 1);
mBuf.erase(mBuf.begin(), mBuf.begin() + (i - mBuf.begin() + 1));
break;
}
}
done = 1;
}
}
I resolved the problem:
In usb_cdc_if.c the #define APP_RX_DATA_SIZE was set to 4 (for some unknown reason). As this is lower than the packet size, incoming packets of a larger size than 4 bytes were overwriting my memory.
It happened, that the following portion of my memory was the FATFS* FatFs[] pointer-list to the initialized FATFS-Filesystem structs.
So subsequently the address to this struct was overwritten, when a command of 5 or more bytes arrived.
Phew, that was a tough one.

How to setup an interrupt driven SPI with stm32F4

I'am using STM32F4 board with CMSIS library and I want setup an interrupt driven SPI, it means an interrupt is triggered each time a byte is sent by the SPI peripheral. The initiaisation function is as below:
void init_SPI1(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStruct;
SPI_InitTypeDef SPI_InitStruct;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5|GPIO_Pin_4;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// connect SPI1 pins to SPI alternate function
//GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
//Set chip select high
GPIOA->BSRRL |= GPIO_Pin_4; // set PE4 high
// enable peripheral clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
/* configure SPI1 in Mode 0
* CPOL = 0 --> clock is low when idle
* CPHA = 0 --> data is sampled at the first edge
*/
SPI_StructInit(&SPI_InitStruct); // set default settings
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // set to full duplex mode, seperate MOSI and MISO lines
SPI_InitStruct.SPI_Mode = SPI_Mode_Master; // transmit in master mode, NSS pin has to be always high
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // one packet of data is 8 bits wide
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; // clock is low when idle
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; // data sampled at first edge
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft ; // set the NSS management to internal and pull internal NSS high
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; // SPI frequency is APB2 frequency / 4
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;// data is transmitted MSB first
SPI_Init(SPI1, &SPI_InitStruct);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = SPI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Enable SPI1*/
SPI_Cmd(SPI1, ENABLE);
return;
}
Then i just loopback SPI_MOSI to SPI_MISO and use a function that transmit the data (a very basic function that takes data from a buffer and then uses CMSIS functions for the transmission). The problem is that when the SPI interrupt is triggered, the program won't get out from the handler. the handler function looks lihe this:
void SPI1_IRQHandler()
{
int a;
a++;
SPI_I2S_ClearITPendingBit(SPI1,SPI_I2S_IT_TXE);
return;
}
Is it a problem in the CMSIS library, or I am not configuring the SPI interrupt in the good way? Please guide me to the right point.
EDIT
This is the function i use for data transmission
void write_SPI1()
{
int i;
for (i=0;i<SPI_TX_MAX; i++)
{
SPI_I2S_SendData(SPI1,spiTxBuff[i]);
SPI_I2S_ITConfig(SPI1,SPI_I2S_IT_RXNE,ENABLE);
}
}
and the interruption deals with the data reception, it just fill spiRxBuff when receiving new data.
void SPI1_IRQHandler()
{
while (SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)== RESET);
spiRxBuff[spiRxCount]= SPI_I2S_ReceiveData(SPI1);
spiRxCount++;
}
The variable used for Reception / Transmission are declared as below :
uint8_t spiTxBuff[SPI_TX_MAX] = {0x01,0x02,0x03,0x04,0x05,0x06};
uint8_t spiRxBuff[SPI_RX_MAX];
static volatile int spiRxCount= 0; // used in SPI1_IRQHandler
what is strange now is that i'am having {0x01,0x02,0x03,0x05,0x06} in spiRxBuff instead of {0x01,0x02,0x03,0x04,0x05,0x06}, but using debug mode the data in spiRxBuff are correct, what goes wrong in your opinion ?
You did not show the function doing the transmit, so I don't know exactly what are you trying to accomplish
Transmitting in a loop
If you are transmitting from a function (in a loop), then you don't need interrupts at all, just make sure that the TXE flag is set before you transmit. Note that you have to interleave sending and receiving somehow.
void SPI1_Transmit(uint8_t *send, uint8_t *receive, int count) {
while(count-- > 0) {
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE)!=SET) {
if(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE)==SET)
*receive++ = SPI_I2S_ReceiveData(SPI1);
}
SPI_I2S_SendData(SPI1, *send++);
}
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE)!=SET) {
/* wait for the last incoming byte */
}
*receive++ = SPI_I2S_ReceiveData(SPI1);
}
Transmitting from interrupt
The TXE interrupt flag is set as long as the SPI device is not busy sending. If you don't do something about it in the interrupt handler, it will trigger an interrupt immediately again and again. You can't clear it manually, but by transmitting another byte, and resetting the transmit interrupt enable flag before sending the last byte.
volatile int spi1_tx_count, spi1_rx_count;
uint8_t *spi1_tx_ptr;
volatile uint8_t *spi1_rx_ptr;
/* set these global variables before enabling interrupts */
void SPI1_IRQHandler() {
if (SPI_I2S_GetITStatus(SPI1, SPI_I2S_IT_TXE) == SET) {
if(--spi1_tx_count < 1)
SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_TXE, DISABLE);
SPI_I2S_SendData(SPI1, *spi1_tx_ptr++);
}
if(SPI_I2S_GetITStatus(SPI1, SPI_I2S_IT_RXNE) == SET) {
*spi_rx_ptr++ = SPI_I2S_ReceiveData(SPI1);
spi1_rx_count++;
}
}
Using DMA
The above examples are using processor power and cycles for a task that can be handled by the DMA conroller alone. A lot of (if not all) processor cycles, if you are talking to a peripheral at 2 MBit/s.
See Project/STM32F4xx_StdPeriph_Examples/SPI/SPI_TwoBoards in the library for an example.
Sorry, I haven't noticed at all that you've amended the question. Look like notifications are sent on new comments or answers, but not on edits.
There are multiple problems with your code. In write_SPI1(), I'd enable RX interrupt only once before the loop, there is no need to do it again and again. Also, you should definitely check whether the TX register is available before sending.
void write_SPI1() {
int i;
SPI_I2S_ITConfig(SPI1,SPI_I2S_IT_RXNE,ENABLE);
for (i=0;i<SPI_TX_MAX; i++) {
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE)!=SET)
;
SPI_I2S_SendData(SPI1,spiTxBuff[i]);
}
}
It is however a bad idea to wait on a flag in the interrupt handler. If RXNE is the only possible interrupt source, then you can proceed straight to receiving.

No interrupts being triggered in UART Receive on PIC18F2680

I have been working with this code for days and cannot figure out why my interrupts are not being triggered. I know data is coming through successfully because I used a probe on a logic analyzer, also my baud rate is correct as I can transmit with UART successfully.
At this point I'm lost, I've read the datasheet over and over and can't figure out my problem. I will try to include only the relative code but enough that you can see how things work in my project.
Please let me know if you see issues with this code.
Thank you!
Code snippets from main.c:
// USART RX interrupt priority
IPR1bits.RCIP = 0;
IPR1bits.TXIP = 0;
// configure the hardware USART device
OpenUSART(USART_TX_INT_OFF & USART_RX_INT_ON & USART_ASYNCH_MODE & USART_EIGHT_BIT &
USART_CONT_RX & USART_BRGH_LOW, 14);
Code snippets from interrupts.c
//----------------------------------------------------------------------------
// Low priority interrupt routine
// this parcels out interrupts to individual handlers
#pragma code
#pragma interruptlow InterruptHandlerLow
// This works the same way as the "High" interrupt handler
void InterruptHandlerLow() {
// check to see if we have an interrupt on USART RX
if (PIR1bits.RCIF) {
PIR1bits.RCIF = 0; //clear interrupt flag
uart_recv_int_handler();
}
// check to see if we have an interrupt on USART TX
if (PIR1bits.TXIF && PIE1bits.TXIE) {
// cannot clear TXIF, this is unique to USART TX
// so just call the handler
uart_tx_int_handler();
}
}
UART RX Interrupt Handler snippet:
void uart_recv_int_handler() {
int msgLen;
//if (DataRdyUSART()) {
uc_ptr->buffer[uc_ptr->buflen] = RCREG;
//uc_ptr->buffer[uc_ptr->buflen] = ReadUSART();
uc_ptr->buflen++;
}
}
Did you
- Set trisC6/7 correctly?
- if you have a part with analog inputs multiplexed on those pins, did you disable them?
- Is your BRG value validated for this part and these oscillator settings?
See also
http://www.piclist.com/techref/microchip/rs232.htm
I migrated to dspic, but I used to do the serial receive under interrupt. This I had in the interrupt (serialin1 is a power of two circular buffer, lastserialin1 the pointer into it, and ser1bufinmask is size of buffer-1)
if (PIR1bits.RCIF == 1) /* check if RC interrupt (receive USART) must be serviced
{
while (PIR1bits.RCIF == 1) /* flag becomes zero if buffer/fifo is empty */
{
lastserialin1=(lastserialin1+1)&ser1bufinmask;
serialin1[lastserialin1]=RCREG;
}
}
To initialize the uart I had:
// Configure USART
TXSTA = 0x20; // transmit enable
RCSTA = 0x90; // spen en cren
RCONbits.IPEN = 1; /* Interrupt Priority Enable Bit. Enable priority levels on interrupts */
INTCONbits.GIE = 1; /* Set GIE. Enables all high priority unmasked interrupts */
INTCONbits.GIEL = 1; /* Set GIEL. Enables all low priority unmasked interrupts */
TRISCbits.TRISC6 = 0; // page 237
TRISCbits.TRISC7 = 1; // page 237
Open1USART (
USART_TX_INT_OFF
&
USART_RX_INT_ON &
USART_ASYNCH_MODE &
USART_EIGHT_BIT & // 8-bit transmit/receive
USART_CONT_RX & // Continuous reception
// USART_BRGH_HIGH, 155); // High baud rate, 155 eq 19k2
USART_BRGH_HIGH, brgval); // High baud rate, 25 eq 115k2
IPR1bits.RCIP = 0;
PIR1bits.RCIF = 0;
with brgval calculated using
#define GetInstructionClock() (GetSystemClock()/4)
#define GetPeripheralClock() GetInstructionClock()
// See if we can use the high baud rate setting
#if ((GetPeripheralClock()+2*BAUD_RATE)/BAUD_RATE/4 - 1) <= 255
#define BRGVAL ((GetPeripheralClock()+2*BAUD_RATE)/BAUD_RATE/4 - 1)
#define BRGHVAL (1)
#else // Use the low baud rate setting
#define BRGVAL ((GetPeripheralClock()+8*BAUD_RATE)/BAUD_RATE/16 - 1)
#define BRGHVAL (0)
#endif