I am trying to develop a water flowmeter using STM32L073 and MAX35103. I need to display flow values on a custom LCD. After 24 minutes, the custom LCD screen freezes but continues to read in the background and take the values.
I run the update code below every time I print data to the LCD. What are the possible errors and omissions?
void LCDUpdate(void){
LCD->CLR |= (1UL << 3U);
LCD->SR |= (1UL << 2U);
while(!((int)(LCD->SR & (1UL << 3U))));
}
Related
I have a STM32F051 driving a H-bridge (with proper gate drivers and overcurrent pulse sent back to the MCU) which power a transformer, using TIM1 and complementary signals (and dead time generation).
I am trying to configure a different "safe" state depending on which overcurrent pulse I receive:
On high side overcurrent, turn off low side fets, turn on high side fets.
On low side overcurrent, turn off high side fets, turn on low side fets.
Idea is to improve overcurrent performance on an inverter.
Is there a possibility to manually set the outputs of the timers to a defined state immediately when receiving a pulse on a GPIO ? I tried with the break function, but you can only set one predefined "safe" state. For my application I need two (for now, more to come).
In the end I found the result and I share it with you.
The source code and examples of libopencm3 helped me to find the answer.
#define TIM_CCMR1_OC1M_INACTIVE (0x2 << 4)
#define TIM_CCMR1_OC1M_FORCE_LOW (0x4 << 4)
#define TIM_CCMR1_OC1M_FORCE_HIGH (0x5 << 4)
#define TIM_CCMR1_OC1M_PWM2 (0x7 << 4)
#define TIM_CCMR1_OC2M_INACTIVE (0x2 << 12)
#define TIM_CCMR1_OC2M_FORCE_LOW (0x4 << 12)
#define TIM_CCMR1_OC2M_FORCE_HIGH (0x5 << 12)
#define TIM_CCMR1_OC2M_PWM2 (0x7 << 12)
Utility functions to disable and enable outputs.
void disable_pwm(){
TIM1->CCER &= ~(TIM_CCER_CC1E | TIM_CCER_CC1NE | TIM_CCER_CC2E | TIM_CCER_CC2NE);
}
void enable_pwm(){
TIM1->CCER |= (TIM_CCER_CC1E | TIM_CCER_CC1NE | TIM_CCER_CC2E | TIM_CCER_CC2NE);
}
Here is how to force the H bridge to short the load to ground as an example.
TIM1->CCMR1 &= ~TIM_CCMR1_OC1M_Msk;
TIM1->CCMR1 |= TIM_CCMR1_OC1M_FORCE_LOW;
TIM1->CCMR1 &= ~TIM_CCMR1_OC2M_Msk;
TIM1->CCMR1 |= TIM_CCMR1_OC2M_FORCE_LOW;
Hope this will be useful to someone else!
Using IDE : AtmelStudio, uC = atmega328p, sensor DHT11
Im trying to read the values from DHT11.
I have set the start signal
DDRC |= 1 << TEMP_SENZOR; //output
_delay_ms(2000); // wait for 2s according to datasheet
PORTC &= ~ (1 << TEMP_SENZOR); //set low for at least 18 ms
_delay_ms(20);
PORTC |= 1 << TEMP_SENZOR; // set high
Then i have set getResponse metod
DDRC &= ~ (1 << TEMP_SENZOR); // set for input
while(PINC & (1 << TEMP_SENZOR));
while(PINC & (1 << TEMP_SENZOR) == 0);
while(PINC & (1 << TEMP_SENZOR));
I have tried to code function several times, to get values but unfortunately i get some values that have nothing to do with reading from Proteus DHT11.
Im new to this, so sorry if question is trivial.
I wrote a simple program to generate PWM wave with 50% duty cycle. Then I went for debugging in AtmelStudio. All registers except OCR0 were assigned there respective values. Why OCR0 not assigned any value.
ATmega32, Fast PWM.
#include <avr/io.h>
int main(void)
{
DDRB = (1 << PB3);
TCCR0 |= (1 << WGM01) | (1 << WGM00) | (1 << COM01);
OCR0 = 127;
TCCR0 |= (1 << CS02);
return 0;
}
So anyway.
You're using the 8-bit counter0 on your Atmega32. Let's see how you set it up:
// Set Pin B3 as output, others as input
DDRB = (1 << PB3);
// Set Clear on compare match + Fast PWM mode + Counter stopped
TCCR0 |= (1 << WGM01) | (1 << WGM00) | (1 << COM01);
// Set comparator value to 127
OCR0 = 127;
// Enable clkIO/256 from prescaler, turning on the counter
TCCR0 |= (1 << CS02);
Okay. First, a few things:
On initial setup, you usually want to assign the value and not or it, to be certain of its state.
Even after, setting it instead of or-ing it avoids a useless read. No impact on behavior for this register, but might be a bit better for performance.
The documentation recommends only enabling the output after you have set it up properly, to avoid spurious output. So you should move the first line last.
I'll be reading from that version of the datasheet.
Now, in fast PWM mode, according to table 38, and 40, the counter behaves like this:
It counts from BOTTOM to MAX (0 to 0xFF).
OCR0 is only used to toggle OC0 pin, not to reset the counting.
OCR0 has double-buffering. Its actual value is not updated until next cycle.
This might be your issue. If any of those are true:
Counter doesn't start properly (could happen if CS2-0 are not correct due to or-ing them instead of setting them).
Counter is stopped early (because your program ends and if the studio detects it, it could disable it at that point - I d'ont use the studio so I cannot really tell).
Then it is possible the value you write to the double buffer never makes it to the actual register. Alas the datasheet doesn't explain in detail how this is handled. Nor does it tell whether reading OCR0 while double buffering is active returns current value or awaiting value.
I have some trouble with, I guess, the overflow interrupt (used to increase resolution on 8-bit timer from 16µs/step to 1µs/step) in my CODE. It seems like the overflow interrupt triggers while the program is in the if-statements in my main loop and thereby screws thigns up!
if(pInt == 1) //PCNINT-if-statment
{
pulse16 = (tot_overflow << 8) | TCNT1; //adds tot_overflow and TCNT1 to be able to set if-statements in PCINT-while-loop with µs
if(PINB & (1 << PINB3)) //if PB3 is HIGH
{
TCNT1 = 0; //resets Timer/Counter1
tot_overflow = 0; //resets tot_overflow variable
}
else
{
if (pulse16 >1555) //when stick 1 travels from 1555 µs towards 2006 µs
{
PORTB &= ~(1 << relayPin); //relay pole switch, + & - on motor
PORTB |= (1 << greenLED); //LED green indicates forward motion
PORTB &= ~(1 << redLED); //turn off red LED
}
else if (pulse16 <1490) //when stick 1 travels from 1490 ms towards 920 µs
{
PORTB |= (1 << relayPin); //relay pole switch, - & + on motor
PORTB &= ~(1 << greenLED); //turn off green LED
PORTB |= (1 << redLED); //LED red indicates backward motion
}
else //if µs is 1490> or <1555 - dead-span to prevent gliteches on relay when stick is in centre position
{
}
}
pInt = 0; //resets pInt to exit PCNINT-if-statment
}
The pInt is a "flag-variable" that indicates PCINT is triggered.
The tot_overflow variable is increment every time the overflow interrupt is triggered.
I use an ATtiny85 as a RC-switch, it should go LOW on PB2-pin when µs from receiver is above 1555 and go HIGH when µs goes below 1490.
What happens is the following: when checking if µs is above 1555 or below 1490 and using overflow interrupt it sometimes turn the PB2-pin HIGH/LOW in the "dead-span" of 1490-1555 when it shouldn't, and sometimes outside the "dead-span"! Here's a VIDEO on the glitch. Notice that the green LED is the redLED, and the yellow LED is greenLED in my code.
I'm quite new at this and I'm not sure why this is happening and I don't understand why the code won't work. I have looked at the CTC function but I can't see that it would help since I still have to use the timer overflow interrupt to get my wanted reolution of 1µs/step.
EDIT
I have tried a couple of variations (of #yann-vernier's suggestions) and still don't get it to work properly, this is what gets closest to a working code:
while(1) //leave and/or put your own code here
{
static uint8_t tot_overflow; //variable to count the number of Timer/Counter1 overflows
if (TIFR & (1 << TOV1) )
{
TIFR |= (1 << TOV1); // clear timer-overflow-flag
tot_overflow ++;
}
if(GIFR & (1 << PCIF) ) //PCINT-flag idicates PCINT
{
uint16_t pulse; //variable to make a 16 bit integer from tot_overflow and TCNT1
// PORTB |= (1 << debugPin); //pin is HIGH on when interrupt is intialized
pulse = (tot_overflow << 8) | TCNT1; //adds tot_overflow and TCNT1 to be able to set if-statements in PCINT-while-loop with µs
this part I don't get to work:
if ( ((TIFR & (1 << TOV1)) && ((pulse & 0xff))) < 0x80)
{
pulse += 0x100; // Overflow had not been counted
}
Im not sure I get what is happening above, the only thing I know is that I probably do it the wrong way! When I comment the above part it works the same as mu old code!
else
{
if(PINB & (1 << PINB3)) //if PB3 is HIGH
{
TCNT1 = 0; //resets Timer/Counter1
tot_overflow = 0; //resets tot_overflow variable
}
else
{
if (pulse > 1555) //when stick 1 travels from 1555 µs towards 2006 µs
{
PORTB &= ~(1 << relayPin); //relay pole switch, + & - on motor
PORTB |= (1 << greenLED); //LED green indicates forward motion
PORTB &= ~(1 << redLED); //turn off red LED
}
else if (pulse < 1490) //when stick 1 travels from 1490 ms towards 920 µs
{
PORTB |= (1 << relayPin); //relay pole switch, - & + on motor
PORTB &= ~(1 << greenLED); //turn off green LED
PORTB |= (1 << redLED); //LED red indicates backward motion
}
else //if µs is 1490> or <1555 - dead-span to prevent gliteches on relay when stick is in centre position
{
// PORTB |= (1 << greenLED); //for debug to indicate dead-span
// PORTB |= (1 << redLED); //for debug to indicate dead-span
}
}
}
GIFR |= (1 << PCIF); //clear PCINT-flag
}
else
{
}
}
}
ISR(TIMER1_OVF_vect) //when Counter/Timer1 overflows
{
}
ISR(PCINT0_vect) //when pin-level changes on PB3
{
}
Is it close or am I still out in the blue?
Yes, the overflow interrupt could happen at any time (although it does happen regularly). So could the pin change interrupt. Each of them in this case only touch a single 8-bit volatile variable (tot_overflow and pInt respectively), which in turn are polled by your main loop. This construction doesn't really help you unless the main loop has other work to do which may take longer than one full timer period (256*8=2048 cycles in this case).
So instead of enabling interrupts, you could check GIFR bit PCIF (instead of pInt) and TIFR bit TOV1 in your main loop, which would get you more easily understood behaviour. Resetting them is a special case of writing a 1 to that bit and only that bit. This would save you time for the pin change interrupt, as the main loop check could happen more frequently and would not need the latency of jumping to the interrupt service routine.
It would not, however, fix your problem. You are trying to measure a pulse width, which on slightly larger AVRs is easily done using the timer input capture feature. The timers in the ATtiny85 don't have this feature. On AVRs that do, the input capture and timer overflow interrupts are arranged in such a way that the input capture interrupt can safely read the software driven overflow counter.
When you get a timer overflow, you increment tot_overflow, and when you've detected a pin change, you read TCNT1 to combine the values. These two counters, while one feeds the other, are not read at the same time. Your threshold values are 0x5d2 and 0x613. If the rollover occurs after reading tot_overflow but before reading TCNT1, you may well get a time like 0x501 at a time which should be 0x601. Similarly if you stop tot_overflow from updating. If you read TCNT1 before tot_overflow, you might get 0x6fe when you should have read 0x5fe. Simply put, there is no safe order - but there is a relation. What we need to know is if the overflow count value was up to date or not at the time the counter value was read.
static uint8_t ovf;
if (TIFR & 1<<TOV1) {
TIFR = 1<<TOV1; // Clear overflow flag
ovf++;
}
if (GIFR & 1<<PCIF) {
GIFR = 1<<PCIF; // clear pin change flag
uint16_t timestamp = ovf<<8 | TCNT1;
if (TIFR&1<<TOV1 && (timestamp&0xff)<0x80)
timestamp += 0x100; // Overflow had not been counted
// Do what we like with the timestamp here
}
The key is that we check for an overflow after we have already loaded the timer value. If the overflow occurred before we read the value, the value read must be low; otherwise we want the old count. This particular sample actually relies on the overflow not being handled in an interrupt, but you can use the same method by enclosing the block in an interrupt masking. We need the two reads of TOV1 and ovf to match, and to read TOV1 after TCNT1. What we don't want is to have an interrupt process and thus clear TOV1 so that we can't infer the order of ovf and TCNT1. Note that using a pin change interrupt to do this logic grants us that automatically, since interrupt handlers run with interrupts disabled.
You won't get higher precision on your pulse widths than the latency variance for responding to the pin change, and in the code you've shown, the timer reset (which should also reset the prescaler, bit PSR1 in GTCCR). This usually means you do want to read the timer in the interrupt handler itself. We can also observe that you could choose a timer pace that makes your thresholds fit in the 8 bit value; for instance with timer1 running at 8MHz/64, your thresholds would be at 186 and 194, with an offset of 16-24µs. One might even do tricks like setting one threshold precisely at an overflow since the timer doesn't have to start at 0.
#YannVernier Thanks for pushing me in the right direction, or giving me the rigth way to do it! ;-) I think I finally nailed it, with a litle extra help from a friend that is!
Here is the final CODE
I didn't first get that I had to remove the TIMSK enable ande sei() plus the ISR routines, also the else-statement that was accidently put after:
if ( ((TIFR & (1 << TOV1)) && ((pulse & 0xff))) < 0x80)
{
pulse += 0x100; // Overflow had not been counted
}
I am trying to do a data transfer control. A button with a pull-up resistor is connected to PINA3 of ATmega644P(Slave). I want to send some data when the button is pressed, if only the bus line is free. For testing purposes, I use another ATmega644P(Master) to send data ,which makes the bus busy, to the Slave one.
This is what I am trying to do: When the button is pressed, start the timer and wait for 300ms. During this time, if it receives some data, go to bus_not_free state. If the timer reaches 300ms while the bus is free, send the data. It works great when the bus is free, but sometimes, it sends the data when the bus is busy.
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "uart.h"
#include "bus_free_check.h"
#include "specs.h"
#define F_CPU 4000000UL
#define UART_BAUD_RATE 19200
enum states{
idle_s,
start_timer_s,
wait_s,
bus_check_s,
send_data_s,
bus_not_free_s
};
enum states state=idle_s;
int main(void)
{
DDRA = 0x00; // PORTA is input.
DDRB = 0xFF; // PORTB is output.
DDRC = 0xFF; // PORTC is output.
DDRD = 0b11111010; // PORTD input/output.
PORTA = 0x00;
PORTB = 0x00;
PORTC = 0x00;
TCCR1B = (1 << WGM12);
TIMSK1 = (1 << OCIE1A);
OCR1A = 1171; // 300ms
uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
sei();
while(1)
{
switch(state)
{
case idle_s:
bus_free=0;
PORTC=0x01;
if (bit_is_clear(PINA,3)) // If the button is pressed
{
while(bit_is_clear(PINA,3)); // Wait until it is unpressed
{
state=start_timer_s; // Start the timer
}
}
else
{
state=idle_s; // Wait in the idle_s until the button is pressed
}
break;
case start_timer_s:
TCCR1B |= (1 << CS10) | (1<< CS12); // Start the timer
state=wait_s; // Go to wait state and wait for 300ms
break;
case wait_s:
if (bit_is_clear(PIND,0)) // During this waiting, if at any time there is a data transfer going on, go to bus_not_free state.
{
TCCR1B &= ~((1 << CS10) | (1 << CS11) | (1 << CS12)); // Stop the timer
TCNT1=0; // Reset the counter
bus_free=0;
state=bus_not_free_s; // Go to bus_not_free state
}
break;
case send_data_s:
send_target_seri_no(); // Bus is free, you can send the data
TCCR1B &= ~((1 << CS10) | (1 << CS11) | (1 << CS12)); // After sending the data, stop the timer.
TCNT1=0; // And reset the counter.
state=idle_s; // Go to idle_s and get ready
break;
case bus_not_free_s:
uart_puts("Bus is not free \r"); // Bus is not free, can't send the data
state=idle_s; // Go to idle state, and don't give up hope
break;
default:
uart_puts("Fatal Error \r");
break;
}
}
}
ISR(TIMER1_COMPA_vect) // If the timer reaches 300ms, that means the bus is free
{
bus_free=1;
if(state==wait_s) // When the timer hits 300ms, if it is still in wait_s state, send the data.
{
state=send_data_s;
}
}
Thank you.
You've got several problems here. One of them is that your state machine is a little off.
Starting in the idle state, you only leave it if the button is pressed. Is this a mechanical button? If so, it needs to be debounced. That means, basically, waiting for it's state to become stable, and THEN determining if a button press has occurred. Spurious input from the button could cause you to pop in and out of the idle state, confusing both you and your program.
You are also abusing the timer a bit. You don't need the timer on if you are not trying to send something, so turn it off when you are done with it. You are also setting the timer over and over again while waiting for the timeout. Not necessary. I'm pretty sure the logic in the timer function is off. It's hard to tell what you meant to do.
The bus check state is the biggest problem. It does too much. You only need to start the timer once when moving out of the idle state. So separate that into another state. Then all the check state does is move to the send state when the bus is free. Your "three tries" loop is not doing anything: it doesn't wait for anything to change.
The reason you get stuck in the "bus not free" state is this: Suppose that the bus input is 0 (not busy) and bus_free=0. How will it ever set bus_free to 1? Your check time function will, in that case, turn off the timer, so the ISR never fires to change bus_free.
I did not get intended function but I can see two bugs.
1) useless code
for(int i=0; i>3; i++) // Try three times before canceling
{
state=bus_check_s; // Go back and check again
PORTB=0x00; // Led is off
bus_free=0; // Bus is not free
}
state=bus_not_free_s; // After 3 attempts, go to bus_not_free_s and warn the user
bus_free=0; // Bus is not free
it can be simplified to
state=bus_not_free_s;
PORTB=0x00;
bus_free=0; // Bus is not free
2) copy&paste error (if you want really stop timer)
TCCR1B |= (0 << CS10) | (0 << CS11) | (0 << CS12); // Stop the timer.
change to
TCCR1B &= ~((1 << CS10) | (1 << CS11) | (1 << CS12))
I have changed the if condition in wait_s as:
case wait_s:
// rec_char=uart_getc();
if ((unsigned char) rec_char) // During this waiting, if at any time there is a data transfer going on, go to bus_not_free state.
{
TCCR1B &= ~((1 << CS10) | (1 << CS11) | (1 << CS12)); // Stop the timer
TCNT1=0; // Reset the counter
state=bus_not_free_s; // Go to bus_not_free state
}
Now I am not facing the problem I stated in my question. I guess my mistake was to say bus is busy if I receive a zero bit only.