Sorry for my english. I have some issues on attiny13 with pwm. pwm works perfectly on adjustment. However while OCR0B value is going to zero or max, led not complete off or on. I tried use " PORTB &= ~ (1<<); " while OCROX was zero, but it doesnt work. afterwards I find that I must add " ISR(TIM0_COMPA_vect)" for interrupt. I have recently begun work on tiny ,so its too hard especially avr side for me and pls ignore my illiteracy. This is code, ı used.
#define F_CPU 1200000
#define LED PB1
#include <avr/io.h>
const int buttonPin = 4; // the pin that the pushbutton is attached to
int buttonPushCounter = 0; // counter for the number of button presses
int buttonState = 0; // current state of the button
int lastButtonState = 1;
int model; // previous state of the button
void setup() {
// initialize the button pin as a input:
pinMode(buttonPin, INPUT);
// initialize the LED as an output:
}
void adc_setup (void)
{
// Set the ADC input to PB2/ADC1
ADMUX |= (1 << MUX0);
ADMUX |= (1 << ADLAR);
// Set the prescaler to clock/128 & enable ADC
// At 9.6 MHz this is 75 kHz.
// See ATtiny13 datasheet, Table 14.4.
ADCSRA |= (1 << ADPS1) | (1 << ADPS0) | (1 << ADEN);
}
void pwm_setup (void)
{
// Set Timer 0 prescaler to clock/8.
// At 9.6 MHz this is 1.2 MHz.
// See ATtiny13 datasheet, Table 11.9.
TCCR0B |= (1 << CS01);
// Set to 'Fast PWM' mode
TCCR0A |= (1 << WGM01) | (1 << WGM00);
// Clear OC0B output on compare match, upwards counting.
TCCR0A |= (1 << COM0B1);
}
void pwm_write (int val)
{
OCR0B = val;
}
void button()
{
buttonState = digitalRead(buttonPin);
// compare the buttonState to its previous state
if (buttonState != lastButtonState) {
// if the state has changed, increment the counter
if (buttonState == HIGH) {
// if the current state is HIGH then the button went from off to on:
buttonPushCounter++;
model = buttonPushCounter % 5 ;
} else {
// if the current state is LOW then the button went from on to off:
}
// Delay a little bit to avoid bouncing
delay(250);
}
lastButtonState = buttonState;
}
int main (void)
{
int adc_in;
// LED is an output.
DDRB |= (1 << LED);
adc_setup();
pwm_setup();
while (1) {
// Get the ADC value
button();
//adc_in = adc_read();
// Now write it to the PWM counter
switch(model)
{
case 0:
pwm_write(0);
//PORTB &= ~ (1<<PB3);
break;
case 1:
pwm_write(50);
break;
case 2:
pwm_write(100);
break;
case 3:
pwm_write(150);
break;
case 4:
pwm_write(255);
//PORTB |= (1<<PB3);
break;
}
}
}
I find another code successfully turn on, off and adjust led via using interrupt with two button, but I dont understand how code write values on OCR0X. Could somebody can explain me the connection between OCR0X and duty(count, pwm value) at the second code . Because ı need write constant values in those OCR0X.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define PWM_PIN PB0
#define KEY_UP_PIN PB1
#define KEY_DOWN_PIN PB2
#define PWM_DUTY_MIN (0)
#define PWM_DUTY_MAX (100)
#define PWM_DUTY_STEP (1)
#define KEY_UP (1 << 1)
#define KEY_DOWN (1 << 2)
static volatile uint8_t duty = 0;
static uint8_t counter = 0;
static void process(void);
static uint8_t read_keys(void);
ISR(TIM0_COMPA_vect)
{
if (duty > PWM_DUTY_MIN && duty < PWM_DUTY_MAX) {
if (counter == 0) {
PORTB |= _BV(PWM_PIN);
} else if (counter == duty) {
PORTB &= ~_BV(PWM_PIN);
}
if (++counter == PWM_DUTY_MAX) {
counter = 0;
}
}
}
int
main(void)
{
/* setup */
DDRB |= _BV(PWM_PIN); // set PWM pin as OUTPUT
PORTB |= _BV(KEY_UP_PIN)|_BV(KEY_DOWN_PIN);
TCCR0A |= _BV(WGM01); // set timer counter mode to CTC
TCCR0B |= _BV(CS00); // set prescaler
OCR0A = 95; // set Timer's counter max value (96 - 1)
TIMSK0 |= _BV(OCIE0A); // enable Timer CTC interrupt
sei(); // enable global interrupts
_delay_ms(100); // time of debounce
/* loop */
while (1) {
process();
_delay_ms(8);
}
}
void
process(void)
{
uint8_t keys;
if (!(keys = read_keys())) {
return;
}
if ((keys & KEY_UP) && duty < PWM_DUTY_MAX) {
duty += PWM_DUTY_STEP;
}
if ((keys & KEY_DOWN) && duty > PWM_DUTY_MIN) {
duty -= PWM_DUTY_STEP;
}
if (duty == PWM_DUTY_MIN) {
PORTB &= ~_BV(PWM_PIN);
} else if (duty == PWM_DUTY_MAX) {
PORTB |= _BV(PWM_PIN);
}
}
static uint8_t
read_keys(void)
{
uint8_t result = 0;
if ((PINB & _BV(KEY_UP_PIN)) == 0) {
result |= KEY_UP;
}
if ((PINB & _BV(KEY_DOWN_PIN)) == 0) {
result |= KEY_DOWN;
}
return result;
}
Related
I'm trying to create a 10 second delay using TIMER1(16 bit) in atmega328p, I don't know if the delay has been created or not because it takes longer duration than 10 seconds and expected output( which is to create pwm waves) is not obtained. Here I have created a 1second delay and looped it 10 times, TIMER0 is used for creating pwm waves.
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "interrupt.h"
#define SET_BIT(PORT,BIT) PORT |= (1<<BIT)
#define CLR_BIT(PORT,BIT) PORT &= ~(1<<BIT)
struct {
volatile unsigned int BIT: 1;
}
FLAG_TIMER;
void timer_configuration() //16 bit timer
{
TCCR1A = 0x00; // Normal mode of operation
TCNT1 = 0xC2F8;
TCCR1B |= ((1 << CS10) | (1 << CS12));
TCCR1B &= ~(1 << CS11); //101
sei(); // Global interrupt
}
void timer_on()
{
TIMSK1 |= (1 << TOIE1);
}
void pwm_configuration() //TIMER0 - 8 bit
{
TCCR0A |= ((1<<WGM00) | (1<<WGM01)); //setting it to fast PWM mode
TCCR0A |= (1<<COM0A1);
TCCR0A &= ~(1<<COM0A0);
TCNT0 = 0x00;
TCCR0B |= ((1<<CS00) | (1<<CS02)); //Prescaler setting 1024
TCCR0B &= ~(1<<CS01);
sei();
}
ISR(TIMER1_OVF_vect)
{
static unsigned int counter;
counter++;
if(counter >= 10)
{
FLAG_TIMER.BIT=1;
counter = 0;
TCNT1 = 0xC2F8;
TIMSK &= ~(1<< TOIE1);
}
else
{
FLAG_TIMER.BIT=0;
}
}
int main(void)
{
SET_BIT(DDRD,PD6); //CRO
timer_configuration();
pwm_configuration();
while(1)
{
timer_on();
if(FLAG_TIMER.BIT == 1)
{
OCR0A = 128; //50% dutycycle
}
}
You set the counter to 49912 on initialisation and increment your count when it overflows, but it will then start from 0, so if 15624 counts = 1 second, then your counter will increment to 10 after 15624 + 9 x 216 counts or about 38.75 seconds.
Move the TCNT1 = 0xC2F8; line in the ISR:
ISR(TIMER1_OVF_vect)
{
static unsigned int counter;
TCNT1 = 0xC2F8;
counter++;
if(counter >= 10)
{
FLAG_TIMER.BIT=1;
counter = 0;
TIMSK &= ~(1<< TOIE1);
}
else
{
FLAG_TIMER.BIT=0;
}
}
I am not familiar with ATmega, but I cannot believe that is an appropriate way to use the timer. Normally you'd up-count to a compare value with auto reset to zero, or down-count from an auto reload value to zero and let the hardware reload the counter.
I am making a game where you need to repeat the sequence of LEDs that light up. This sequence is set by two LEDs. To repeat the sequence, I use the joystick.
I had an idea to make two bool arrays where True will indicate the left LED, and False will indicate the right LED. The first array must contain a random sequence(True/False) that needs to be repeated. When I push to one or the other side of the joystick, I want to write to the second array, respectively, True / False and all this time compare them.
This is what I have at the moment. (AT90USB647)
#define F_CPU 2000000UL
#include <avr/io.h>
#include <stdbool.h>
int main(void) {
MCUCR |= 0x80;
MCUCR |= 0x80;
DDRA = 0xFF;
PORTF = 0x20;
bool seq2[100];
while(1)
{
uint8_t x = PINF;
if(!(x & 0x20)) {
PORTA = 0x80;
}
else if(!(x & 0x08)) {
PORTA = 0x01;
}
else {
PORTA = 0x00;
}
}
}
The main question is how do I write True or False to an array when I push the joystick?
A basic approach could be:
#define F_CPU 2000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
volatile unsigned int counter;
static unsigned char pattern_position;
static unsigned char array_position;
static unsigned char pattern[] = {
0b01010101,
0b11010101,
0b10010101
};
ISR(TIMER0_COMPA_vect)
{
counter++;
}
int main(void)
{
MCUCR |= 0x80;
DDRA = 0xFF;
PORTF = 0x20;
// Timer initialization
// Mode: CTC
// Prescaler: 1024
TCCR0A = (1<<WGM01);
TCCR0B = (1<<CS02) | (1<<CS00);
// Calculate a correct time
//
// we want 1 ms -> f_TIMER0 = 1/T = 1/(1 * 10^-3)s = 1 kHz
//
// f_CPU f_CPU 20 MHz
// f_TIMER0 = ------------- -> OCR0A = ---------------- = ------------- = ~ 20 (It is not exactly 1ms but it is ok)
// k_H * OCR0A k_H * f_TIMER0 256 * 1 kHz
//
OCR0A = 20;
TIMSK0 = (1<<OCF0A); // Enable Timer0 Overflow Compare Match interrupt
// Show the sequence that the user should input
for (unsigned char i=0; i < sizeof(pattern)/sizeof(&pattern[0]); i++)
{
for (unsigned char j=0; j < 8; j +=2)
{
// There is possible a signal missing to show the user that the next pattern occurs!
PORTA = ((pattern[i]>>(j+1))<<PINA7) | ((pattern[i]>>j)<<PINA0);
// That the user can see the patterns a delay is necessary!
_delay_ms(1000);
}
}
// Signalize that the game starts
for (unsigned char i=0; i <8; i++)
{
PORTA ^= 0x81;
_delay_ms(1000);
}
TCNT0 = 0x00;
sei();
while(1)
{
// There is possible a signal missing to trigger next pattern input to the user!!!
if(!(PINF & (1<<PINF5)))
{
PORTA |= 0x80;
}
if(!(PINF & (1<<PINF3)))
{
PORTA |= 0x01;
}
// Time is 4 seconds to match the correct pattern
if(counter >= 4000)
{
if(!((pattern[pattern_position] & (1<<array_position)) == (0x01 & PORTA)))
{
// Wrong input end of game
}
array_position++;
if(!((pattern[pattern_position] & (1<<array_position)) == (0x01 & (PORTA>>8))))
{
// Wrong input end of game
}
array_position++;
if(array_position >= 8)
{
array_position = 0;
pattern_position++;
}
if(pattern_position >= (sizeof(pattern)/sizeof(&pattern[0])))
{
// End of game reached winning!
}
counter = 0x00;
PORTA = 0x00;
TCNT0 = 0x00;
}
}
}
I´m not sure if you are exactly trying this to do and i also can not test the code on your target platform but maybe this is a rudimental approach to solve your problem...
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 currently trying to create a simple program for an ATMega48 emulator that has a PCINT2 external interruptor listening on PORTD input and changing the output based on its value.
Here's the code of the interruptor:
unsigned int d; // a variable that has to change based on the input
ISR(PCINT2_vect) {
if (PORTD % 2 == 0) {
if (d <= 8000) {
d += 500;
}
} else {
if (d >= 1000) {
d -= 500;
}
}
}
the main() function:
int main(void)
{
DDRD = 0x00; // set PORTC for input
DDRB = 0xFF; // set PORTB for output
PORTB = 0x00; // Initial value is 0
PCMSK0 = 0b00000100;
d = 4000;
sei();
while (1) {
// Enable\Disable the LED with an initial delay
if (PIND == 0b00000000) {
PORTB = 0b00100000;
} else {
PORTB = 0b00000000;
}
// Delay for N seconds (determined by interrupt)
delay_stuff(d);
}
return 1;
}
Currently it's not calling the interruptor no matter what happens to any port, my assumption is that I'm not registering some magical ATMega listeners to call the interruptor.
How to register the interruptor then, what am I missing here?
According to datasheet page 75-76, you must enable Pin Change Interrupt in PCICR register and select which pin will be enabled on the corresponding IO in PCMSK2 for PCINT2 (PCMSK0 is for PCINT0, i.e PINB).
int main(void)
{
DDRD = 0x00; // set PORTC for input
DDRB = 0xFF; // set PORTB for output
PORTB = 0x00; // Initial value is 0
PCICR |= _BV(PCIE2); //or, PCICR |= 0x04;
PCMSK2 |= 0xFF; //any changes in PIND will trigger interrupt
d = 4000;
sei();
//other codes...
}
I am working on a personal project, hacking a multimeter and adding backlight to it. I am using an Attiny13.
I have the following code:
/* IR_Switch.c
*
* Created: 30/11/2014 23:52:15
* Author: keenox
*/
#define F_CPU 128000UL // 128kHz osc, no prescaling
#define SEC(VAL) ((unsigned int)(VAL) * F_CPU / 256)
#define INV_SEC(VAL) (F_CPU / 256 / (unsigned int)(VAL))
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/sleep.h>
#define FORCE_INLINE //__attribute__((always_inline))
#define PWM_ON() do { TCCR0A |= _BV(COM0A1); } while (0)
#define PWM_OFF() do { TCCR0A &= ~_BV(COM0A1); } while (0)
#define COUNTER_ON() do { counter = 0; TIMSK0 = _BV(TOIE0); } while (0)
#define COUNTER_OFF() do { TIMSK0 = 0; } while (0)
#define LED_ON() ( (TCCR0A & _BV(COM0A1)) || (PORTB & _BV(PINB0)) )
#define BUTTON_DOWN() ((~PINB) & _BV(PINB3))
#define BUTTON_UP() (PINB & _BV(PINB3))
#define TIMEOUT 15
char step = 50;
unsigned long counter = 0;
void ledFull(unsigned char _val)
{
PWM_OFF();
if (_val)
PORTB |= _BV(PINB0);
else
PORTB &= ~_BV(PINB0);
}
void setLed()
{
if (OCR0A > 249)
ledFull(1);
else if (OCR0A < 6)
ledFull(0);
else
PWM_ON();
}
ISR(TIM0_OVF_vect)
{
counter++;
if (BUTTON_UP())
{
if (counter >= INV_SEC(4))
{
PORTB |= _BV(PINB4);
if (!LED_ON())
{
COUNTER_OFF();
}
else if (counter >= SEC(TIMEOUT))
{
ledFull(0);
COUNTER_OFF();
}
}
}
else if (counter > SEC(3))
{
// Change intensity every one sec while button down
counter -= SEC(1);
if (OCR0A > 249 || OCR0A < 6)
step = -step;
OCR0A += step;
setLed();
}
}
ISR(PCINT0_vect)
{
cli();
PCMSK = 0x0;
if (BUTTON_DOWN())
{
MCUCR |= _BV(ISC00); // Switch to rising edge
COUNTER_ON();
}
else
{
MCUCR &= ~_BV(ISC00); // Switch to falling edge
if (counter <= INV_SEC(2)) // Normal push
{
PORTB &= ~_BV(PINB4);
}
else if (counter <= SEC(2))
{
if (LED_ON())
{
ledFull(0);
COUNTER_OFF();
}
else
{
setLed();
}
}
}
PCMSK = _BV(PCINT3);
sei();
}
int main(void)
{
DDRB = _BV(PINB4) | _BV(PINB0); // All inputs, but PB4 output
PORTB = 0xFF & ~_BV(PINB0); // All 1, except PINB0
MCUCR |= _BV(ISC01); // Falling edge interrupt
GIMSK = _BV(PCIE); // Activate only pin change interrupt
PCMSK = _BV(PCINT3); // PB3 interrupt mask
TCCR0A = _BV(WGM01) | _BV(WGM00); // Set OC0A at TOP, Fast PWM
TCCR0B = _BV(CS00); // Timer on, No prescaling
OCR0A = 255; // Max bright
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
COUNTER_OFF();
while (1)
{
sleep_enable();
#if defined(sleep_bod_disable)
sleep_bod_disable();
#endif
sei();
sleep_cpu();
sleep_disable();
}
}
The problem is it wakes up only on first interrupt (button push), executes it and then nothing.
If I don't use sleep (leave only while(1);) the program runs as expected.
Do you know what could be the problem?
LE: Added full code.
If I have:
sei();
while (1) {}
Then everything works OK. I just want to use sleep to reduce consumption.
Your sleep mode is "Power-down Mode"
as described in 7.1.3 of the reference manual
"Only an External Reset, a Watchdog Reset, a Brown-out
Reset, an external level interrupt on INT0, or a pin change interrupt can wake up the MCU. This sleep mode halts all generated clocks"
So the push button interrupt is handled, however the timer interrupt you enable at push button never fires because returning to sleep mode disables the timer.
You want the "Idle" sleep mode.