I'm trying to implement uart in interrupt mode, but something go wrong obviously. Here is my problem: I want to send some strings as soon as possible (example: want to send 10 times string "test123") but for some reason that isn't possible (I make some mistake but can't understand where is that mistake). I use STM32CubeIDE, mcu is stm32f407vgt6. After first successful transmit code fall into Error_Handler() which is not acceptable. When I use delays between each transmit all string will be successful transmitted but why that can be done in this way.Here is code
uint8_t TxData[] = "test123\n";
bool flagTxCmpltUsart = true;
for(i = 0; i < 10; i++){`
if(HAL_UART_Transmit_IT(&huart3, TxData, strlen(TxData)) != HAL_OK)
{
Error_Handler();
}
Wait_Unit_Uart_Tx_Is_Complete();
Reset_Uart_Tx_Complete_Flag();}
void Reset_Uart_Tx_Complete_Flag(void)
{
flagTxCmpltUsart = false;
}
void Wait_Unit_Uart_Tx_Is_Complete(void)
{
while(!flagTxCmpltUsart){}
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART3)
{
flagTxCmpltUsart = true;
}
}
Since void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) is called in the interrupt context, you should set your complete flag as
volatile bool flagTxCmpltUsart = true;
To make sure the compiler knows that is could change outside of the normal program flow.
You have to check your UART status, you'll get error if the transmission line is busy. Try to insert the following code between each call;
while (HAL_UART_GetState(&UartHandle) != HAL_UART_STATE_READY){
//Do Something..
}
Related
I am using the ADC on my STM32L431 with both DMA and interruption. Everything works fine, except that the ISR is often entered with none of the enabled interrupt flag being set.
Initial ISR code:
void ADC1_IRQHandler()
{
if(LL_ADC_IsActiveFlag_JEOC(ADC1))
{
inj_buf[BL - DMA1_Channel1->CNDTR] = ADC1->JDR1;
}
else if(LL_ADC_IsActiveFlag_OVR(ADC1))
{
LL_ADC_ClearFlag_OVR(ADC1);
}
else
{
__NOP(); //A breakpoint is set here and it is reached quite often
}
}
Somebody pointed out in the post I posted on ST forum that this is caused by the program exiting ISR too soon, therefore the interrupt flag is not yet cleared, then the program would enter ISR again. As soon as the ISR entered the flag finally gets cleared.
Since then, I tried to fixed it by doing the following:
1.
void ADC1_IRQHandler()
{
if(LL_ADC_IsActiveFlag_JEOC(ADC1))
{
inj_buf[BL - DMA1_Channel1->CNDTR] = ADC1->JDR1;
__DSB();
}
else if(LL_ADC_IsActiveFlag_OVR(ADC1))
{
LL_ADC_ClearFlag_OVR(ADC1);
__DSB();
}
else
{
__NOP();
}
}
void ADC1_IRQHandler()
{
if(LL_ADC_IsActiveFlag_JEOC(ADC1))
{
inj_buf[BL - DMA1_Channel1->CNDTR] = ADC1->JDR1;
loop_cnt++; //Read - Modify - Write
if(loop_cnt >= 1024) loop_cnt = 0;
}
else if(LL_ADC_IsActiveFlag_OVR(ADC1))
{
LL_ADC_ClearFlag_OVR(ADC1);
loop_cnt++;
loop_cnt--;
}
else
{
__NOP();
}
}
According to this article by ARM
void ADC1_IRQHandler()
{
if(LL_ADC_IsActiveFlag_JEOC(ADC1))
{
inj_buf[BL - DMA1_Channel1->CNDTR] = ADC1->JDR1;
temp_isr = ADC1->ISR & 0x7FF; //Read immediately after write
}
else if(LL_ADC_IsActiveFlag_OVR(ADC1))
{
LL_ADC_ClearFlag_OVR(ADC1);
temp_isr = ADC1->ISR & 0x7FF;
}
else
{
__NOP();
}
}
According to this post on a different forum
But none of them solved the issue.
Please help! Thank you in advance!
This should be a comment rather than an answer, but I don't quite have enough reputation to just comment. Anyway...
Why are you reading the ADC values in the ISR and storing them in a buffer, if you are DMAing the data anyway? This seems pointless and confused. Are you DMAing into SRAM, or into another peripheral?
If the answer is SRAM, these ISRs are not required. Disable interrupts for the ADC, and (maybe, or maybe not) enable them for the DMA.
Microphone readout gets randomly interrupted
I have a microphone hooked up to my Arduino on a Serial hardware port 2.
https://sigrok.org/wiki/Colead_SL-5868P
I receive data from the microphone by sending 0x20 to it. The following functions read out it's data.
FlushMic1 is a function to flush out the Serial2 buffer, so we can read out the correct data from the buffer.
String getMic1()
{
String valueMic;
if(flushedMic1())
{
Serial2.write(0x20);
while (Serial2.available() <= 10){;}
valueMic = "";
for (int i = 0; i < 10; i++)
{
int data = Serial2.read();
valueMic += String(data);
}
}
return valueMic;
}
bool flushedMic1()
{
bool returnBool = false;
while(Serial2.available())
{
Serial2.read();
returnBool = true;
}
return returnBool;
}
On another port I have a transponder loop connected, to measure passing transponders.
If a transponder passes it sends char to the Serial Hardware port 1. It can be detected if it includes a '#'.
bool isTransponderPassing()
{
char data = Serial1.read();
if (data == '#')
{
return true;
}
return false;
}
Running the getMic1() function in a loop works perfectly fine. If the : if(isTransponderPassing()) is used in the in main loop I sometimes get the correct data, sometimes false data, when I receive false data, the entire program gets interrupted, which propably fills the buffer with 16's and that's why we receive 16's.
Who knows where my program can get stuck?
void setup()
{
// Starts the serial monitor
Serial.begin(2400);
// Starts Serial monitor of AMB decoder
Serial1.begin(9600);
// Starts Serial monitor of microphone 1
Serial2.begin(2400);
}
void loop()
{
Serial.println(Serial2.available());
Serial.println(Serial1.available());
if(isTransponderPassing())
{
Serial.println(getMic1());
}
}
I fixed the issue, because I wasn't aware Serial.readString() in getMic1() has a delay of 1 second. So I should just read the documentation better next time.
So everyone, Serial.readString() has a delay of 1 second, hope this saves time for others!
Use Serial.readStringUntil(char ofChoice) instead, this reads the buffer into a String, but quits when it has reached the given char.
Buffer = "Hello world";
// Serial.readString() does:
delay(1000);
return Buffer;
result : "Hello world"
// Serial.readStringUntil("o") does:
return BufferUntilChar("o")
result : "Hello wo"
Hello everyone im doing my first steps with RTOS. Im trying to receive an amount of data using UART in an interrupt mode. I have a Display Task where the commands are being written to a global buffer, and i just created a UART Handler Task where i want to read the bytes. The problems im facing are.
The semaphore i use inside the UART Task is unknown, even though i declared it global in the main function, so the xSemaphoreTake() function has errors there. Maybe a helpful Note: the UART Task is in a seperated file.
Is my implemntation of the HAL_UART_RxCpltCallback and the UART Task clean?
here is the code i wrote:
SemaphoreHandle_t uartInterruptSemaphore = NULL;
int main(void)
{
/* USER CODE BEGIN 1 */
void mainTask(void* param) {
uartInterruptSemaphore = xSemaphoreCreateBinary();
if(uartInterruptSemaphore != NULL) {
// Display Thread with a 2 priority
xTaskCreate(&displayTask, "Display Thread", 1000, &huart4, 2, NULL);
// deferred Interrupt to be synchronized with the Display Task, must have a higher priority than the display task
xTaskCreate(&UartHandlerTask, "UART Handler Task", 1000, &huart4, 3, NULL);
}
for(;;){
}
}
the callback function i wrote:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *uart_cb) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if(uart_cb->Instance == USART4) {
xSemaphoreGiveFromISR(uartInterruptSemaphore, &xHigherPriorityTaskWoken);
}
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}
and the handler task:
void UartHandlerTask(void* param) {
huart_cache = param;
const uint8_t tmp = rx_byte; //rx byte is global volatile variable
for(;;){
if(xSemaphoreTake(uartInterruptSemaphore, portMAX_DELAY) == pdPASS) {
HAL_UART_Receive_IT((UART_HandleTypeDef *)huart_cache, (uint8_t *)&rx_byte, 1);
// write data to the buffer
RX_interrupt(tmp);
}
}
}
I would recommend getting a better handle on C before trying to use an RTOS. This will also show you a better way of unblocking a task form an interrupt than using a binary semaphore: https://www.freertos.org/2020/09/decrease-ram-footprint-and-accelerate-execution-with-freertos-notifications.html
i'm working on arduino and i have made two modes in loop(),mode1 in while loop and mode second in while loop they have conditions to be met, i switch between them using button that interrupts routine that is currently executed,changes flags to force program to change to second mode and for some reason instead of breaking out of current while loop (since conditoins are not met anymore )to change to the other it just doesnt respond
or changes from automatic->stops responding
#define modeSwitch 2
boolean manual;
boolean automatic;
String sOld;
boolean flagPrint = false;
volatile int modeSwitchValue;
String receivedData = "";
String invitation = "Welcome to Morse Code Encoder!\nPlease choose a mode: Write 'automatic' for an automatic mode or 'manual' or manual code.\n";
void setup() {
pinMode(modeSwitch, INPUT);
attachInterrupt(0, changeMode, RISING);
manual = false;
automatic = false;
volatile int modeSwitchValue = 5;
Serial.begin(9600);
Serial.println(invitation);
}
void loop() {
if (Serial.available() > 0) {
receivedData = Serial.readStringUntil('\n');
if (receivedData == "manual" ) {
manual = true;
automatic = false;
modeSwitchValue = 0;
}
if ( receivedData == "automatic") {
automatic = true;
manual = false;
modeSwitchValue = 1;
}
while ((manual == true) && (modeSwitchValue == 0) ) {
String s = "Manual mode:";
while (checkPrint(s) == true) {
Serial.println(s);
}
automatic = false;
}
while ((automatic == true) && (modeSwitchValue == 1)) {
String s = "Automatic mode:";
while (checkPrint(s) == true) {
Serial.println(s);
}
manual = false;
}
}
}
void changeMode() {
if ( modeSwitchValue == 0) {
modeSwitchValue = 1;
automatic = true;
manual = false;
}
if ( modeSwitchValue == 1) {
modeSwitchValue = 0;
manual = true;
automatic = false;
}
}
I deleted checkPrint() function cause it only ensures msgs are printed once so its not essential to show here i think
I thought that while executing automatic code, i press button and it interrupts,changing mode variable then goes back to same place in program where the interrupt occured. then software notices that while loop condition of automatic mode is not fulfilled anymore so it breaks out to loop() and finds while loop of manual mode to be executed since the conditions are met. am i thinking wrong? or have i missed something?
If you really want to attach an ISR to the RISING edge of a button signal, you should ignore multiple triggers. Minimum 2 msec, but why not ignore everything that's unrealistic (e.g. 100 msec)
As you just toggle two states, I'd propose a boolean variable istate.
volatile bool istate; // the state maintained in the ISR changeMode
void changeMode() {
static unsigned long lastime;
if (millis() - lastime > 100) {
lastime = millis();
istate = ! istate;
}
}
All other redundant stuff (automatic, manual) can be built from that volatile ISR variable in the main code, if necessary.
In my unterstanding, the name buttonState is just wrong. And the variable unnecessary.
Do you know the data type enum?
enum {MANUAL, AUTO} mode;
if (istate) mode = AUTO;
else mode = MANUAL;
might be nice for your purpose.
You are mixing up assignment "=" and comparison "==" in your changeMode() function. Here is a corrected version of the function.
void changeMode() {
if ( buttonState == HIGH) {
modeSwitchValue = 1;
automatic = true;
manual = false;
buttonState = LOW;
}
if (buttonState == LOW) {
modeSwitchValue = 0;
manual = true;
automatic = false;
buttonState = HIGH;
}
}
You should also realize that mechanical buttons can have contact bounce. Since you are using an interrupt to detect changes in the button state you might get erratic behavior. Usually, it is better to poll the button state and apply a de-bounce time delay on the order of 20ms.
You also have not shared how the button is wired. Since you are detecting a rising edge, I assume that the button is wired to pull the pin high when pressed. If you are using a single-pole single-though button, you will need a pull down resistor to ground, otherwise the interrupt pin will float.
You can avoid the required pull-up or pull down resistor, by connecting a simple button between the input pin and ground. Then change your initialization code to
pinMode(pin, INPUT_PULLUP)
You will then want to trigger interrupt on the falling edge.
I hope this helps.
I have a light sensor that prints the value of its input to the Serial monitor. It's pretty much a trip wire but when an object is in its way, it prints the value every 1 millisecond. If I add a delay it won;t trigger the second sensor until the delay is done. How would I get it to only print once, without any disturbance or interference with the other sensors?
void loop() {
if (analogRead(sensor1) == 0) {
timer.start ();
tStop = false;
//Serial.println (timer.elapsed());
Serial.println ("Start Time = 0");
}
This is quite an interesting problem, in the normal world of computers we would solve this via threading. However as you are running without an OS we have to do one of two things, implement coroutines (fake threading without an OS) or use asynchronous code and interrupts.
My understanding is that you print something when an object first comes into the way of your sensor, as the arduino uno as opposed to the due is not easy to implement coroutines on we shall try the interrupt route.
First you will likely be interested in this library http://playground.arduino.cc/Code/Timer1
It allows you to add an interrupt service routine to run on a timer. Use the attachInterrupt(function, period) function in the library for this.
In your interrupt service routine you will want to check the sensor, set a variable to say how long ago since it was last triggered and print the message if appropriate. This means your main loop is completely free to run other code and will not block your other sensors.
For example:
void TimFun()
{
static int LastRead;
if(LastRead && (0 == analogRead(sensor1))
{
Serial.println("SensorTrip");
}
LastRead = analogRead(sensor1);
}
void loop()
{
// Do other stuff here
}
void setup()
{
Timer1.initialize(100000);
Timer1.attachInterrupt(TimFun);
// Rest of setup Here
}
I managed to make an int before the void setup and then used a while loop. with in the if statement.
int i = 1;
if (analogRead(sensor1) == 0) {
timer.start ();
tStop = false;
while (i == 1) {
Serial.println ("Start Time = 0");
i++;
}
}
You probably should use an if instead of a while loop that will never execute more than once.
bool tripped = false;
void setup(){
//setup stuff here
}
void loop() {
if ( analogRead(sensor1) == 0 )
{
timer.start ();
tStop = false;
if ( tripped == false )
{
Serial.println ("Start Time = 0");
tripped = true;
}
}
}