I am writing a program using automation software for controlling an oil tank.
I have several alarm signals, if any of these signals become true this triggers an alarm flag. This Alarm flag then needs to light up a warning lamp. Up to this stage I am fine, however I am trying to make the lamp flash on and off.
I know I have to use timers and so far have implemented that turns the lamp on for 2s and then off for 2s but I don't know how to loop this so it continues to do so until a reset button is pressed.
This is my code so far;
PROGRAM _CYCLIC
Warning_Lamp;
TON_Warning(IN := Alarm_Active_Flag_1 , PT := T#2s );
IF(TON_Warning.Q = TRUE)THEN
Warning_Lamp := TRUE;
END_IF
TON_Warning_2(IN := TON_Warning.Q , PT := T#2s );
IF(TON_Warning_2.Q = TRUE)THEN
Warning_Lamp := FALSE;
END_IF
END_PROGRAM
Without knowing the platform of PLC or syntax and conventions of the STL for that PLC I can't give you the most efficient example, but here's a little pseudo-code that should get the point across
// Set a Bit to indicate an alarm was triggered
IF(Alarm_State := TRUE) THEN
Wait_For_Reset = TRUE;
END_IF
// Condition your code off the state of that bit rather than your alarm bit
IF(Wait_For_Reset := TRUE) THEN
//YOUR CODE HERE
END_IF
//Reset the bit that controls your code loop when the user presses Reset
IF(Reset_Pressed := TRUE) THEN
Wait_For_Reset = FALSE:
END_IF
I would avoid DO/WHILE loops unless this is the only job this PLC has, because with most platforms I've encountered, a DO/WHILE loop interrupts the normal scan of the PLC until the loop completes.
Now obviously this doesn't do anything to the Actual Alarm bit, only what your code does if it's HIGH, and if the alarm condition still exists then it will just set again and continue processing your code. If you want to prevent that from happening, it would be best to use an Rising Edge Input from Alarm_State to set the Wait_For_Reset bit. That way it only sets once for each LO/HIGH transition of the Alarm_State. Every PLC I've encountered has some version of Rising Edge Bit. If you want to do that manually in code then you could do something like this:
IF(Alarm_State := TRUE AND WFR_ONS := FALSE) THEN
Wait_For_Reset := TRUE;
WFR_ONS := TRUE; //One Shot
END_IF
IF(Alarm_State := FALSE) THEN
WFR_ONS := FALSE;
END_IF
The rest would remain the same.
count := count +1;
IF count > 2000 THEN
count:=0;
END_IF
IF count>500 THEN
Lamp:TRUE;
ELSE
lamp:=FALSE;
END_IF
// for 2 seconds (cycle time = 1ms*2000)
Related
I want to make certain effects with RGB led which changes colour using PWM signals.
|¯¯¯¯¯¯¯¯¯¯||¯¯¯¯¯¯¯¯¯¯||¯¯¯¯¯¯¯¯¯¯|
| Green || Yellow || Pink |
|__________||__________||__________|
15ms 10ms 25ms
|¯¯¯¯¯¯¯¯¯¯||¯¯¯¯¯¯¯¯¯¯||¯¯¯¯¯¯¯¯¯¯|
| Blue || Magneta || Blue |
|__________||__________||__________|
20ms 10ms 20ms
Based on interrupt, these led colours should be displayed on LED.
I made a task specifically for this purpose and running these effects on that task.
while(1)
{
if (indication_type == event_a)
{
led_color_green();
osDelay(15);
led_color_yellow();
osDelay(10);
led_color_pink();
osDelay(25);
}
else if (indication_type == event_b)
{
led_color_blue();
osDelay(20);
led_color_magneta();
osDelay(10);
led_color_blue();
osDelay(20);
}
else
..
..
}
Code is working but there are two issues
Each led effect is visible for 50ms and if an event change has occurred, the led keeps on executing the previous state and a new state is showed after it has exited if-else condition with delay as per current implementation. How can I change the led indication immediately after receiving an interrupt?
I think there should be some clean way to make such effects especially with led. How can I improve this code? Any code reference you can share?
I am using STM32F4 and CMSIS RTOS v2
You need to be able to make a decision about the "indication type" at every time quanta (tick), and switch the sequence immediately.
The following is an outline of one way to do that:
// Color/time descriptor for each sequence
static const struct
{
void (*setcolor)(void) // Pointer to color set function
int showtime ; // Time to display color
} indication_descriptor[][3] =
{
{ {led_color_green, 15}, {led_color_green, 15}, {led_color_green, 15} }, // event_a sequence
{ {led_color_blue, 15}, {led_color_magneta, 15}, {led_color_blue, 15} }, // event_b sequence
...
} ;
// Sequence control variables
int last_indication_type = indication_type - 1 ; // force "change" on first iteration
int current_indication = 0 ;
int current_step = 0 ;
int step_time = 0 ;
while(1)
{
// On indication change...
if( indication_type != last_indication_type )
{
// Set the indication descriptor...
if( indication_type == event_a )
{
current_indication = 0 ;
}
else if( indication_type == event_b )
{
current_indication = 1 ;
}
...
last_indication_type = indication_type ;
// Set initial indication color and time for this sequence
current_step = 0
step_time = indication_descriptor[current_indication][0].showtime ;
indication_descriptor[current_indication][0].setcolor() ;
}
// Wait one tick
osDelay(1) ;
// Decrement time in step
step_time-- ;
// If end of step...
if( step_time == 0 )
{
// Set colour and time for next step
current_step == (current_step % 3) ;
step_time = indication_descriptor[current_indication][current_step].showtime ;
indication_descriptor[current_indication][current_step].setcolor() ;
}
}
It is likely that further improvements can be made, but from the fragment you have provided it is not possible to advise. For example the data type and range of indication_type, event_a etc are not shown - it is possible that a switch-case or lookup table or even arithmetic could replace the if/else if/.../else selection block, but more information would be required.
If you need faster and more deterministic sequence interruption than 1ms you'd have to use an osTimer, with a callback function that sets a thread flag and have the task wait on a thread flag rather than osDelay(). The interrupter can then set a different thread flag to wake the task to abort the timer and switch indication. That is somewhat more complex however and likely not necessary in this case. A pattern that is useful for when hard-real-time response at the microsecond order is required. If you did that the sequence code might change thus:
...
// Set initial indication color and time for this sequence
current_step = 0
osTimerStart( step_timer, indication_descriptor[current_indication][0].showtime ) ;
indication_descriptor[current_indication][0].setcolor() ;
}
// Wait for timer expiry or event
uint32_t flags = osThreadFlagsWait( STEP_COMPLETE_FLAG | SEQUENCE_CHANGE_FLAG, osFlagsWaitAny, step_time ) ;
// If end of step...
if( (flag & STEP_COMPLETE_FLAG) != 0 )
{
// Set colour and time for next step
current_step == (current_step % 3) ;
step_time = indication_descriptor[current_indication][current_step ].showtime ;
indication_descriptor[current_indication][current_step].setcolor() ;
}
else
{
osTimerStop( step_timer ) ;
}
It has the advantage of having far fewer task context switches which may be useful if you need to reduce CPU load to achieve real-time deadlines or reduce power consumption by entering sleep for longer periods.
osWait allows you to wait for either an event or a timeout. So if you can generate a proper signal when you have an interrupt, the thread will wake-up and you can immediately switch LED state.
See https://www.keil.com/pack/doc/cmsis/RTOS/html/group__CMSIS__RTOS__Wait.html#details
Put the RTOS to work and use an RTOS timer. From the interrupt that triggers the change in indication_type, you can call your function to set the color and then start the timer for the desired color duration (this can also be done in a task context after the interrupt notifies the task). On the timeout, set the next color and restart the timer with the next color duration. When indication_type changes, you can simply set the color again and restart the timer with the new indication_type color pattern.
hi i planning to made a multiple servo controlling with serial as control trigger signal on AVR with C and codevision
but when the trigger is true, the servo running in crazy loop, it back to original position (0 degree) instead of stay on desired position, my tutor give me hint to use "wait ... until" statement with the old data comparison but i'm not found the way to utilize it yet on google
because utilizing break; at the end of the if left the chip freeze until it reset
and the old code(it runs the servo forth and back continously)
while (1)
{
while(UCSRA & (1<<RXC))
{
// Place your code here
//data=UDR;
PORTC=UDR;
data=UDR;
//PORTB=data;
} ;
if (data== 0x0A || data== 0x0B)
{
if (data== 0x0A)
{
old_data=data;
PORTA=0x00;
PORTA.1=1;
movservo0(90,7);
movservo1(15,3);
}
if (data== 0x0B)
{
old_data=data;
PORTA=0x00;
PORTA.1=1;
movservo0(15,3);
movservo1(90,7);
}
}
}
as for movservo0 (another movservo() almost had same code)
void set_servo1(unchar derajat)
{ unchar n;
servo2=1;
delay_us(750);
for(n=0; n<derajat; n++)
{
delay_us(12);
};
servo2=0;
delay_ms(10);
}
void movservo0(unsigned char sudut, unsigned char speed)
{
unchar i;
set_servo1(sudut);
for (i=1;i<=sudut;i+=speed){
set_servo1(i);
delay_ms(100/speed);
}
}
This new code is a bit better.
The top of the while(UCSRA & (1<<RXC)) loop is fine this way. The loop body will only be entered if there is a character to be read. (Although, I'm not sure why you are expecting to read '\n' or vertical tab.)
A minor problem is the way the UDR is read. The act of reading UDR erases the contents. So you should be using
data=UDR;
PORTC=data;
The jumping of the servos appears to be in the moveservo() function. This function always sets the angle to 1 and then gradually increases the angle of the servo until it reaches the desired angle.
setservo() appears to be an attempt to perform PWM to drive a servo, but it doesn't work correctly. To keep the servo at a desired angle, you have to keep switching the pin from 0 to 1 at the correct times, not just once like this function does. Have you looked at the PWM functions of the timers? You just set these up and they run in the background. An alternative is to use a timer to set an interrupt to wake up and toggle the pins as you need.
If you want to do the switching of the pins without interrupts, then you should be using delays in the while(1) loop itself. Just use the setservo() functions to change some variables.
while(1)
{
// read the UART if ready and set the target values
// this part only runs occasionally
while(UCSRA & (1<<RXC))
{
// etc
}
// The rest of the loop body runs every time
// adjust the servo values toward the target values
// use a counter to determine if to adjust during this loop iteration
// e.g., a slow speed counts to a higher number before adjusting
delay(750);
for(int i = 0; i < NUM_INTERVALS; ++i)
{
// decide whether to set pins to 1 or 0 based on the servo values
delay(INTERVAL);
}
}
I don't know about your particular servos, but they typically have a period where most of the time the pin is 0, and then a short time in the period where the pin is 1. You will need to adjust NUM_INTERVALS and INTERVAL and 750 to add up to the correct length of time.
There is so much wrong with this snippet, it is hard to start. It is difficult to say why the servo is moving, as it is only ever "moved" to the same value every time. (Unless, you have omitted some code that sets it to some other value.)
Typically, when receiving UART, the processor should wait until the character is received. This is accomplished by this
while(UCSRA & (1<<RXC) == 0);
Note the ; creating an empty body of the while loop. This is probably what your tutor meant. When the flag is set, then the loop exits and the data is ready to be read.
while(UCSRA & (1<<RXC) == 0);
data = UDR;
Next, you have a block which looks like you meant to be part of a while loop, but it isn't. The body of the loop is the single statement abbove it. The block gets executed every time.
{
if(data=0x0a)
{
olddata=data;
movservo0(90,7); //move servo to certain degree
}
}
Another error is the condition in the if statement. It looks like you are trying to test if data is 0x0A, but that is not what is happening. Instead, you set data to be 0x0A, and then execute the inner part every time. The condition you probably want is if(data == 0x0A). Note the == instead of =.
So your code is equivalent to
while(1)
{
while(ucsra & (1<<RXC)) data=udr; // maybe read UDR into data
data=0x0a; // set data anyway
olddata=data;
movservo0(90,7); //move servo to certain degree
}
Again, for the jumping servo, I suspect some code that is omitted here that is also runs every time. Or else, the moveservo() function has a problem itself.
I'm not so great with VHDL and I can't really see why my code won't work. I needed an NCO, found a working program and re-worked it to fit my needs, but just noticed a bug: every full cycle there is one blank cycle.
The program takes step for argument (jump between next samples) and clock as trigger.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL; --try to use this library as much as possible.
entity sinwave_new_01 is
port (clk :in std_logic;
step :in integer range 0 to 1000;
dataout : out integer range 0 to 1024
);
end sinwave_new_01;
architecture Behavioral of sinwave_new_01 is
signal i : integer range 0 to 1999:=0;
type memory_type is array (0 to 999) of integer range 0 to 1024;
--ROM for storing the sine values generated by MATLAB.
signal sine : memory_type :=(long and boring array of 1000 samples here);
begin
process(clk)
begin
--to check the rising edge of the clock signal
if(rising_edge(clk)) then
dataout <= sine(i);
i <= i+ step;
if(i > 999) then
i <= i-1000;
end if;
end if;
end process;
end Behavioral;
What do I do to get rid of that zero? It appears every full cycle - every (1000/step) pulses. It's not supposed to be there and it messes up my PWM...
From what I understand the whole block (dataout changes, it is increased, and if i>999 then i<=i-1000) executes when there is a positive edge of clock applied on the entrance...
BUT it looks like it requires one additional edge to, I don't know, reload it? Does the code execute sequentially, or are all conditions tested when the clock arrives? Am I reaching outside the table, and that's why I'm getting zeroes in that particular pulse? Program /shouldn't/ do that, as far as I understand if statement, or is it VHDL being VHDL and doing its weird stuff again.
How do I fix this bug? Guess I could add one extra clock tick every 1k/step pulses, but that's a work around and not a real solution. Thanks in advance for help.
It looks like your problem is that your variable 'i' exceeds 999 before you reset it. Remember, you're in a sequential process. 'i' doesn't get the assigned value until the next clock tick AFTER you assign it.
I think if you change this code
i <= i + step;
if (i > 999) then
i <= i-1000;
to
if ((i + step) > 999) then
i <= (i + step) - 1000;
else
i <= i + step;
you should get the behavior you're looking for.
One more thing...
Does the declaration of sine (sample array) actually creates combinatory circuit (bad) or allocates those samples in ROM memory ('good')?
Im working on a NCO (still) and I got problems with adress select block - my teacher wants the samples in ROM block (done that already) but the adressing thingie doesnt seem to work. What I need is a modulo 200 accumulator with variable step... I adopted this code from a sample where somebody used i as counter to pick a value from an array of samples, BUT I need to simply copy i to the output port.
Something with PWM wasnt working, it skipped not ten but ~80 samples, so I decided to check the adressing - Ive been mighty surprised when I noticed that adress changes INDEPENDENTLY from the clock signal. ( http://i.imgur.com/XL9l8mj.jpg )
Heres the code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL; --try to use this library as much as possible.
entity adress_select_200 is
port (clk :in std_logic;
step :in integer range 0 to 200;
adress : out integer range 0 to 199
);
end adress_select_200;
architecture Behavioral of adress_select_200 is
signal i : integer range 0 to 399:=0;
begin
process(clk)
begin
--to check the rising edge of the clock signal
if(rising_edge(clk)) then
adress <= i;
i <= i+step;
if ((i + step) > 199) then
i <= (i + step) - 200;
else
i <= i + step;
end if;
end if;
end process;
end Behavioral;
Im not so great with VHDL, but I suppose the whole loop should ONLY execute on clk rising edge, right? Meanwhile its doing that weird sh... in the middle of the cycle, no idea why.
How do I stop that from happening?
I think I might be having a stack overflow problem or something similar in my embedded firmware code. I am a new programmer and have never dealt with a SO so I'm not sure if that is what's happening or not.
The firmware controls a device with a wheel that has magnets evenly spaced around it and the board has a hall effect sensor that senses when magnet is over it. My firmware operates the stepper and also count steps while monitoring the magnet sensor in order to detect if the wheel has stalled.
I am using a timer interrupt on my chip (8 bit, 8057 acrh.) to set output ports to control the motor and for the stall detection. The stall detection code looks like this...
// Enter ISR
// Change the ports to the appropriate value for the next step
// ...
StallDetector++; // Increment the stall detector
if(PosSensor != LastPosMagState)
{
StallDetector = 0;
LastPosMagState = PosSensor;
}
else
{
if (PosSensor == ON)
{
if (StallDetector > (MagnetSize + 10))
{
HandleStallEvent();
}
}
else if (PosSensor == OFF)
{
if (StallDetector > (GapSize + 10))
{
HandleStallEvent();
}
}
}
this code is called every time the ISR is triggered. PosSensor is the magnet sensor. MagnetSize is the number of stepper steps that it takes to get through the magnet field. GapSize is the number of steps between two magnets. So I want to detect if the wheel gets stuck either with the sensor over a magnet or not over a magnet.
This works great for a long time but then after a while the first stall event will occur because 'StallDetector > (MagnetSize + 10)' but when I look at the value of StallDetector it is always around 220! This doesn't make sense because MagnetSize is always around 35. So the stall event should have been triggered at like 46 but somehow it got all the way up to 220? And I don't set the value of stall detector anywhere else in my code.
Do you have any advice on how I can track down the root of this problem?
The ISR looks like this
void Timer3_ISR(void) interrupt 14
{
OperateStepper(); // This is the function shown above
TMR3CN &= ~0x80; // Clear Timer3 interrupt flag
}
HandleStallEvent just sets a few variable back to their default values so that it can attempt another move...
#pragma save
#pragma nooverlay
void HandleStallEvent()
{
///*
PulseMotor = 0; //Stop the wheel from moving
SetMotorPower(0); //Set motor power low
MotorSpeed = LOW_SPEED;
SetSpeedHz();
ERROR_STATE = 2;
DEVICE_IS_HOMED = FALSE;
DEVICE_IS_HOMING = FALSE;
DEVICE_IS_MOVING = FALSE;
HOMING_STATE = 0;
MOVING_STATE = 0;
CURRENT_POSITION = 0;
StallDetector = 0;
return;
//*/
}
#pragma restore
Is PosSensor volatile? That is, do you update PosSensor somewhere, or is it directly reading a GPIO?
I assume GapSize is rather large (> 220?) It sounds to me like you might have a race condition.
// PosSensor == OFF, LastPosMagState == OFF
if(PosSensor != LastPosMagState)
{
StallDetector = 0;
LastPosMagState = PosSensor;
}
else
{
// Race Condition: PosSensor turns ON here
// while LastPosMagState still == OFF
if (PosSensor == ON)
{
if (StallDetector > (MagnetSize + 10))
{
HandleStallEvent();
}
}
else if (PosSensor == OFF)
{
if (StallDetector > (GapSize + 10))
{
HandleStallEvent();
}
}
}
You should cache the value of PosSensor once, right after doing StallDetector++, so that in the event PosSensor changes during your code, you don't start testing the new value.
This is definitely not stack overflow. If you blew the stack (overflowed it) your application would simply crash. This sounds more like something we used to call memory stomping in my C++ days. You may not be accessing the memory location that the StallDetector value occupies via StallDetector variable alone. There may be another part of your code "stomping" this particular memory location erroneously.
Unfortunately, this kind of issue is very hard to track down. About the only thing you could do is systematically isolate (remove from execution) chunks of your code until you narrow down and find the bug.
Do you have nest ISRs on your system? Could be something along the lines of start your ISR and increment your count, then interrupt it and do it again. Do this enough times and your interrupt stack can overflow. It could also explain such a high counter variable as well.
Does HandleStallEvent() "look at" StallDetector within the ISR or does it trigger something on the main loop? If it's on the main loop, are you clearing the interrupt bit?
Or are you looking at StallDetector from a debugger outside the ISR? Then a retriggered interrupt would use the correct value each time, but execute too many times, and you would only see the final, inflated value.
On second thought, more likely you don't have to clear an interrupt-generating register, but rather the interrupt pin is remaining asserted by the sensor. You need to ignore the interrupt after it's first handled until the line deasserts, such as by having the original ISR disable itself and and reinstall it in a second ISR which handles the 1->0 transition.
You might then also need to add debouncing hardware or adjust it if you have it.
Check your parameter types. If you defined the parameters in a way different than the caller expects then calling your method could overwrite the space that variable is stored in. (For instance if you wrote the function expecting an int but it is pushing a long onto the stack.)
You could see what additional options your debugger supports. In Visual Studio, for example, it is possible to set a "data breakpoint", where you break when a memory location changes (or is set to a certain value, or above a threshold, ...).
If something like this is possible in your case, you could see where the data is changed and if there is someone else writing to the memory erroneously.