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;
}
}
}
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.
I'm trying to pick up C, using an esp32. While looking at exactly how FreeRTOS works, I found the following page regarding how to use the tasks, and best practices etc.
https://www.freertos.org/implementing-a-FreeRTOS-task.html
According to this page, to prevent starvation, tasks should be event based. Regarding what I am trying to achieve, I will try to provide a very simplified example.
Background
I have a LCD screen, which should display data from a sensor. The data shown on the LCD will be done using a task, which according to the documentation, should never exit and should be event driven to prevent starvation.
I have a way of controlling the data shown on the LCD screen, which would be a rotary encoder. This encoder can be clicked, which should refresh the sensor's data.
Question
How would I implement the event based tasks, which are described on the FreeRTOS page, in this specific context? I had a look at the documentation and the "simple" example projects on their github, but as a beginner within C and embedded, they were extremely overwhelming.
Simple demo code
void update_sensor_task(void *pvParameters)
{
// Ensure the task keeps on running
for( ; ; )
{
if(event_update_sensor) // How would I be able to notify the task that this should be run?
{
// update the data
}
}
// Tasks should not be returning, but if they happen to do so, ensure a clean exit
vTaskDelete(NULL);
}
void screen_temperature_task(void *pvParameters)
{
for(; ;)
{
if(event_sensor_updated)
{
// Update the lcd screen with the new data
}
}
vTaskDelete(NULL);
}
void on_rotary_clicked(void *pvParameters)
{
// Notify the sensor task that it should be updating?
}
EDIT:
By using what has been marked as the correct answer, I have managed to get it to work by implementing it the following way:
/* Queue used to send and receive the data */
QueueHandle_t xStructQueue = NULL;
/* Struct which shall be used to hold and pass around the data for the LCD screen*/
struct LcdData
{
int current_temp;
int current_humidity;
} xLcdData;
void initialize_queues(void)
{
xLcdData.current_humidity = 0;
xLcdData.current_temp = 0;
xStructQueue = xQueueCreate(
/* The maximum number of items the queue can hold*/
5,
/* The size of each struct, which the queue should be able to hold */
sizeof( xLcdData )
);
if(xStructQueue == NULL)
{
ESP_LOGE(TAG, "Queue has not been initialized successfully");
}
}
void screen_temperature_task_simplified(void *pvParameters)
{
int counter = 0;
for(; ;)
{
struct LcdData xReceivedStructure;
BaseType_t result;
result = xQueueReceive(xStructQueue, &xReceivedStructure, ( TickType_t ) 10);
if(result == pdPASS)
{
counter = counter + 1;
char snum_current_counter[12];
sprintf(snum_current_counter, "%d", counter);
i2c_lcd1602_clear (lcd_info);
i2c_lcd1602_write_string (lcd_info, snum_current_counter);
}
}
vTaskDelete(NULL);
}
void update_sensor_struct(void)
{
xLcdData.current_temp = DHT11_read().temperature;
xLcdData.current_humidity = DHT11_read().humidity;
// Log the results in the console
printf("Temperature is %d \n", xLcdData.current_temp);
printf("Humidity is %d\n", xLcdData.current_humidity);
ESP_LOGI(TAG, "Data has been updated");
}
void on_rotary_clicked_simplified()
{
ESP_LOGI(TAG, "Rotary encoder has been clicked!");
// Update the struct which holds the data
update_sensor_struct();
/* Send the entire struct to the queue */
xQueueSend(
/* The handle of the queue */
xStructQueue,
/* The adress of the struct which should be sent */
(void *) &xLcdData,
/* Block time of 0 says don't block if the queue is already full.
Check the value returned by xQueueSend() to know if the message
was sent to the queue successfully. */
( TickType_t ) 0
);
}
I use FRTOS and event driven development.
The typical flow here would be:
for(;;)
{
BaseType_t result;
result = xQueueReceive(LCD_Event_Queue, &someLCDEvent, QUEUE_TIMEOUT);
if (result == pdPASS)
{
/* We have new event data in someLCDEvent; Use that data to update the LCD */
}
else
{
/* No new event, do some brief idle-time processing if necessary */
}
}
In brief, wait up to QUEUE_TIMEOUT time for a new event to arrive.
If a new event arrives within that timeframe successfully, then process the data in that event and update your screen.
If a new event does not arrive, you have an opportunity to do some other maintenance work.
Designing and defining the structure-type of someLCDEvent, and putting data into the queue is a big topic, and will depend a lot on your specific project.
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..
}
I have this loop, how would I end the loop?
void loop() {
// read the pushbutton input pin:
a ++;
Serial.println(a);
analogWrite(speakerOut, NULL);
if(a > 50 && a < 300){
analogWrite(speakerOut, 200);
}
if(a <= 49){
analogWrite(speakerOut, NULL);
}
if(a >= 300 && a <= 2499){
analogWrite(speakerOut, NULL);
}
This isn't published on Arduino.cc but you can in fact exit from the loop routine with a simple exit(0);
This will compile on pretty much any board you have in your board list. I'm using IDE 1.0.6. I've tested it with Uno, Mega, Micro Pro and even the Adafruit Trinket
void loop() {
// All of your code here
/* Note you should clean up any of your I/O here as on exit,
all 'ON'outputs remain HIGH */
// Exit the loop
exit(0); //The 0 is required to prevent compile error.
}
I use this in projects where I wire in a button to the reset pin. Basically your loop runs until exit(0); and then just persists in the last state. I've made some robots for my kids, and each time the press a button (reset) the code starts from the start of the loop() function.
Arduino specifically provides absolutely no way to exit their loop function, as exhibited by the code that actually runs it:
setup();
for (;;) {
loop();
if (serialEventRun) serialEventRun();
}
Besides, on a microcontroller there isn't anything to exit to in the first place.
The closest you can do is to just halt the processor. That will stop processing until it's reset.
Matti Virkkunen said it right, there's no "decent" way of stopping the loop. Nonetheless, by looking at your code and making several assumptions, I imagine you're trying to output a signal with a given frequency, but you want to be able to stop it.
If that's the case, there are several solutions:
If you want to generate the signal with the input of a button you could do the following
int speakerOut = A0;
int buttonPin = 13;
void setup() {
pinMode(speakerOut, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);
}
int a = 0;
void loop() {
if(digitalRead(buttonPin) == LOW) {
a ++;
Serial.println(a);
analogWrite(speakerOut, NULL);
if(a > 50 && a < 300) {
analogWrite(speakerOut, 200);
}
if(a <= 49) {
analogWrite(speakerOut, NULL);
}
if(a >= 300 && a <= 2499) {
analogWrite(speakerOut, NULL);
}
}
}
In this case we're using a button pin as an INPUT_PULLUP. You can read the Arduino reference for more information about this topic, but in a nutshell this configuration sets an internal pullup resistor, this way you can just have your button connected to ground, with no need of external resistors.
Note: This will invert the levels of the button, LOW will be pressed and HIGH will be released.
The other option would be using one of the built-ins hardware timers to get a function called periodically with interruptions. I won't go in depth be here's a great description of what it is and how to use it.
The three options that come to mind:
1st) End void loop() with while(1)... or equally as good... while(true)
void loop(){
//the code you want to run once here,
//e.g., If (blah == blah)...etc.
while(1) //last line of main loop
}
This option runs your code once and then kicks the Ard into
an endless "invisible" loop. Perhaps not the nicest way to
go, but as far as outside appearances, it gets the job done.
The Ard will continue to draw current while it spins itself in
an endless circle... perhaps one could set up a sort of timer
function that puts the Ard to sleep after so many seconds,
minutes, etc., of looping... just a thought... there are certainly
various sleep libraries out there... see
e.g., Monk, Programming Arduino: Next Steps, pgs., 85-100
for further discussion of such.
2nd) Create a "stop main loop" function with a conditional control
structure that makes its initial test fail on a second pass.
This often requires declaring a global variable and having the
"stop main loop" function toggle the value of the variable
upon termination. E.g.,
boolean stop_it = false; //global variable
void setup(){
Serial.begin(9600);
//blah...
}
boolean stop_main_loop(){ //fancy stop main loop function
if(stop_it == false){ //which it will be the first time through
Serial.println("This should print once.");
//then do some more blah....you can locate all the
// code you want to run once here....eventually end by
//toggling the "stop_it" variable ...
}
stop_it = true; //...like this
return stop_it; //then send this newly updated "stop_it" value
// outside the function
}
void loop{
stop_it = stop_main_loop(); //and finally catch that updated
//value and store it in the global stop_it
//variable, effectively
//halting the loop ...
}
Granted, this might not be especially pretty, but it also works.
It kicks the Ard into another endless "invisible" loop, but this
time it's a case of repeatedly checking the if(stop_it == false) condition in stop_main_loop()
which of course fails to pass every time after the first time through.
3rd) One could once again use a global variable but use a simple if (test == blah){} structure instead of a fancy "stop main loop" function.
boolean start = true; //global variable
void setup(){
Serial.begin(9600);
}
void loop(){
if(start == true){ //which it will be the first time through
Serial.println("This should print once.");
//the code you want to run once here,
//e.g., more If (blah == blah)...etc.
}
start = false; //toggle value of global "start" variable
//Next time around, the if test is sure to fail.
}
There are certainly other ways to "stop" that pesky endless main loop
but these three as well as those already mentioned should get you started.
This will turn off interrupts and put the CPU into (permanent until reset/power toggled) sleep:
cli();
sleep_enable();
sleep_cpu();
See also http://arduino.land/FAQ/content/7/47/en/how-to-stop-an-arduino-sketch.html, for more details.
just use this line to exit function:
return;
I'm programming a robot, and unfortunately in its autonomous mode I'm having some issues.
I need to set an integer to 1 when a button is pressed, but in order for the program to recognize the button, it must be in a while loop. As you can imagine, the program ends up in an infinite loop and the integer values end up somewhere near 4,000.
task autonomous()
{
while(true)
{
if(SensorValue[positionSelectButton] == 1)
{
positionSelect = positionSelect + 1;
wait1Msec(0350);
}
}
}
I've managed to get the value by using a wait, but I do NOT want to do this. Is there any other way I can approach this?
assuming that the SensorValue comes from a physical component that is asynchronous to the while loop, and is a push button (i.e. not a toggle button)
task autonomous()
{
while(true)
{
// check whether
if(current_time >= next_detect_time && SensorValue[positionSelectButton] == 1)
{
positionSelect = positionSelect + 1;
// no waiting here
next_detect_time = current_time + 0350;
}
// carry on to other tasks
if(enemy_is_near)
{
fight();
}
// current_time
current_time = built_in_now()
}
}
Get the current time either by some built-in function or incrementing an integer and wrap around once reach max value.
Or if you are in another situation:
task autonomous()
{
while(true)
{
// check whether the flag allows incrementing
if(should_detect && SensorValue[positionSelectButton] == 1)
{
positionSelect = positionSelect + 1;
// no waiting here
should_detect = false;
}
// carry on to other tasks
if(enemy_is_near)
{
if(fight() == LOSING)
should_detect = true;
}
}
}
Try remembering the current position of the button, and only take action when its state changes from off to on.
Depending on the hardware, you might also get a signal as though it flipped back and forth several times in a millisecond. If that's an issue, you might want to also store the timestamp of the last time the button was activated, and then ignore repeat events during a short window after that.
You could connect the button to an interrupt and then make the necessary change in the interrupt handler.
This might not be the best approach, but it will be the simplest.
From The Vex Robotics catalogue :
(12) Fast digital I/O ports which can be used as interrupts
So, most probably which ever micro-controller of Vex you are using will support Interrupts.
Your question is a bit vague
I m not sure why u need this variable to increment and how things exactly work...but i ll make a try.Explain a bit more how things work for the robot to move...and we will be able to help more.
task autonomous()
{
int buttonPressed=0;
while(true)
{
if(SensorValue[positionSelectButton] == 1)
{
positionSelect = positionSelect +1;
buttonPressed=1;
}
else{
buttonPressed = 0;
}
//use your variables here
if( buttonPressed == 1){
//Move robot front a little
}
}
}
The general idea is :
First you detect all buttons pressed and then you do things according to them
All these go in your while loop...that will(and should) run forever(at least as long as your robot is alive :) )
Hope this helps!