在AVR Atmega324A上复用七段显示

时间:2014-05-21 13:21:43

标签: c embedded avr atmega

我整天都被困在这一天,我正在尝试使用两个七段显示来创建一个倒计时器。我希望它从20开始,倒数到零。而10<我只想打开左侧显示(即数十位没有0)。我正在使用Atmega 324A。我将所有端口C连接到显示段,并使用PIND0在两者之间切换。这是我到目前为止所拥有的。

#include <avr/io.h>
#include<util/delay.h>


int main(void) {

    int prescale = (8000000/8)/1000-1;
    int digit = 1;
    uint8_t display;
    int seven_seg = {0x3F,0X06,0X5B,0X4F,0X66,0X6D,0X7D,0C07,0X7F,0X6F};
    // Set OC1 to output
    DDRD = (1<<0);
    DDRC = 0xFF;

    OCR1A = prescale;
    //clear counter on compare match
    TCCR1A = (0<<COM1A1) | (1<<COM1A0);
    //Set Prescale and CTC Mode
    TCCR1B =  (0<<CS12) | (1<<CS11) | (0<<CS10) | (0<<WGM13) | (1<<WGM12);


    while(1) {

            display++;
            if(display>50) display = 0;
            for (i = 250; i>0; i--){        
            PORTD ^= 0<<PIND0;
            PORTC = seven_seg[display%10];                
            PORTD ^= 1<<PIND0;
            _delay_ms(100);
            for (i = 250; i>0; i--){        
                PORTD ^= 1<<PIND0;
                PORTC = seven_seg[display/10];                
                PORTD ^= 0<<PIND0;
                _delay_ms(100);
             }
            }
        while((TIFR1 & (1<<OCF1A)) == 0) {}

        TIFR1 &= (1 << OCF1A);  

    }
}

所有这一切都将两个显示都设置为0.我是否需要另一个for循环来遍历seven_seg []数组,而它正在执行此操作?真的不知道如何解决这个问题。任何帮助都会很棒。

1 个答案:

答案 0 :(得分:2)

你犯了两个大错:

  1. 你不使用计时器
  2. 你应该将显示驱动逻辑与价值生成逻辑
  3. 分开

    最好的事情是你分开任务并计划如何实现它。

    任务一:提供数据显示
    任务二:将数据传输到显示友好表示
    任务三:显示该数据的aktual

    任务一很简单。假设你想要显示整数,你有三个7-seg-disps。 所以任务一是提供一些数据来显示。

    int16_t numberToDisplay = 234;
    

    任务二也不是那么难。显示友好表示将是每个显示元素一个字节。

    #define NUM_7SEGS 3
    volatile uint8_t dispData[NUM_7SEGS]; // volatile since it is be accassed by different contexts
    

    现在我们需要一些将输入值传输到显示数据的机制

    void val2DispData(int16 val)
    {
       uint8_t i;
       for(i=NUM_7SEGS; i; --i){
          uint8_t r = (uint8_t)(val%10);
          val /= 10
          dispData[i-1] = seven_seg[r];
       }
    }
    

    很好,现在?

    任务三是最困难的。我们需要有人说输出要做什么。 由于想要多路复用3个显示元素,意味着:

    • 停用当前显示元素
    • 将下一个数字的数据放入出口
    • 激活下一个显示元素
    • 等一下。

    这四个步骤我们想要非常快地执行,以便观察者不会识别一次只有一个元素是活动的。 由于这完全独立于其他程序逻辑,我们需要在“后台”中执行此操作。

    您的主程序流只是调用该函数而后台计时器ISR担心显示。

    因此我们必须设置一个定时器并在其中断服务程序中调用元素数据的切换。 (用于设置计时器和计时器中断请参考另一个教程)

    // this have to be called cyclic from timer isr
    // frequency is not that important but should be at 
    // least NUM_7SEGS * 200 Hz to not look ugly
    void cyclicDisplayTask()
    {
      static uint8_t currentElement = 0;
    
      // disable all elements
      PORTD = 0;
    
      // put data on the port
      PORTC = dispData[currentElement]; // this is why the volatile is necessary. without the compiler would not notice that values may be changed by the main program flow
    
      // enable next element
      PORTD = (1<<currentElement);
    
      ++currentElement;
      if(currentElement>=NUM_7SEGS){
        currentElement = 0;
      }
    }
    

    当然,您必须使特定显示元素的启用适应您的硬件。

    另请注意,您可以使用晶体管来驱动元件。 AVR端口引脚强驱动单个段,但驱动该段的公共阳极/阴极的另一侧可能过载。这当然取决于细分市场中的LED。如果这是低电流LED(~2mA),那就没关系。