uwezi icon

20211114_pwm_demo

uwezi | PRO | 05/31/22 11:15:22 PM UTC (Edited) | 0 ⭐ | 815 👁️ | Never ⏰ | []
C |

3.89 KB

|

None

|

0 👍

/

0 👎

/*
 * 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