/* * 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 #include #include // 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); } }