I have a small task to program the following using C, for an ATmega48 controller:
In the beginning, all (6) LEDs are turned on.
When button SW1 is pressed, LEDs are only allowed to be turned off with the respective buttons.
When button SW2 is pressed, LEDs are only allowed to be turned on with the respective buttons.
Here is what I came up with:
#define F_CPU 1000000UL
#include <asf.h>
#include <util/delay.h>
volatile unsigned char r,d;
ISR(INT0_vect)
{
if (d = 0)
{
PORTB = PORTB | 1<<1;
d = 1;
}
else
{
PORTB = PORTB | 0<<2;
d = 0;
}
}
int main (void)
{
d = 0;
DDRC = 0x00;
PORTC = 0xFF;
DDRB= 0xFF;
PORTB = 0x00;
PCICR = 0b00000001;
sei();
while(1)
{
PCMSK0 = 0b00000100;
while (d == 0)
{
for (int i=0; i<6; i++)
{
if(!(PINC & (1<<i)))
PORTB = PORTB | i<<1;
}
}
PCMSK0 = 0b00000010;
while (d == 1)
{
for (int i=0; i<6; i++)
{
if(!(PINC & (1<<i)))
PORTB = PORTB | i<<0;
r = r<<1;
}
}
}
}
When I tried to simulate in Atmel AVR Studio, wrong LEDs were turning on when I pressed the respective buttons (e.g. LED2 for SW4), and interrupts never happened.
Please explain what I've done wrong, as I have already scoured several resources, and each one gives a different approach, which I fail to comprehend.
Though since you have not initialied interrupts in a proper way, These lines of code here which are not being executed at all, have two issues.
if (d = 0)
{
PORTB = PORTB | 1<<1;
d = 1;
}
else
{
PORTB = PORTB | 0<<2;
d = 0;
}
One issue that is clearly visible is: if (d = 0) does not check if d is equal to 0, instead it assigns 0 to d every single time.
Are you trying to set and clear the pins?
If yes, this is not the way it is supposed to be done.
Usually in Embedded we set the pins this way: PORTB |= (1<<1); //you have got this right
and clear the pins by PORTB &= ~(1<<1);
You stated that interrupts never happened. It is then a valid conclusion that d remains 0 all the time and you loop in while (d == 0) {} all the time. So r is never modified unless from elsewhere/apart from the code you have shown. Does this r have any implementation related to GPIO for LEDs? If Yes, then you have to first fix the issues in interrupts.
The reason behind interrupts never happened is that you have not initialized the External interrupts at all.
You need to configure EICRA – External interrupt control register A register.
Then you need to configure the EIMSK – External interrupt mask register
Then you need to configure the EIFR – External interrupt flag register
Reading the datasheet always helps to understand the implementation details.
Related
The code compiles correctly but I am unable to obtain 1kHz Square Wave. I attached an LED at PINB1 to check.
I used Timer1, with CTC mode and Prescalar as 64.
PLease Help.
#define F_CPU 8000000L
#include <avr/io.h>
#include "avr/iom32.h"
// - - - - PROGRAM TO GENERATE A SQUARE WAVE OF 1KHz - - - - //
void _delay_();
int main(void)
{
DDRB = 0xFF;
OCR1AH = 0xF4;
OCR1AL = 0x23;
TCNT1H = 0;
TCNT1L = 0;
while (1)
{
PORTB |= (1 << 4);
_delay_();
PORTB &= ~(1 << 4);
_delay_();
}
}
void _delay_() {
TCCR1A = 0x00;
TCCR1B = 0x0B;
while(!(TIFR & (1 << 4)));
TCCR1B = 0x00;
TCCR1A = 0x00;
TIFR |= 0x10;
}
As oldtimer suggested, you should use a o-scope to verify the output. If you don't have one, or if you do and still no output, then try replacing the delay routine with a simple software delay such as this:
void _delay_() {
// simple software delay
for (uint32_t i = 0; i < 50000; i++);
}
The idea is to adjust the maximum count (50000) to any value that creates a long enough delay to see the LED blink. If the LED still doesn't blink, then the problem is with the other code, or the external connection to the LED. For example, you say PINB1, but isn't that at bit position 1 << 1, but your code uses 1 << 4.
I have tried to implement TX only uart for ATTiny85 and receive the bits using arduino micro. (similiar question did not help since it is quite different to my situation)
My intend is to be able to print via attiny85 -> arduino -> console so I can debug the attiny85, since I don't have oscilloscope available.
attiny85 fuses are: "efuse:w:0xff:m -U hfuse:w:0xdf:m -U lfuse:w:0xf1:m" aka. 16MHz F_CPU
arduino also seems to have 16MHz F_CPU
Similiar to the mentioned question attiny85 sends bits via timer0 ISR one bit at time:
#ifndef F_CPU
#define F_CPU 16000000UL // 16 MHz
#endif
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define TX_PIN PB0
volatile uint16_t tx_shift_reg = 0;
ISR(TIMER0_COMPA_vect) {
uint16_t local_tx_shift_reg = tx_shift_reg;
if( local_tx_shift_reg & 0x01 ) {
PORTB |= _BV(TX_PIN);
} else {
PORTB &= ~_BV(TX_PIN);
}
local_tx_shift_reg >>= 1;
if(!local_tx_shift_reg) {
// Stop timer0.
GTCCR |= (1<<TSM) | (1<<PSR0);
}
tx_shift_reg = local_tx_shift_reg;
}
void UART_tx(char byte) {
uint16_t local_tx_shift_reg = tx_shift_reg;
local_tx_shift_reg = (0b1<<9) | ((uint16_t)byte<<1);
tx_shift_reg = local_tx_shift_reg;
TCNT0 = 0;
TCCR0B |= (1<<CS02)|(1<<CS00); // 1024 prescaler
GTCCR &= ~(1<<TSM);
}
void UART_tx_char(char c) {
UART_tx( c );
// wait until transmission is finished
while(tx_shift_reg);
}
void UART_init() {
cli()
// set TX pins as output
DDRB |= (1<<TX_PIN);
PORTB |= (1<<TX_PIN);
// set timer0 to CTC mode, keep it halted.
TCCR0A |= (1<<WGM01);
TCCR0B = 0;
// enable interrupt
TIMSK |= (1<<OCIE0A);
OCR0A = 128;
OCR0B = 128;
TCNT0 = 0;
GTCCR |= (1<<TSM);
sei();
}
void main(void)
{
UART_init();
while(1) {
for(char c = 1; c < 128; ++c) {
UART_tx_char(c);
_delay_ms(100);
}
}
}
Then arduino receives the bits:
/*
* ATtiny85 bit-bang uart monitor for ATmega32u4
*/
#define LED_PIN 13
#define RX_PIN 7
// rx_state == 0 // timer1 not running
// rx_state == 1 // receive in progress
// rx_state == 2 // data ready in rx_data_reg
volatile int rx_state = 0;
volatile int rx_bit_nro = 0;
volatile uint16_t rx_shift_reg = 0;
volatile uint16_t rx_data_reg = 0;
void rx_start_interupt() {
if(rx_state == 0) {
digitalWrite(LED_PIN, HIGH);
while(digitalRead(RX_PIN));
// Start timer1
rx_state++;
TCNT1 = 0;
TCCR1B = (1 << WGM12) | (1 <<CS12) | (1 << CS10);
}
}
ISR(TIMER1_COMPA_vect) {
uint16_t bit = digitalRead(RX_PIN) > 0;
rx_shift_reg >>= 1;
rx_shift_reg |= (bit << 7);
++rx_bit_nro;
if(rx_bit_nro == 9) {
// Stop timer.
TCCR1B = 0;
TCNT1 = 0;
rx_data_reg = rx_shift_reg;
rx_shift_reg = 0;
rx_bit_nro = 0;
rx_state++;
digitalWrite(LED_PIN, LOW);
}
}
void setup() {
noInterrupts();
// Program timer1 for UART bit bang receive.
TCCR1A = 0; // set entire TCCR1A register to 0 (stops it)
TCCR1B = 0; // same for TCCR1B
TCNT1 = 0; // initialize counter value to 0
OCR1A = 128;
TIMSK1 |= (1 << OCIE1A);
interrupts();
pinMode(LED_PIN, OUTPUT);
pinMode(RX_PIN, INPUT);
// Attach RX start interupt.
pinMode(digitalPinToInterrupt(RX_PIN), INPUT);
attachInterrupt(digitalPinToInterrupt(RX_PIN), rx_start_interupt, FALLING);
Serial.begin(9600);
while(!Serial);
Serial.print("F_CPU:");
Serial.println(F_CPU,DEC);
Serial.println("Waiting for data from attiny85...");
}
void loop() {
if(rx_state == 2) {
uint16_t local_rx_data = rx_data_reg;
rx_state = 0;
Serial.println(local_rx_data,HEX);
}
}
I have tried pretty much everything to make it work, but received bytes come back garbace.
What am I missing?
Note: I'm using timer0 on attiny85 and timer1 on arduino.
Solved: switching ISR to TIMER1_COMPAB_vect and OCR1B = 64 actually works! Yay!
I recently ran into this issue of serial comms coming out as garbage on the other end. In my case, the ATtiny85 was sending bits to my laptop using an USB to TTL UART converter which had worked beautifully in other situations, but, I was just getting garbage in the Arduino IDE serial monitor.
I found a forum post which mentioned the possibility of calibrating OSCCAL.
I get a little bit fancier in my related blog post, but I tested the theory that I should calibrate OSCCAL using this code:
#include <SoftwareSerial.h>
SoftwareSerial comm(-1, 0);
static const int anchor = 128;
void
print_osccal(int v) {
comm.println(F("********************************"));
comm.print(F("OSCCAL = "));
comm.println(v);
comm.println(F("********************************"));
}
void
setup() {
delay(5000);
comm.begin(300);
OSCCAL = anchor;
print_osccal(anchor);
delay(5000);
}
void
loop() {
int x;
for (int i = 1; i < 128; ++i) {
x = anchor + i;
OSCCAL = x;
print_osccal(x);
delay(1000);
x = anchor - i;
OSCCAL = x;
print_osccal(x);
delay(1000);
}
}
It was a revelation seeing garbage all of a sudden transform to nicely formatted messages in the serial monitor (and, of course, back to garbage as the search is an infinite loop).
Now, the ATtiny85's internal oscillator can only support 1 MHz and 8 MHz. As I understand it, OSCCAL exists because this internal oscillator is 1) not very accurate and 2) is very sensitive the temperature.
If the ATtiny85 is set to run at 16MHz, it needs a reliable, external oscillator, and no amount of fiddling with OSCCAL might help. But, it did in my case allow me to discover the value(s) which made SoftwareSerial tick at 8 MHz and a range of baud rates from 300 to 38400.
This allowed me to get the right value for the bit banging UART to work with the 1MHz clock which is not supported by SoftwareSerial.
The aim is to store the newest 10 ADC readings in an array and then calculate the average of them to be used elsewhere. Removing the oldest each time it is updated.
Regarding the LED timing, it must switch timing from 1s to 0.25s if ADC reading is within boundaries as written below, how can this be implemented correctly? I know my method works but can be done better.
As for the LED's they must change patterns if a switch is pressed as you can see, which they do, but yet again I'm sure it can be done another simpler way!
Below is my code, Also I'm sure there are many an error and plenty of room for optimization, I will gladly accept it all!
#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
unsigned int timecount0;
unsigned int adc_reading;
volatile uint32_t timing = 1;
volatile uint32_t accumulator = 0;
volatile uint16_t average = 0;
volatile uint16_t samples = 0;
#define LED_RED PORTB = ((PORTB & ~0b00001110)|(0b00000010 & 0b00001110))
#define LED_GREEN PORTB = ((PORTB & ~0b00001110)|(0b00001000 & 0b00001110))
#define LED_BLUE PORTB = ((PORTB & ~0b00001110)|(0b00000100 & 0b00001110))
#define LED_RGB PORTB = ((PORTB & ~0b00001110)|(0b00001000 & 0b00001110))
#define DELAY_COUNT 6
volatile uint8_t portdhistory = 0xFF;
void Timer0_init(void)
{
timecount0 = 0; // Initialize the overflow count. Note its scope
TCCR0B = (5<<CS00); // Set T0 Source = Clock (16MHz)/1024 and put Timer in Normal mode
TCCR0A = 0; // Not strictly necessary as these are the reset states but it's good
// practice to show what you're doing
TCNT0 = 61; // Recall: 256-61 = 195 & 195*64us = 12.48ms, approx 12.5ms
TIMSK0 = (1<<TOIE0); // Enable Timer 0 interrupt
PCICR |= (1<<PCIE0);
PCMSK0 |= (1<<PCINT0);
sei(); // Global interrupt enable (I=1)
}
void ADC_init(void)
{
ADMUX = ((1<<REFS0) | (0<<ADLAR) | (0<<MUX0)); /* AVCC selected for VREF,ADLAR set to 0, ADC0 as ADC input (A0) */
ADCSRA = ((1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADIE)|(7<<ADPS0));
/* Enable ADC, Start Conversion, Auto Trigger enabled,
Interrupt enabled, Prescale = 32 */
ADCSRB = (0<<ADTS0); /* Select AutoTrigger Source to Free Running Mode
Strictly speaking - this is already 0, so we could omit the write to
ADCSRB, but included here so the intent is clear */
sei(); //global interrupt enable
}
int main(void)
{
ADC_init();
Timer0_init();
DDRD = 0b00100000; /* set PORTD bit 5 to output */
DDRB = 0b00111110; /* set PORTB bit 1,2,3,4,5 to output */
sei(); // Global interrupt enable (I=1)
while(1)
{
if(!(PIND & (1<<PIND2)))
{
PORTD = PORTD |= (1<<PORTD5);
PORTB = PORTB |= (1<<PORTB4);
if(average>512)
{
PORTB = PORTB |= (1<<PORTB5);
}
}
else
{
PORTD = PORTD &= ~(1<<PORTD5);
PORTB = PORTB &= ~(1<<PORTB4);
}
}
}
ISR(TIMER0_OVF_vect)
{
TCNT0 = 61; //TCNT0 needs to be set to the start point each time
++timecount0; // count the number of times the interrupt has been reached
if(!(PIND & (1<<PIND3)))
{
if (timecount0 >= 0) // 40 * 12.5ms = 500ms
{
PORTB = ((PORTB & ~0b00001110)|(0b00000000 & 0b00001110));
}
if (timecount0 >= 8*timing)
{
LED_RED;
}
if (timecount0 >= 16*timing)
{
LED_GREEN;
}
if (timecount0 >= 24*timing)
{
PORTB = ((PORTB & ~0b00001110)|(0b00000110 & 0b00001110));
}
if (timecount0 >= 32*timing)
{
PORTB = ((PORTB & ~0b00001110)|(0b00001000 & 0b00001110));
}
if (timecount0 >= 40*timing)
{
PORTB = ((PORTB & ~0b00001110)|(0b00001010 & 0b00001110));
}
if (timecount0 >= 48*timing)
{
PORTB = ((PORTB & ~0b00001110)|(0b00001100 & 0b00001110));
}
if (timecount0 >= 56*timing)
{
PORTB = ((PORTB & ~0b00001110)|(0b00001110 & 0b00001110));
}
if (timecount0 >= 64*timing)
{
timecount0 = 0;
}
}
else
{
if (timecount0 >= 0)
{
PORTB = ((PORTB & ~0b00001110)|(0b00000000 & 0b00001110)); //ALL OFF
}
if (timecount0 >= 8*timing)
{
LED_RED;
//PORTB = ((PORTB & ~0b00001110)|(0b00000010 & 0b00001110)); //RED
}
if (timecount0 >= 16*timing)
{
LED_GREEN;
}
if (timecount0 >= 24*timing)
{
LED_BLUE;
}
if (timecount0 >= 32*timing)
{
timecount0 = 0;
}
}
}
ISR (ADC_vect) //handles ADC interrupts
{
adc_reading = ADC; //ADC is in Free Running Mode
accumulator+= adc_reading;
if ((adc_reading > 768) & (adc_reading <= 1024))
{
timing = 10;
}
if ((adc_reading >= 0) & (adc_reading<= 768) )
{
timing = 2.5;
}
samples++;
if(samples == 10)
{
average = accumulator/10;
accumulator = 0;
samples = 0;
}
}
Depending on your processors, you may way to keep ISR() speedy and avoid expensive /,%.
The LED stuff, I'd handle in a timer interrupt.
#define N 10
volatile unsigned sample[N];
volatile unsigned count = 0;
volatile unsigned index = 0;
volatile unsigned sum = 0;
ISR (ADC_vect) {
if (count >= N) {
sum -= sample[index];
} else {
count++;
}
sample[index] = ADC;
sum += sample[index];
index++;
if (index >= N) {
index = 0;
}
}
unsigned ADC_GetAvg(void) {
block_interrupts();
unsigned s = sum;
unsigned n = count;
restore_interrupts();
if (n == 0) {
return 0; //ADC ISR never called
}
return (s + n/2)/n; // return rounded average
}
I'd recommend an integer version of a low pass filter than the average of the last N.
In terms of the moving averaging w/ N = 10, chux - Reinstate Monica has provided the solution. Chux - Reinstate Monica also recommends looking at an integer version of a low pass filter. I personally like the Exponentially Weighted Moving Average (EWMA) because it's fairly simple to code and only requires a few values to do the averaging. This is compared to having to hold 10 in array in your case. I would recommend Elliot Williams's Make: AVR Programming Chapter 12 for this. In case you don't have access to this readily, the EWMA, as explained in Make AVR, starts with
y_current = (1/16)*x_current + (15/16)*y_previous
where in our case, y_current is the updated EWMA value, x_current is the newest sample from your ADC, and y_previous is the last EWMA value. The choice of 16 can also be changed along with the weights, 1 and 15. Keeping it a power of 2 is important though, as you will see. As shown in Elliot Williams book, you multiply by 16 and compensate for rounding problems and get the following,
16*y_current = x_current + 16*y_previous - (16*y_previous - 8)/16.
Now, I know this looks ugly but what we have is scaled by 16 average value that's an integer and only relies on integer addition (the 16*y_previous is stored as one value so you don't do the multiplication) and a bit shift; that's the reason why a power of 2 was chosen in the EWMA, dividing by 16 is the same as a right bit shift of 4. Ok, so what does this average look like in code:
// Snippet from Make: AVR Programming
uint16_t x_current; // ADC value.
uint16_t y_current; // Average ADC value.
// Get the EWMA.
y_current = x_current + y_current - ((y_current - 8) >> 4);
// Send the value over USART (assuming it's wired up). Remember that
// y_current is scaled by 16.
printf("%d\n",(y_current>>4));
The above is just the EWMA that you can use in your code and an example of sending it, which is just a reminder that the value if scaled. Remember, this is just the averaged ADC value. Likely you will be wanting to use the ADC value as an input to a function to get the value of some measured quantity. Rather than actually using a function and calculating values, you can create a look-up table where the index is the ADC value and the array entry at that index is the precalculated value.
In terms of your other code, the things that could be corrected/streamlined are in your ISRs. In ISR(TIMER0_OVF_vect) you have some bit operations that are constant and can be precalculated so that your not doing it everytime the ISR(TIMER0_OVF_vect) fires.
PORTB = ((PORTB & ~0b00001110)|(0b00000000 & 0b00001110));
becomes
PORTB = ((PORTB & 0b11110001)|(0b00000000)); // Saves a bit inversion and '&'
which shows that your ORing, |, doesn't affect the result, because you're ORing against all zeros.
Finally, in your ISR (ADC_vect) you are using the bitwise, &, and not the logical and, &&. You get the same result but it's like using a wrench to hammer in a nail. I know this is a lot but I hope it helps and let me know if you need clarification.
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 trying to turn on the LEDs when the user button is pressed
I think I have enabled the peripheral clocks right and the right register. The button is on porta bit 0
Here is my code...any help would be great. Sorry if it is a bit simple, I am still learning the board.
int main (void)
{
RCC->AHB1ENR=0x9;
GPIOA->MODER = 0x00000002;
GPIOD->MODER = 0x55000000;
GPIOD->OTYPER = 0;
GPIOD->OSPEEDR = 0;
GPIOD->PUPDR = 0;
GPIOA->PUPDR = 0;
GPIOA->OTYPER = 0;
GPIOA->OSPEEDR = 0;
while(1)
{
if(GPIOA->IDR == 0x0001){
GPIOD->ODR = 0xF000;
}
else{
GPIOD->ODR = 0;
}
}
}
I don't know the STM32f4 but I'm guessing that instead of
if(GPIOA->IDR == 0x0001)
You want
if ((GPIOA->IDR & 0x0001) != 0)
The original checks that the low bit is on AND all other bits are off while the new version just checks the low bit and ignores the rest.