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.
Related
I'm trying to write 4 uint32's of data into the flash memory of my STM32F767ZI so I've looked at some examples and in the reference manual but still I cannot do it. My goal is to write 4 uint32's into the flash and read them back and compare with the original data, and light different leds depending on the success of the comparison.
My code is as follows:
void flash_write(uint32_t offset, uint32_t *data, uint32_t size) {
FLASH_EraseInitTypeDef EraseInitStruct = {0};
uint32_t SectorError = 0;
HAL_FLASH_Unlock();
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
EraseInitStruct.Sector = FLASH_SECTOR_11;
EraseInitStruct.NbSectors = 1;
//EraseInitStruct.Banks = FLASH_BANK_1; // or FLASH_BANK_2 or FLASH_BANK_BOTH
st = HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError);
if (st == HAL_OK) {
for (int i = 0; i < size; i += 4) {
st = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, FLASH_USER_START_ADDR + offset + i, *(data + i)); //This is what's giving me trouble
if (st != HAL_OK) {
// handle the error
break;
}
}
}else {
// handle the error
}
HAL_FLASH_Lock();
}
void flash_read(uint32_t offset, uint32_t *data, uint32_t size) {
for (int i = 0; i < size; i += 4) {
*(data + i) = *(__IO uint32_t*)(FLASH_USER_START_ADDR + offset + i);
}
}
int main(void) {
uint32_t data[] = {'a', 'b', 'c', 'd'};
uint32_t read_data[] = {0, 0, 0, 0};
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
flash_write(0, data, sizeof(data));
flash_read(0, read_data, sizeof(read_data));
if (compareArrays(data,read_data,4))
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7,SET);
}
else
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14,SET);
}
return 0;
}
The problem is that before writing data I must erase a sector, and when I do it with the HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError), function, the program always crashes, and sometimes even corrupts my codespace forcing me to update firmware.
I've selected the sector farthest from the code space but still it crashes when i try to erase it.
I've read in the reference manual that
Any attempt to read the Flash memory while it is being written or erased, causes the bus to
stall. Read operations are processed correctly once the program operation has completed.
This means that code or data fetches cannot be performed while a write/erase operation is
ongoing.
which I believe means the code should ideally be run from RAM while we operate on the flash, but I've seen other people online not have this issue so I'm wondering if that's the only problem I have. With that in mind I wanted to confirm if this is my only issue, or if I'm doing something wrong?
In your loop, you are adding multiples of 4 to i, but then you are adding i to data. When you add to a pointer it is automatically multiplied by the size of the pointed type, so you are adding multiples of 16 bytes and reading past the end of your input buffer.
Also, make sure you initialize all members of EraseInitStruct. Uncomment that line and set the correct value!
I'm actually trying to receive serial line message using the UART0 of a CC1310 with Contiki-ng.
I have implemented a uart callback function with will group all the characters received into a single variable, and stop collecting when it receives a "\n" character.
int uart_handler(unsigned char c)
{
if (c == '\n')
{
end_of_string = 1;
index = 0;
}
else
{
received_message_from_uart[index] = c;
index++;
}
return 0;
}
The uart0 is initialised at the main and only process of the system and then it waits in a infinite while loop until the flag end_of_string
PROCESS_THREAD(udp_client_process, ev, data)
{
PROCESS_BEGIN();
uart0_init();
uart0_set_callback(uart_handler);
while (1)
{
//wait for uart message
if (end_of_string == 1 && received_message_from_uart[0] != 0)
{
LOG_INFO("Received mensaje\n");
index = 0;
end_of_string = 0;
// Delete received message from uart
memset(received_message_from_uart, 0, sizeof received_message_from_uart);
}
etimer_set(&timer, 0.1 * CLOCK_SECOND);
PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&timer));
}
PROCESS_END();
}
As you can see, the while loop stops at each iteration with a minimum time timer event. Although this method works, I think it is a bad way to do it, as I have read that there are uart events that can be used, but I have not been able to find anything for the CC1310.
Is it possible to use the CC1310 (or any other simplelink platform) UART with events and stop doing unnecessary iterations until a message has reached the device?
I am using a NUCLEO-L476RG development board,
I am learning to write GPIO drivers for STM32 family
I have implementing a simple logic in which I need to turn on an LED when a push button is pressed.
I have a strange issue:
Edit 1:The Bread board LED turns ON when the line temp=10 is commented, it doesn't turn ON when the delay issue called. Assuming if I add any line of code into that while loop the LED does not turn ON
The Bread board LED turns ON when the delay() function is commented, it doesn't turn ON when the delay issue called.
What could be the issue?
I have powered the board using the mini usb connector on the board, and the clock is configured at MSI with 4MHz
#define delay() for(uint32_t i=0; i<=50000; i++);
int main(void)
{
GPIO_Handle_t NucleoUserLED,NucleoUserPB,BreadBoardLED,BreadBoardPB;
uint8_t inputVal,BBinpVal;
uint32_t temp;
//User green led in the nucleo board connected to PA5
NucleoUserLED.pGPIO = GPIOA;
NucleoUserLED.GPIO_Pin_Cfg.GPIO_PinNumber = GPIO_PIN_5;
NucleoUserLED.GPIO_Pin_Cfg.GPIO_PinMode = GPIO_MODE_OP;
NucleoUserLED.GPIO_Pin_Cfg.GPIO_PinPuPdControl = GPIO_IP_NO_PUPD;
NucleoUserLED.GPIO_Pin_Cfg.GPIO_PinOpType = GPIO_OP_TYPE_PP;
//User blue button in the nucleo connected to PC13
NucleoUserPB.pGPIO = GPIOC;
NucleoUserPB.GPIO_Pin_Cfg.GPIO_PinNumber = GPIO_PIN_13;
NucleoUserPB.GPIO_Pin_Cfg.GPIO_PinMode = GPIO_MODE_IP;
NucleoUserPB.GPIO_Pin_Cfg.GPIO_PinPuPdControl = GPIO_IP_NO_PUPD;
//User led in the bread board connected to PC8
BreadBoardLED.pGPIO = GPIOC;
BreadBoardLED.GPIO_Pin_Cfg.GPIO_PinNumber = GPIO_PIN_8;
BreadBoardLED.GPIO_Pin_Cfg.GPIO_PinMode = GPIO_MODE_OP;
BreadBoardLED.GPIO_Pin_Cfg.GPIO_PinPuPdControl = GPIO_IP_NO_PUPD;
BreadBoardLED.GPIO_Pin_Cfg.GPIO_PinOpType = GPIO_OP_TYPE_PP;
//User DPDT connected in the breadboard connected to PC6
BreadBoardPB.pGPIO = GPIOC;
BreadBoardPB.GPIO_Pin_Cfg.GPIO_PinNumber = GPIO_PIN_6;
BreadBoardPB.GPIO_Pin_Cfg.GPIO_PinMode = GPIO_MODE_IP;
BreadBoardPB.GPIO_Pin_Cfg.GPIO_PinPuPdControl = GPIO_IP_PU;
GPIO_PeriClkCtrl(GPIOA, ENABLE);
GPIO_PeriClkCtrl(GPIOC, ENABLE);
GPIO_Init(&NucleoUserLED);
GPIO_Init(&NucleoUserPB);
GPIO_Init(&BreadBoardLED);
GPIO_Init(&BreadBoardPB);
while(1)
{
/*****************************************************************
* Controlling the IO present in the nucleo board *
*****************************************************************/
inputVal = GPIO_ReadInputPin(NucleoUserPB.pGPIO, NucleoUserPB.GPIO_Pin_Cfg.GPIO_PinNumber);
BBinpVal = GPIO_ReadInputPin(BreadBoardPB.pGPIO, BreadBoardPB.GPIO_Pin_Cfg.GPIO_PinNumber);
if(inputVal == 0)
{
GPIO_ToggleOutputPin(NucleoUserLED.pGPIO, NucleoUserLED.GPIO_Pin_Cfg.GPIO_PinNumber);
}
/*****************************************************************
* Controlling the IO present in the bread board *
*****************************************************************/
if (BBinpVal == 0 )
{
GPIO_WriteOutputPin(BreadBoardLED.pGPIO, BreadBoardLED.GPIO_Pin_Cfg.GPIO_PinNumber, 1);
}
else
{
GPIO_WriteOutputPin(BreadBoardLED.pGPIO, BreadBoardLED.GPIO_Pin_Cfg.GPIO_PinNumber, 0);
}
delay();
}
return 0;
}
It is not a function only the macrodefinition.
Your loop is likely to be optimized out
define it as
void inline __attribute__((always_inline)) delay(uint32_t delay)
{
while(delay--) __asm("");
}
Bear in mind that 50000 can be quite long if you run on low clock settings.
Not sure what the issue is because "it is not working" is not very specific.
However there are "quality" issues:
That is an inappropriate use of a macro - there is no benefit over using a function. The function call overhead argument does not hold - it is a delay, it is supposed to take time!
The empty-loop counter is not declared volatile - the compiler at any optimisation level other then the minimum is likely to remove the loop altogether.
A for-loop for a delay is a crude and generally non-deterministic solution, with a period that will change between compilers, with different compiler options and on different targets or with different clock speeds. STM32 is a Cortex-M device and given that you should use the SYSTICK counter for this. For example, as a minimum something like:
volatile uint32_t tick = 0 ;
void SysTick_Handler(void)
{
tick++ ;
}
void delayms( uint32_t millisec )
{
static bool init = false ;
if( !init )
{
SysTick_Config( SystemCoreClock / 1000 ) ;
init = true ;
}
uint32_t start = tick ;
while( tick - start < millisec ) ;
}
The issue was solved by declaring the iterator as a global variable. Now the LED turns on when the Push button is pressed
Previous implementation
#define delay() for(uint32_t i=0; i<=50000; i++);
Working implementation
uint32_t temp;
void delay(void)
{
for(temp = 0;temp<=50000;temp++)
{
;
}
}
Can any one tell me how declaring the variable as global solves the issue?
Find the working implementation below
#include <stdint.h>
#include "stm32l476xx.h"
#include "stm32l476xx_gpoi_driver.h"
#if !defined(__SOFT_FP__) && defined(__ARM_FP)
#warning "FPU is not initialized, but the project is compiling for an FPU. Please initialize the FPU before use."
#endif
uint32_t temp;
void delay(void)
{
for(temp = 0;temp<=50000;temp++)
{
;
}
}
int main(void)
{
GPIO_Handle_t NucleoUserLED,NucleoUserPB,BreadBoardLED,BreadBoardPB;
volatile uint8_t inputVal,BBinpVal;
//User green led in the nucleo board connected to PA5
NucleoUserLED.pGPIO = GPIOA;
NucleoUserLED.GPIO_Pin_Cfg.GPIO_PinNumber = GPIO_PIN_5;
NucleoUserLED.GPIO_Pin_Cfg.GPIO_PinMode = GPIO_MODE_OP;
NucleoUserLED.GPIO_Pin_Cfg.GPIO_PinPuPdControl = GPIO_IP_NO_PUPD;
NucleoUserLED.GPIO_Pin_Cfg.GPIO_PinOpType = GPIO_OP_TYPE_PP;
//User blue button in the nucleo connected to PC13
NucleoUserPB.pGPIO = GPIOC;
NucleoUserPB.GPIO_Pin_Cfg.GPIO_PinNumber = GPIO_PIN_13;
NucleoUserPB.GPIO_Pin_Cfg.GPIO_PinMode = GPIO_MODE_IP;
NucleoUserPB.GPIO_Pin_Cfg.GPIO_PinPuPdControl = GPIO_IP_NO_PUPD;
//User led in the bread board connected to PC8
BreadBoardLED.pGPIO = GPIOC;
BreadBoardLED.GPIO_Pin_Cfg.GPIO_PinNumber = GPIO_PIN_8;
BreadBoardLED.GPIO_Pin_Cfg.GPIO_PinMode = GPIO_MODE_OP;
BreadBoardLED.GPIO_Pin_Cfg.GPIO_PinPuPdControl = GPIO_IP_NO_PUPD;
BreadBoardLED.GPIO_Pin_Cfg.GPIO_PinOpType = GPIO_OP_TYPE_PP;
//User DPDT connected in the breadboard connected to PC6
BreadBoardPB.pGPIO = GPIOC;
BreadBoardPB.GPIO_Pin_Cfg.GPIO_PinNumber = GPIO_PIN_6;
BreadBoardPB.GPIO_Pin_Cfg.GPIO_PinMode = GPIO_MODE_IP;
BreadBoardPB.GPIO_Pin_Cfg.GPIO_PinPuPdControl = GPIO_IP_PU;
GPIO_PeriClkCtrl(GPIOA, ENABLE);
GPIO_PeriClkCtrl(GPIOC, ENABLE);
GPIO_Init(&NucleoUserLED);
GPIO_Init(&NucleoUserPB);
GPIO_Init(&BreadBoardLED);
GPIO_Init(&BreadBoardPB);
while(1)
{
/*****************************************************************
* Controlling the IO present in the nucleo board *
*****************************************************************/
inputVal = GPIO_ReadInputPin(NucleoUserPB.pGPIO, NucleoUserPB.GPIO_Pin_Cfg.GPIO_PinNumber);
BBinpVal = GPIO_ReadInputPin(BreadBoardPB.pGPIO, BreadBoardPB.GPIO_Pin_Cfg.GPIO_PinNumber);
if(inputVal == 0)
{
GPIO_ToggleOutputPin(NucleoUserLED.pGPIO, NucleoUserLED.GPIO_Pin_Cfg.GPIO_PinNumber);
}
/*****************************************************************
* Controlling the IO present in the bread board *
*****************************************************************/
temp = 10;
if (BBinpVal == 0 )
{
GPIO_WriteOutputPin(BreadBoardLED.pGPIO, BreadBoardLED.GPIO_Pin_Cfg.GPIO_PinNumber, 1);
}
else
{
GPIO_WriteOutputPin(BreadBoardLED.pGPIO, BreadBoardLED.GPIO_Pin_Cfg.GPIO_PinNumber, 0);
}
delay();
}
return 0;
}
The issue is solved,
There was an bug in the driver layer I have written
Whenever an GPIO is configured as Input, the registers related to Output for that GPIO pin should be set to their reset value or the driver should not implement the API related to the Output
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.
I'm trying to comunicate a pic18f24k50 with an arduino 101. I'm using two lines to establish a synchronized comunication. On every change from low to high of the first line, I read the value from the second line.
I have no problems with the arduino code, my problem is that in the pic, the Interrupt on change triggers on the second change from low to high instead of triggering on the first change. This only happens the first time I send data, after that, it works perfectly (it triggers on the first change and I receive the byte properly). Sorry for my english, I'll try to explain myself better with this image:
Channel 1 is the clock signal, channel2 is the data (Im sending one byte for the moment, with bit values 10101010). Channel 4 is an output I'm changing every time I process a bit. (as you can see, it begins on the second rise of the clock signal instead of the first one). This is captured on the first sent byte, the next ones works ok.
I post the relevant code in the pic here:
This is where I initialize things:
TRISCbits.TRISC6 = 0;
TRISCbits.TRISC1 = 1;
TRISCbits.TRISC2 = 1;
IOCC1 = 1;
ANSELCbits.ANSC2=0;
IOCC2 = 0;
INTCONbits.IOCIE = 1;
INTCONbits.IOCIF = 0;
And this is on the interrupt code:
void interrupt SYS_InterruptHigh(void)
{
if (INTCONbits.IOCIE==1 && INTCONbits.IOCIF==1)
{
readByte();
}
}
void readByte(void)
{
while(contaBits<8)
{
INTCONbits.IOCIE = 0;
INTCONbits.IOCIF = 0;
while (PORTCbits.RC1 != HIGH)
{
}
if (PORTCbits.RC1 == HIGH)
{
LATCbits.LATC6 = !LATCbits.LATC6;
//LATCbits.LATC6 = ~LATCbits.LATC6;
switch (contaBits)
{
case 0:
if (PORTCbits.RC2 == HIGH)
varByte.b0 = 1;
else
varByte.b0 = 0;
break;
case 1:
if (PORTCbits.RC2 == HIGH)
varByte.b1 = 1;
else
varByte.b1 = 0;
break;
case 2:
if (PORTCbits.RC2 == HIGH)
varByte.b2 = 1;
else
varByte.b2 = 0;
break;
case 3:
if (PORTCbits.RC2 == HIGH)
varByte.b3 = 1;
else
varByte.b3 = 0;
break;
case 4:
if (PORTCbits.RC2 == HIGH)
varByte.b4 = 1;
else
varByte.b4 = 0;
break;
case 5:
if (PORTCbits.RC2 == HIGH)
varByte.b5 = 1;
else
varByte.b5 = 0;
break;
case 6:
if (PORTCbits.RC2 == HIGH)
varByte.b6 = 1;
else
varByte.b6 = 0;
break;
case 7:
if (PORTCbits.RC2 == HIGH)
varByte.b7 = 1;
else
varByte.b7 = 0;
break;
}
contaBits++;
}
}//while(contaBits<8)
INTCONbits.IOCIE = 1;
contaBits=0;
}
LATCbits.LATC6 = !LATCbits.LATC6; <-- this is the line corresponding to channel 4.
RC1 is channel 1
and RC2 is channel 2
My question is what am I doing wrong, why on the first sent of bytes the interrupt doesn't triggers on the first change of the line 1?
Thank you.
What are you trying to achieve?
The communication protocol as you're describing is commonly refered to as SPI (Serial Peripheral Interface). You should use the hardware implementation available by PIC/Microchip, if possible, for best performance.
Keep your code documented/formatted/logic
I noticed your code being a little, weird.
Weird code gives weird errors.
First:
You're using interrupts, you have this blocking code in your interrupt. Which isn't nice at all.
Second:
Your "contabits" and "varByte" variable come out of nowhere, probably they're global. Which might not be the best practice.
Though, if you're using interrupts, and when it might make sense to transfer the variables to your main program by using a global. The global should be volatile.
Third:
That case switch is just 8x the same code, but a little different.
Also, add some comments to your code.
To be honest
I didn't spot your actual error, has been a long time since I worked with PIC.
But, you should try hardware SPI.
Below is my attempt at software SPI as you described, it might be a little more logical, and takes out any annoyance of the interrupts.
I would recommend replacing the while(CLOCK_PIN_MACRO != 1){}; with for(unsigned int timeout = 64000; timeout > 0; timeout--){};.
So that you can be sure your program won't hang while waiting for an SPI signal that won't come. (Make the timeout something what fits your needs)
#define CLOCK_PIN_MACRO PORTCbits.RC1
#define DATA_PIN_MACRO PORTCbits.RC2
/*
Test, without interrupts.
I would strongly advise to use Hardware SPI or non-blocking code.
*/
unsigned char readByte(void){
unsigned char returnByte = 0;
for(int i = 0; i < 8; i++){ //Do this for bit 0 to 7, to get a complete byte.
while(CLOCK_PIN_MACRO != 1){}; //Wait until the Clock pin goes high.
if(DATA_PIN_MACRO == 1) //Check the data pin.
returnByte = returnByte & (1 << i); //Shift the bit in our byte. (https://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/bitshift.html)
while(CLOCK_PIN_MACRO == 1){}; //Wait untill the Clock pin goes low again.
}
return returnByte;
}
void setup(){
TRISCbits.TRISC6 = 0;
TRISCbits.TRISC1 = 1;
TRISCbits.TRISC2 = 1;
//IOCC1 = 1;
ANSELCbits.ANSC2=0;
//IOCC2 = 0;
INTCONbits.IOCIE = 1;
INTCONbits.IOCIF = 0;
}
void run(){
unsigned char theByte = readByte();
}
main(){
setup();
while(1){
run();
}
}
You can find the following from the datasheet.
The pins are compared with the old value latched on the last read of
PORTB. The "mismatch" outputs of RB7:RB4 are ORed together to generate
the RB Port Change Interrupt with Flag bit,RBIF (INTCON<0>).
This means that when you read PORTB RB7:RB4 pins, the value of each pin will be stored in an internal latch. Interrupt will be generated only when there is any change in the pin input from the previously latched value.
Then, what will be the default initial value in the latch? i.e. Before we read anything from PORTB.
What ever it is. in your case the default latch value is different from the logic state of your input signal. During the next transition of the signal the latch value and signal level becomes same. So no intterrupt is generated first time.
What you can do is setting the latch value to the initial state of your signal. This can be done by reading PORTB (should be done before enabling the intterrupt).
The below code is enough to simply read the register.
unsigned char ch;
ch = PORTB;
Hope this will help.