I am trying to toggle a LED with C code and CMSIS. However, what happens is that it doesn't toggle the LED, it just turns it on and that's it. It needs to toggle when the button is pressed.
this is my button configuration:
//Configure Port C pin 13 (Button)
RCC->AHBENR |= 0x80000;
GPIOC->MODER &= ~(00 << 26);
GPIOC->PUPDR &= ~(01 << 26);
this is my Led configuration:
RCC->AHBENR |= 0x20000;
GPIOA->MODER |= 0x400;
GPIOA->OTYPER |= 0x00;
GPIOA->PUPDR &= ~(00 << 10);
and this is the code that i have this far:
//Read pin state of Port C pin 13
if((GPIOC->IDR & (1<<13)) == 0)
{
if(!button_down)
{
GPIOA->ODR = 0b0100000;
}
button_down = 1;
}
else
{
button_down = 0;
}
Related
I'm testing some things on a Attiny85 and thought about the best way to handle the interrupt rutine. I know it is bad to have a lot of code in the interrupt handler, but I'm uncertain of any other ways to do this. I want my main program to sleep and wake on PCINT, the PCINT comes from multiple pins (rotary encoder A, b & switch and a receiving UART) so I was thinking just having a lot of code in the handler.
The code to determining which pin caused the interrupt, would look like this
#include <avr/io.h>
#include <stdint.h> // has to be added to use uint8_t
#include <avr/interrupt.h> // Needed to use interrupts
volatile uint8_t portbhistory = 0xFF; // default is high because the pull-up
int main(void)
{
DDRB &= ~((1 << DDB0) | (1 << DDB1) | (1 << DDB2)); // Clear the PB0, PB1, PB2 pin
// PB0,PB1,PB2 (PCINT0, PCINT1, PCINT2 pin) are now inputs
PORTB |= ((1 << PORTB0) | (1 << PORTB1) | (1 << PORTB2)); // turn On the Pull-up
// PB0, PB1 and PB2 are now inputs with pull-up enabled
PCICR |= (1 << PCIE0); // set PCIE0 to enable PCMSK0 scan
PCMSK0 |= (1 << PCINT0); // set PCINT0 to trigger an interrupt on state change
sei(); // turn on interrupts
while(1)
{
/*main program loop here */
}
}
ISR (PCINT0_vect)
{
uint8_t changedbits;
changedbits = PINB ^ portbhistory;
portbhistory = PINB;
if(changedbits & (1 << PB0))
{
/* PCINT0 changed */
}
if(changedbits & (1 << PB1))
{
/* PCINT1 changed */
}
if(changedbits & (1 << PB2))
{
/* PCINT2 changed */
}
}
And then ofc inside each of the if-statements in the interrupt handler, there would be code handling something, like this code, turning on the Timer0
TCNT0 = 0; // Set counter to 0
OCR0A = SERIAL_BIT_TIME; // Call timer interrupt in middle of first bit
position = 0; // Reset position and data
TIMSK |= 1 << OCIE0A; // Enable interrupt for compare register A (timer interrupt)
TIFR |= 1 << OCF0A; // Clear timer interrupt flag to prevent it jumping directly there
PCMSK &= ~(1 << SERIAL_RECEIVE); // Disable pin change interrupt
or with the switch input, the code inside the if-statement would be
if (lightState)
{
dali.transmit(ADDRESS, OFF);
lightState = 0;
}
else
{
dali.transmit(ADDRESS, ON);
lightState = 1;
}
Would this be a dumb solution?
volatile uint8_t flag;
int main(void)
{
DDRB &= ~((1 << DDB0) | (1 << DDB1) | (1 << DDB2)); // Clear the PB0, PB1, PB2 pin
// PB0,PB1,PB2 (PCINT0, PCINT1, PCINT2 pin) are now inputs
PORTB |= ((1 << PORTB0) | (1 << PORTB1) | (1 << PORTB2)); // turn On the Pull-up
// PB0, PB1 and PB2 are now inputs with pull-up enabled
PCICR |= (1 << PCIE0); // set PCIE0 to enable PCMSK0 scan
PCMSK0 |= (1 << PCINT0); // set PCINT0 to trigger an interrupt on state change
sei(); // turn on interrupts
while(1)
{
gotosleep();
do
{
switch(flag)
{
case 1:
dosomething1();
break;
case 2:
dosomething2();
break;
case 3:
dosomething3();
break;
}
cli();
flag = 0;
sei();
}while(flag);
}
}
ISR (PCINT0_vect)
{
uint8_t changedbits;
changedbits = PINB ^ portbhistory;
portbhistory = PINB;
if(changedbits & (1 << PB0))
{
flag = 1;
}
if(changedbits & (1 << PB1))
{
flag = 2;
}
if(changedbits & (1 << PB2))
{
flag = 3;
}
}
I try to activate a timer at the first detection on falling edge of hardware interrupt detection and to deactivate the timer after 8 runs. But when I stop the timer, the hardware interrupt triggers again and starts the timer immediately.
In the image you can ignore the blue signal. The purple signal is timer1 toggling the pin. Green is hardware interrupt toggling the pin.
All it has to do is trigger at the first falling edge, then toggling a pin in a period of time.
My question is: why does hardware interrupts trigger twice?
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
volatile int cnt = 0;
uint8_t data[8];
int main(void)
{
// TIMER 1
DDRB |= (1 << PORTB0); // Set LED as output
DDRB |= (1 << PORTB1); // Set LED as output
DDRB |= (1 << PORTB2); // Set LED as output
DDRD &= ~(1 << PORTD2); // Set LED as INPUT
PORTD |= (1 << PORTD2); // PULLUP resistor
TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode
TIMSK1 &= ~(1 << OCIE1A); // Disable CTC interrupt
//TIMSK1 |= (1 << OCIE1A); // Enable CTC interrupt
OCR1A = 1200; // Set CTC compare value to 15873Hz at 16MHz AVR clock, with a prescaler of 1
//TCNT0 = 0;
TCCR1B |= ((1 << CS10)); // Start timer at Fcpu/1
// Interrupt
EICRA |= (1 << ISC01) | (1 << ISC11); //ENABLE INT0 and INT1 ON falling EDGE
EIMSK |= (1 << INT0); //ENABLE INT0
EIMSK |= (1 << INT1); //ENABLE INT0
sei(); // Enable global interrupts
while (1)
{
}
}
ISR(TIMER1_COMPA_vect)
{
cnt = cnt + 1;
PORTB ^= (1 << PORTB0); // D8
if (cnt > 7)
{
TIMSK1 &= ~(1 << OCIE1A); // stop CTC interrupt
EIMSK |= (1 << INT0); //Enable INT0
EIMSK |= (1 << INT1); //Enable INT0
return;
}
}
ISR(INT0_vect) //External interrupt_zero ISR
{
EIMSK &= ~(1 << INT0); //Disable INT0
PORTB ^= (1 << PORTB2); // Toggle the LED
TCNT1 = 0;
cnt = 0;
TIMSK1 |= (1 << OCIE1A); // Enable CTC interrupt
}
ISR(INT1_vect) //External interrupt_zero ISR
{
PORTB ^= (1 << PORTB2); // Toggle the LED
EIMSK &= ~(1 << INT1); //Disable INT0
TCNT1 = 0;
cnt = 0;
TIMSK1 |= (1 << OCIE1A); // Enable CTC interrupt
}
The falling edge sets the interrupt flag even if you disable it. This is called a "pending" interrupt. As soon as the interrupt is enabled, its service routine is called (given that all other enabling conditions are met).
You need to clear this pending flag before you enable the interrupt.
I'm trying to run two functions 'similtaniously' via interrupts:
1) Measure ADC via timing of timer 0 (100Hz) and show results on pin 0-5
2) Blink a led via timer 1 (10Hz) on pin 6.
Problem seems to be that the ISR of timer 1 blocks the function, so nothing else is executed. Here is the code:
(Please don't be offended by any styling mistakes, the code is under development)
#define F_CPU 16000000UL // 16MHz Clock speed
#include <avr/io.h>
#include <avr/interrupt.h>
void ADC_init(void);
void SetTimer0(void);
void SetTimer1(void);
int main(void)
{
DDRB |= (1<<DDB0) + (1<<DDB1) + (1<<DDB2) + (1<<DDB3) + (1<<DDB4) + (1<<DDB5) + (1<<DDB6);
PORTB = 0b00000000;
DDRB &= ~(1<<DDB7);
ADC_init();
SetTimer0();
SetTimer1();
while(1){
}
}
void ADC_init(void)
{
cli();
// Select Vref=AVcc
// and set left adjust result
// select pin ADC0 (PC0)
ADMUX |= (1<<REFS0)|(1<<ADLAR);
//and enable ADC
//enable ADC interupt
//enable autotriggering
//set prescaller to 128
ADCSRA |= (1<<ADEN) | (1<<ADATE) | (1<<ADIE) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
//set ADC trigger source - Timer0 compare match A
ADCSRB |= (1<<ADTS1)|(1<<ADTS0);
// StartADC
ADCSRA |= (1<<ADSC);
sei();
}
//initialize timer0 match A on 100hz
void SetTimer0(void)
{
cli();
TCCR0A = 0; // set entire TCCR0A register to 0
TCCR0B = 0; // same for TCCR0B
TCNT0 = 0; // initialize counter value to 0
// set compare match register for 100Hz increments
OCR0A = 155; // = 16000000 / (1024 * 100.16025641025641)-1
// toggle PD6/OC0A pin on compare match
TCCR0A |=(1<<COM0A0)|(1<<WGM01);
//Set CTC mode
TCCR0B |= (1 << WGM01);
// Set CS02, CS01 and CS00 bits for 1024 prescaler
TCCR0B |= (1 << CS02) | (0 << CS01) | (1 << CS00);
// enable timer compare interrupt
TIMSK0 |= (1 << OCIE0A);
sei();
}
//initialize timer1 10hz
void SetTimer1(void){
cli();
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
TCNT1 = 0; // initialize counter value to 0
// set compare match register for 10 Hz increments
OCR1A = 24999; // = 16000000 / (64 * 10) - 1 (must be <65536)
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS12, CS11 and CS10 bits for 64 prescaler
TCCR1B |= (0 << CS12) | (1 << CS11) | (1 << CS10);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
sei();
}
// ADC done interrupt
ISR(ADC_vect)
{
// Clear timer compare match flag
TIFR0=(1<<OCF0A);
// save ADC measurement
uint16_t val = ADC;
// show ADC results
if (val < 100)
{ PORTB = 0b00000000; }
else if (val < 300)
{ PORTB = 0b00000001; }
else if (val < 550)
{ PORTB = 0b00000011; }
else if (val < 850)
{ PORTB = 0b00000111; }
else if (val < 1020)
{ PORTB = 0b00001111; }
else
{ PORTB = 0b00011111; }
}
ISR(TIMER1_COMPA_vect)
{
//PORTB ^= PINB5;
static uint16_t on = 0;
if (on == 1){
PORTB = 0b00100000;
on = 0;
}
else {
PORTB = 0b00000000;
on = 1;
}
}
When the SetTimer1() function is disabled the DCA runs as expected. So individually are are both ISR's working fine, but together not. Could someone help me solve this problem?
You have output compare interrupt enabled fro Timer0:
// enable timer compare interrupt
TIMSK0 |= (1 << OCIE0A);
but no ISR handler for that interrupt.
The default __bad_interrupt handler just performs jump to zero interrupt vector, i.e. restarts the program.
That means if you have an interrupt enabled there should be an ISR for that interrupt.
I'm trying to get encoder data to my stm32 h7 and display counter value through uart.
I can see counter value 0 through my uart terminal but counter value never goes up...
I'm using encoder mode.
Please, help me to do this
static void Encoder_init(void)
{
RCC->APB2RSTR &= ~(1 << 1);
RCC->APB2ENR |= (1 << 1); //TIM8 clk enable
TIM8->CR1 &= ~(1 << 0); //tim8 disable
TIM8->SMCR |= (0x03 << 0); //SMS set
TIM8->CCER &= ~(1 << 1); //CC1P
TIM8->CCER &= ~(1 << 5); //CC2P
TIM8->CCER &= ~(1<<3); //CC1NP LOW
TIM8->CCER &= ~(1<<7); //CC2NP LOW
TIM8->CCMR1 |= (1<<0); //CC1S
TIM8->CCMR1 |= (1<<8); //CC2S
TIM8->PSC = 0; //prescaler zero
TIM8->ARR = 0xFFFF;
TIM8->CR1 |= (1 << 0); //tim8 enable}
}
void tEncoder(void *pvParameters)
{
uint8_t encoder_data;
char buf[4];
char val;
RCC->AHB4RSTR &= ~(1 << 2);
RCC->AHB4ENR |= (1 << 2);
GPIOC->MODER &= ~(0x03 << 12);
GPIOC->MODER &= ~(0x03 << 14);
GPIOC->PUPDR |= (1<<6) | (1<<7);
Encoder_init();
while(1) {
vTaskDelay(1000);
if(encoder_data!=TIM8->CNT) {
encoder_data = TIM8->CNT;
int2str(buf, encoder_data);
uart7_buffer_putstr(buf);
SystemPrint("tEncoder counting\n\r");
}
}
}
Im not able to get any data out from the MOSI pin, or the CLK pin when measuring with an oscilloscope on the pins.
this is my code:
include
include "stm32f407xx.h"
void delay(){
int i;
for(i = 0; i < 1000000; i++);
}
int main(){
// Enable RCC APB2 clock for SYSCFG (for externalinterrupt)
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
// Enable RCC APB1 clock for SPI2
RCC->APB1ENR |= (1 << 14);
// GPIOA for pushbutton
RCC->AHB1ENR |= (1 << 0);
// GPIOB for SPI
RCC->AHB1ENR |= (1 << 1);
// GPIOD for LED
RCC->AHB1ENR |= (1 << 3);
/////// GPIO B init for MOSI, MISO och CLK pins:
GPIOB->MODER |= (2 << (2*13)); //Sets pin 13 as Alternate function
GPIOB->MODER |= (2 << (2*14)); //Sets pin 14 as Alternate function
GPIOB->MODER |= (2 << (2*15)); //Sets pin 15 as Alternate function
GPIOB->OTYPER |= PUSH_PULL_PIN_13;
GPIOB->OTYPER |= PUSH_PULL_PIN_14;
GPIOB->OTYPER |= PUSH_PULL_PIN_15;
GPIOB->OSPEEDR |= OSPEEDR_MEDIUM_PIN_13;
GPIOB->OSPEEDR |= OSPEEDR_MEDIUM_PIN_14;
GPIOB->OSPEEDR |= OSPEEDR_MEDIUM_PIN_15;
GPIOB->PUPDR |= PUPDR_PULLDOWN_PIN_13; // Pull down for CLK
GPIOB->PUPDR |= PUPDR_PULLUP_PIN_14; // Pull up for MISO
GPIOB->PUPDR |= PUPDR_PULLUP_PIN_15; // Pull up for MOSI
GPIOB->AFR[1] |= ALT_FUNC_AF5_PIN_13; // 5 för AF5 till pin 13
GPIOB->AFR[1] |= ALT_FUNC_AF5_PIN_14; // 5 för AF5 till pin 14
GPIOB->AFR[1] |= ALT_FUNC_AF5_PIN_15; // 5 för AF5 till pin 15
///////// GPIO A init for pushbutton
GPIOA->MODER |= MODER_INPUT_PIN_0;
GPIO D init for LED
GPIOD->MODER |= MODER_OUTPUT_PIN_12; //Sets pin 12 as output
GPIOD->MODER |= MODER_OUTPUT_PIN_13; //Sets pin 13 as output
GPIOD->MODER |= MODER_OUTPUT_PIN_14; //Sets pin 14 as output
GPIOD->MODER |= MODER_OUTPUT_PIN_15; //Sets pin 15 as output
GPIOD->OTYPER |= PUSH_PULL_PIN_12;
GPIOD->OTYPER |= PUSH_PULL_PIN_13;
GPIOD->OTYPER |= PUSH_PULL_PIN_14;
GPIOD->OTYPER |= PUSH_PULL_PIN_15;
GPIOD->OSPEEDR |= OSPEEDR_MEDIUM_PIN_12;
GPIOD->OSPEEDR |= OSPEEDR_MEDIUM_PIN_13;
GPIOD->OSPEEDR |= OSPEEDR_MEDIUM_PIN_14;
GPIOD->OSPEEDR |= OSPEEDR_MEDIUM_PIN_15;
GPIOD->PUPDR |= PUPDR_PULLDOWN_PIN_12; //Frågetecken på denna, ska det vara 0 1 eller 2??
GPIOD->PUPDR |= PUPDR_PULLDOWN_PIN_13; //Frågetecken på denna, ska det vara 0 1 eller 2??
GPIOD->PUPDR |= PUPDR_PULLDOWN_PIN_14; //Frågetecken på denna, ska det vara 0 1 eller 2??
GPIOD->PUPDR |= PUPDR_PULLDOWN_PIN_15; //Frågetecken på denna, ska det vara 0 1 eller 2??
GPIOD->AFR[1] |= ALT_FUNC_AF0_PIN_12; // 0 för AF0 till pin 12
GPIOD->AFR[1] |= ALT_FUNC_AF0_PIN_13; // 0 för AF0 till pin 13
GPIOD->AFR[1] |= ALT_FUNC_AF0_PIN_14; // 0 för AF0 till pin 14
GPIOD->AFR[1] |= ALT_FUNC_AF0_PIN_15; // 0 för AF0 till pin 15
//////// SPI init
SPI2->CR1 |= (0 << CR1_CLOCK_PHASE); //Set clockphase in CR1 register
SPI2->CR1 |= (1 << CR1_CLOCK_POLARITY); //Set polarity in CR1 register
SPI2->CR1 |= (4 << CR1_BAUD_RATE); //Set baudrate
SPI2->CR1 |= (1 << CR1_DATA_FRAME_FORMAT); //16-bit transmission
SPI2->CR1 |= (1 << CR1_ENABLE_SPI); //Enable SPI
SPI2->CR1 |= (1 << CR1_MASTER_SELECT); //MASTER SELECTION
SPI2->CR1 |= (1 << CR1_LSB_FIRST); //LSB FIRST
SPI2->CR1 |= (1 << CR1_SLAVE_SELECT); //SSI set
SPI2->CR1 |= (1 << CR1_SW_SLAVE_MANAGE); //SSM, software slave management selected, which means SSI will be used insted of I/O
SPI2->CR1 |= (0 << CR1_RECEIVE_ONLY); //Transmit and Receive
SPI2->CR1 |= (0 << CR1_CRC_TRANSFER); //NOT USED, CRC
//SPI2->CR1 |= (0 << CR1_CRC_CALCULATION); //NOT USED, CRC calculation disabled
//SPI2->CR1 |= (0 << CR1_BIDIOE); //enable transmit-only if set
SPI2->CR1 |= (0 << CR1_BIDIMODE); //1-line bidirectional mode if set
//////// Enable external interrupt
//SYSCFG->EXTICR[0] |= (0 << 0); //Enable
EXTI->IMR |= (1 << 0); //Enable interrupt
EXTI->RTSR |= (1 << 0); //set Risnig edge interrupt
EXTI->FTSR |= (0 << 0); //falling edge interrupt (disabled)
NVIC_EnableIRQ(EXTI0_IRQn);
SPI2->CR2 |= (1 << 7);
while(1){
GPIOD->ODR |= (1 << 12); // led ON
delay();
GPIOD->ODR &= ~(1 << 12); // led OFF
delay();
}
return 0;
}
void EXTI0_IRQHandler(){
EXTI->PR |= (1 << 0); //Clears interrupt pending bit
while(1){
GPIOD->ODR |= (1 << 13); //Blå led ON
delay();
GPIOD->ODR &= ~(1 << 13); //Blå led OFF
delay();
// Here I want to Send Data
SPI2->DR |= 0xFF;
}
}
Please use appropriate macros definition from stm32f407xx.h file. It's much better to read:
TIM2->DIER |= TIM_DIER_UIE; /* interrupt enable on 1st channel */
than:
TIM2->DIER |= (1 << 1); /* interrupt enable on 1st channel */
It will avoid confusions, and maybe indicate where is your problem. In current code format it is much time taking to decode if You set registers correctly.
edit: ok, got some smell:
GPIOD->AFR[1] |= (0 << (30)); // 5 för AF0 till pin 15
AFR registers are 4 bits per pin. Why are You shifting value by 30 ? Shouldn't it be 28 ?
edit_2: Is your push button triggers EXTI irq ? If yes -- that is not good...
I don't remember if on this board is any hardware that provide button debounce funcionality, even though, You should put it in code. Now, interrupt may be triggered many, many times instead of 1 irq = 1 pushbutton press.
edit_3:
SPI2->CR2 |= (1 << 7);
Enable irq when TX buffer empty. Buffer is empty, untill You press the button. This causes not-stoping invoking of SPI2 interrupt, and You don't have defined handler for it. Try to clarify what You are trying to achieve, and how this should be done.