/*
* Created: 2021-11-14 16:22:34
* Author : uwezi
*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4-digit 7-segment LED display with common anodes
- digit cathodes connected directly to PB0, PB1, PB2, PB3
- segments A-PD0, B-PD1, C-PD2, D-PD3, E-PD4, F-PD5 and G-PD6
connected with one 220 ohm resistor per segment between port pin and LED
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
// 7-segment font for numbers 0-9
uint8_t font[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};
// use to buffer contents of display
volatile uint8_t framebuffer[4];
// which digit is active now
volatile uint8_t current_digit = 0;
// display the contents of framebuffer on the LED display by multiplexing
ISR(TIMER0_OVF_vect)
{
// the code for the interrupt service routine
PORTD = 0xff; // all segments off
current_digit += 1; // step one digit
current_digit %= 4; // and keep between 0-3
PORTB = (PORTB & 0b00001111) | (0b00010000 << current_digit);
PORTD = framebuffer[current_digit];
}
void init(void)
{
// we can collect all hardware initialization here
DDRB = 0b11110000; // lower B-pins as output
DDRD = 0b11111111; // all D-pins as output
framebuffer[0] = 0xff;
framebuffer[1] = 0xff;
framebuffer[2] = 0xff,
framebuffer[3] = 0xff;
// setting up TIMER0 (8 bit)
// - no pwm
// - prescaler 2:1, 500 kHz clock rate
// - normal counting mode 0...255
// - timer overflow interrupt enabled
TCCR0A = (0 << COM0A1) | (0 << COM0A0)
| (0 << COM0B1) | (0 << COM0B0)
| (0 << WGM01) | (0 << WGM00);
TCCR0B = (0 << WGM02)
| (0 << CS02) | (1 << CS01) | (0 << CS00); // clock prescaler
// enable the TIMER0 OVERFLOW INTERRUPT
TIMSK0 = (0 << OCIE0B) | (0 << OCIE0A) | (1 << TOIE0);
// setting up the ADC
// - left aligned for 8-bit results
// - channel ADC0
ADMUX = (0 << REFS1) | (1 << REFS0)
| (1 << ADLAR)
| (0 << MUX3) | (0 << MUX2) | (0 << MUX1) | (0 << MUX0);
ADCSRA = (1 << ADEN) | (1 << ADSC)
| (0 << ADATE) | (0 << ADIF) | (0 << ADIE)
| (0 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
// setting up TIMER1
// - OC1A and OC1B running in non-inverting pwm
// - clock prescale 1:1, 1us per tick
// - WGM13..10 determine mode
// 0 0 0 1 phase correct pwm, 8-bit
// 0 1 0 1 fast pwm, 8-bit
TCCR1A = (1 << COM1A1) | (0 << COM1A0)
| (1 << COM1B1) | (0 << COM1B0)
| (0 << WGM11) | (1 << WGM10);
TCCR1B = (0 << WGM13) | (0 << WGM12)
| (0 << CS12) | (0 << CS11) | (1 << CS10);
// don't forget to enable interrupts
sei();
}
// take the contents of value and put its numeric contents
// into the 4 digits of the framebuffer for the display
void update(uint16_t value)
{
uint16_t A = value;
uint8_t i;
for (i = 0; i < 4; i++)
{
// take the remainder of the division by 10
// look up the font for this number
// put it into the framebuffer
framebuffer[3-i] = ~font[A % 10];
A = A / 10;
}
}
int main(void)
{
uint16_t value = 9999;
// do the hardware initialization
init();
// set a constant width on OC1B of 40/256 or 79/511
OCR1B = 40;
while (1)
{
// start an ADC conversion
ADCSRA = ADCSRA | (1 << ADSC);
while (ADCSRA & (1 << ADSC))
{
// we are still waiting
}
// just use the 8 highest bits 0...255
value = ADCH;
// put the value into the framebuffer
update(value);
// and set the pulse width of OC1A to the same value
OCR1A = value;
// wait 10 ms between updates
_delay_ms(10);
}
}
Comments