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!
Related
So I have an assignment that wants me to perform certain code when a button on PD2 is presed.
The problem i'am having right now is i don't really know what to check for neither do
i understand the underlying logic.
So this is the code i've come up with thus far.
DDRD=0x00; //PORTD pin 0 as input
PORTD=0x00;
DDRB=0xFF; //PORTB as output
PORTB=0x00;
while(1){
if (PIND & (1<<PD2)==1) // check if PD2 is pressed on PIND2
{
// modify bits on PORTB here
}
}
I'm using Atmega328 and running this on AtmelStudio 7
To set PD2 to an input, run this C/C++ code, which clears bit 2 in register DDRD. There is no need to modify entire registers:
DDRD &= ~(1 << 2);
(You could skip this because every pin will be an input by default after the AVR resets.)
I don't know how you wired your button. If the button is wired between the pin and GND, and there is no external pull-up resistor to pull your pin high, then you should enable the internal pull-up after the pin is an input by running this code, which sets bit 2 in register PORTD:
PORTD |= 1 << 2;
Now to read the state of the button, you can use this C/C++ expression, which will evaluate to 0 if the pin is low or non-zero if the pin is high:
PIND & (1 << 2)
The expression below also works. It has the advantage of always evaluating to 0 or 1:
PIND >> 2 & 1
Here's some (untested) code that ties it all together, reading from PD2 and writing the value of PD2 to an output on PB3:
#include <avr/io.h>
int main()
{
// Make PD2 an input, pulled high.
DDRD &= ~(1 << 2);
PORTD |= 1 << 2;
// Make PB3 an output.
DDRB |= 1 << 3;
while (1)
{
if (PIND >> 2 & 1)
{
// PD2 is high, so drive PB3 high.
PORTB |= 1 << 3;
}
else
{
// PD2 is low, so drive PB3 low.
PORTB &= ~(1 << 3);
}
}
}
You might have to adapt the part of the code that deals with the output. I don't actually know what pin your output is on and what kind of behavior you want, so I just did something simple.
This code is full of the C bitwise operators. It is important for you to get a good book about C and learn exactly what those operators do, and this answer would be too long if I attempted to teach you all of them. Here is a list of operators for you to learn: <<, >>, &, &=, |, |=, ~.
unsigned char input_byte;
while(1){
__no_operation(); /* using nop for synchronization */
input_byte = PIND; /* read entire PIND */
}
This way you should be able to read your inputs every program cycle. But I've not tested this. It's important to use __no_operation() before reading again PIND.
I have been programming the stm32l412kb nucleo board, attempting to achieve basic UART communication. Transmission from the board works great but the board is not appearing to receive any data.
For the software side, I have tried using standard HAL code in a few ways different, in both interrupt and non-interrupt mode. I have tied a more basic approach (shown below). From debugging line by line I have found that the Receive Data register (RDR) is not filling (and consequently the flag which sets when there is data there is not setting). This has been the error in each case.
This aim of this code is to send back the character entered.
#include "stm32l4xx.h"
int main(void)
{
/* USER CODE BEGIN 1 */
/*The Usart2 peripheral needs its clock to be enabled.*/
RCC->APB1ENR1 |= RCC_APB1ENR1_USART2EN;
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;
/*The 72 MHz APB1 bus clock with a 9600baud rate gives a baud rate for the register of 0x1D4C*/
USART2->BRR = 0x1D4C;
/*For USART2 we need to enable the overall UART (U) driver, the transmission lines(T) and the reading lines(R). UART Enable is last.*/
USART2->CR1 |= USART_CR1_RE | USART_CR1_TE | USART_CR1_UE;
/*Setting transmission pin*/
GPIOA->MODER |= GPIO_MODE_AF_PP;
GPIOA->OSPEEDR |= GPIO_SPEED_FREQ_HIGH;
/* USER CODE END 1 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if (USART2->ISR & USART_ISR_RXNE) //if RX is not empty
{
char temp = USART2->RDR; //fetch the data received
USART2->TDR = temp; //send it back out
while (!(USART2->ISR & USART_ISR_TC)); //wait for TX to be complete
}
}
return 0;
}
To send the data I have used RealTerm Serial Capture and have tried also the stm32cubeIDE console. One possibility of the source of the problem is that the DataSheet says
"In the USART, the start bit is detected when a specific sequence of samples is recognized. This sequence is: 1 1 1 0 X 0 X 0 X 0 0 0 0."
I have not coded any way of leading my data with this, however, from all the examples I have seen from a couple of books as well as videos, they did not need to think about this and it worked perfectly. Could it be a hardware problem? Is there something I'm not initialising? I have even tried different cables.
Many thanks in advance for any help,
Harry
/*********************************UPDATE**************************************/
First and foremost, thank you very much for the help, I now understand basics such as how to use the datasheet to configure the registers. It is much appreciated. I have updated my code but still the problem remains.
So I have updated my configuration as so:
/*Configuring GPIO Pins*/
/*Clearing whatever is held in the mode registers for pins 2 and 3 (Inverting with their masks.)*/
GPIOA -> MODER &= ~(GPIO_MODER_MODE2_Msk | GPIO_MODER_MODE3_Msk);
/*The 2 bits 10 are being shifted to the position which configures Mode of pin 2 and also for pin 3 in the mode register.
*(10 is alterntive function mode).*/
GPIOA -> MODER |= (0b10 << GPIO_MODER_MODE2_Pos) | (0b10 << GPIO_MODER_MODE3_Pos);
/*Clearing whatever is held in the output speed registers for pins 2 and 3*/
GPIOA -> OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED2_Msk | GPIO_OSPEEDR_OSPEED3_Msk);
/*Setting the speed of pins 2 and 3 to be very high(11)*/
GPIOA -> OSPEEDR |= (0b11 << GPIO_OSPEEDR_OSPEED2_Pos) | (0b11 << GPIO_OSPEEDR_OSPEED3_Pos);
/*Clearing whatever is held in the alternative function registers for pins 2 and 3.*/
GPIOA -> AFR[0] &= ~(GPIO_AFRL_AFSEL2_Msk | GPIO_AFRL_AFSEL3_Msk);
/*Setting the pins 2 and 3 to their alternative functions(TX and RX)*/
GPIOA -> AFR[0] |= (7 << GPIO_AFRL_AFSEL2_Pos) | (7 << GPIO_AFRL_AFSEL3_Pos);
/*Clock Configuration*/
/*Enabling the USART2 peripheral clock.*/
RCC->APB1ENR1 &= ~(RCC_APB1ENR1_USART2EN_Msk);
RCC->APB1ENR1 |= (0b1 << RCC_APB1ENR1_USART2EN_Pos);
/*Enabling the GPIOA port peripheral clock*/
RCC->AHB2ENR &= ~(RCC_AHB2ENR_GPIOAEN_Msk);
RCC->AHB2ENR |= (0b1 << RCC_AHB2ENR_GPIOAEN_Pos);
/*USART Configuartion*/
/*The 72 MHz APB1 bus clock with a 9600baud rate gives a baud rate for the register of 0x1D4C*/
USART2->BRR = 0x1D4C;
/*For USART2 we need to enable the overall UART (U) driver, the transmission lines(T) and the reading lines(R). UART Enable is last.*/
USART2->CR1 &= ~(USART_CR1_RE_Msk | USART_CR1_TE_Msk | USART_CR1_UE_Msk);
USART2->CR1 |= USART_CR1_RE | USART_CR1_TE | USART_CR1_UE;
Which has greatly developed my understanding of how to properly configure the device. However, I'm still having a problem with the overall aim of the code to bounce back a character, as the data is still not being read by the MCU. I will pursue on and update if it's successful. I'm thankful for any further suggestions.
this does not initialize the GPIO MODER or OPEEDR regiters.
GPIOA->MODER |= GPIO_MODE_AF_PP;
GPIOA->OSPEEDR |= GPIO_SPEED_FREQ_HIGH;
GPIO_MODE_AF_PP & GPIO_SPEED_FREQ_HIGH are HAL definitions and cant be used on the register level.
You need to set the appropriate values for every pin you use:
It will never receive or send anything as you forgot to set the GPIO -> AF registers and the hardware is not connected to the pins internally.
You can find the alternate functions mappin in the Datasheet
and the AF GPIO registers in the Reference Manual
this sequence should be:
GPIOA -> MODER &= ~(GPIO_MODER_MODE2_Msk | GPIO_MODER_MODE3_Msk);
GPIOA -> MODER |= (0b10 << GPIO_MODER_MODE2_Pos) | (0b10 << GPIO_MODER_MODE3_Pos);
GPIOA -> OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED2_Msk | GPIO_OSPEEDR_OSPEED23Msk);
GPIOA -> OSPEEDR |= (0b11 << GPIO_OSPEEDR_OSPEED2_Pos) | (0b11 << GPIO_OSPEEDR_OSPEED3_Pos);
GPIOA -> AFR[0] &= ~(GPIO_AFRL_AFSEL2_Msk | GPIO_AFRL_AFSEL3_Msk);
GPIOA -> AFR[0] |= (7 << GPIO_AFRL_AFSEL2_Pos) | (7 << GPIO_AFRL_AFSEL3_Pos);
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 tried to create a CTC timer interrupt on my ATmega32U4 leonardo board. When I continuously check the value of OCF1A I have no problem detecting when the output reaches the desired value, however once I move the code into an interrupt, the interrupt never triggers.
Timer setup:
#include <avr/io.h>
void setupTimer()
{
TCCR1B |= (1 << WGM12); // CTC mode
TCCR1B |= ((0 << CS10) | (0 << CS11) | (1 << CS12)); // set up prescaler
OCR1A = 6249; // 100 ms set up output compare value for interrupt
TIMSK1 |= (1 << OCIE1A); // enable interrupt on clock compare
}
The loop that works:
setupTimer();
for (;;) {
if (TIFR1 & (1 << OCF1A)) {
PORTC ^= (1 << PORTC7);
TIFR1 = (1 << OCF1A);
}
}
The interrupt that does not work:
#include <avr/interrupt.h>
ISR(TIMER1_COMPA_vect) {
PORTC ^= (1 << PORTC7);
}
I must be missing something as from what I have seen in the tutorials the above code should work. One interesting observation here is that if I have both the loop and the interrupt in my code at once if I call sei(), the LED does not blink as if the OCF1A register was cleared prematurely.
I'm pretty sure it is irrelevant in this case but the fuses are as follows: E:CB, H:D8, L:FF.
I use avr-g++ to compile and the code is spread out between several files.
Given that someone got here through google two years after this question was asked I suppose I should share my own findings on this matter.
The code provided in my question is correct and assuming that there is a call to sei()somewhere after the setupTimer() the interrupt should trigger correctly. The issue was just as c0redumb described in his answer - the bootloader was messing with some registers and thus preventing the code from running correclty. However my solution to this problem was slightly different as in my case, the interrupt would not trigger even after unplugging and re-plugging the board (it is possible that the bootloader has changed in the two years since I have asked this question).
The simplest way to prevent a conflict between the code and the bootloader is to simply remove the bootloader. By using USBasp programmer one can simply load their own code onto the board and thus be sure that it is the only thing running on the CPU.
You have two problems:
You need to make sure main() doesn't return even when it's not doing anything except waiting for the interrupt
You need to enable interrupts via sei() after setting everything up.
Here's a working example (I changed the LED port to PB5 because I've tested this on an Arduino Uno and that already has an LED built-in)
#include <avr/interrupt.h>
#include <avr/io.h>
void main ()
{
DDRB |= 1 << DDB5;
TCCR1B |= 1 << WGM12;
TCCR1B |= 1 << CS12;
OCR1A = 6249;
TIMSK1 |= 1 << OCIE1A;
sei();
for(;;);
}
ISR(TIMER1_COMPA_vect)
{
PORTB ^= 1 << PORTB5;
}
This issue puzzles me today as well. Through search I found your question. I did some more searches all around and found no answer to this one. I had thought that I must have forgotten to enable some circuit or set some flags. Finally, with an LED as my debugger, I have found the cause.
The problem is with the bootloader, not your code. To get it to work, you just unplug the board from USB (after having written code through bootloader), and re-plugin so the bootloader jumps directly to your code at powerup, and it works there. The bootloader must have done some fancy footwork in uploading the code, and didn't work all well afterward in this situation.
As a reference, I used a ProMicro board which I believe has Caterina bootloader same as the Leonardo board you used.
#define F_CPU 8000000UL
#include <avr/io.h>
/*
* main -- Main program
*/
int main(void)
{
/* Set OC1A pin to be an output */
DDRD|=(1<<5);
/* Set output compare register value */
OCR1A = 4000;
/* Set timer counter control registers A and B so that
* - mode is - clear counter on compare match
* - output compare match action is to toggle pin OC1A
* - correct clock prescale value is chosen.
* TCCR1C can just stay as default value (0).
*/
TCCR1A |=(1<<COM0A0) | (1<<WGM12);
TCCR1B |= (0<<CS12) | (1<<CS11) | (1<<CS10) | (1<<WGM12) | (1<<WGM12);
while(1){
}
}
I have a led linked to the OC1A port, yet it never flashes, some help would be much appreciated.
I have scoured the data sheet and do not comprehend what else must be done in order to make the led flash, i am sure it would be simple for someone with any c knowledge.
First of all, when setting registers like TCCR1A you need to simply assign the value you want to put into it, not OR the bits you want to set with what is already in there as you'll get an unwanted mix of old & new.
Then try changing this:
TCCR1A = (1 << COM1A0); //COM1A0 in stead of COM0A0, and WGM12 is not part of TCCR1A
TCCR1B = (1 << CS11) | (1 << CS10) | (1 << WGM12); //No need to write (0 << x) for bits you don't want set, WGM12 is part of TCCR1B
If the led is flashing too fast, increase OCR1A and/or change the prescaler (cs 10, 11 and 12)