Attiny85在定时器中断中使用全局变量

时间:2017-07-05 06:47:17

标签: timer arduino global-variables interrupt attiny

我对Arduino编程很陌生。然而,我已经编写了一段时间。我目前正在尝试使用Arduino IDE和Digispark Attiny85开发板编写复音钢琴。要一次播放多个音符我正在使用正弦表和快速PWM。这是我的代码:

int val = 1;
uint8_t C = 0;
uint8_t D = 0;
uint8_t E = 0;
uint8_t F = 0;
uint8_t G = 0;
uint8_t A = 0;
uint8_t B = 0;

static uint8_t  sin_C[123] = {16,16,17,18,19,20,20,21,22,23,23,24,25,25,26,27,27,28,28,29,29,30,30,30,31,31,31,31,31,31,31,31,31,31,31,31,31,31,30,30,30,29,29,28,28,27,27,26,25,25,24,23,23,22,21,20,20,19,18,17,16,16,15,14,13,12,11,11,10,9,8,8,7,6,6,5,4,4,3,3,2,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,2,2,3,3,4,4,5,6,6,7,8,8,9,10,11,11,12,13,14,15,15};
static uint8_t sin_D[110] = {16,16,17,18,19,20,21,22,23,23,24,25,26,26,27,28,28,29,29,30,30,30,31,31,31,31,31,31,31,31,31,31,31,31,30,30,30,29,29,28,27,27,26,25,25,24,23,22,21,20,20,19,18,17,16,15,14,13,12,11,11,10,9,8,7,6,6,5,4,4,3,2,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,2,2,3,3,4,5,5,6,7,8,8,9,10,11,12,13,14,15,15};
static uint8_t sin_E[98] = {16,17,18,19,20,21,22,23,23,24,25,26,27,27,28,29,29,30,30,31,31,31,31,31,31,31,31,31,31,31,30,30,30,29,28,28,27,26,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,5,4,3,3,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,2,2,3,4,4,5,6,7,8,8,9,10,11,12,13,14,15};
static uint8_t sin_F[92] = {16,17,18,19,20,21,22,23,24,25,26,27,27,28,29,29,30,30,31,31,31,31,31,31,31,31,31,31,30,30,30,29,28,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,3,2,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,2,2,3,4,4,5,6,7,8,9,10,11,12,13,14,15};
static uint8_t sin_G[82] = {16,17,18,19,20,22,23,24,25,26,27,28,28,29,30,30,31,31,31,31,31,31,31,31,31,30,30,29,29,28,27,26,25,24,23,22,21,20,19,17,16,15,14,12,11,10,9,8,7,6,5,4,3,2,2,1,1,0,0,0,0,0,0,0,0,0,1,1,2,3,3,4,5,6,7,8,9,11,12,13,14,15};
static uint8_t sin_A[73] = {16,17,18,20,21,22,24,25,26,27,28,29,29,30,31,31,31,31,32,31,31,31,31,30,29,29,28,27,26,25,24,22,21,20,18,17,16,14,13,11,10,9,7,6,5,4,3,2,2,1,0,0,0,0,0,0,0,0,0,1,2,2,3,4,5,6,7,9,10,11,13,14,15};
static uint8_t sin_B[65] = {16,17,19,20,22,23,24,26,27,28,29,30,30,31,31,31,32,31,31,31,30,30,29,28,27,26,24,23,22,20,19,17,16,14,12,11,9,8,7,5,4,3,2,1,1,0,0,0,0,0,0,0,1,1,2,3,4,5,7,8,9,11,12,14,15};

ISR(TIMER0_COMPA_vect) {
  C++;
  D++;
  E++;
  F++;
  G++;
  A++;
  B++;
  if(C>122) {
    C = 0;
  }
  if(D>109) {
    D = 0;
  }
  if(E>97) {
    E = 0;
  }
  if(F>91) {
    F = 0;
  }
  if(G>81) {
    G = 0;
  }
  if(A>72) {
    A = 0;
  }
  if(B>64) {
    B = 0;
  }
  int values[7] = {sin_C[C],sin_D[D],sin_E[E],sin_F[F],sin_G[G],sin_A[A],sin_B[B]};
  OCR0A = values[val];
}

void setup() {
  DDRB |= (1<<PB0);
  TCNT0 = 0;
  TCCR0A=0;
  TCCR0B=0;
  TCCR0A |=(1<<COM0A1);
  TCCR0A |=(1<<WGM01);
  TCCR0A |=(1<<WGM00);
  TCCR0B |= (1 << CS00);
  OCR0A=254;
  TIMSK |= (1<<OCIE0A);
}

void loop() {
}

目前我无法使用变量来控制我应该播放的音符,即即使它编译也不会有效:

OCR0A = values[val];

其中val是我设置的全局变量。

我想知道是否有某种方法可以做到这一点或达到相同的效果,就像我替换线

OCR0A = values[1] + values[3] + values[5];

对于前面提到的那个,Attiny85输出一个像它应该的D和弦。但是我希望笔记由按钮控制,所以我需要能够在程序运行时更改它们,并且全局变量是我能想到的唯一方法。任何解决方案都将非常感激。

1 个答案:

答案 0 :(得分:0)

使用global vars是在主程序循环和中断处理程序之间传递数据的唯一方法。

在撰写此回复时,我发现您应该考虑在正弦表中使用签名值。需要签名添加才能使数学正常工作。

这一行:

OCR0A = values[val];

只能产生单声道声音。

你正走在正确的道路上:

int values[7] = {sin_C[C],sin_D[D],sin_E[E],sin_F[F],sin_G[G],sin_A[A],sin_B[B]};

虽然。

所以,是的,另一个全局变量应该可以解决这个问题:

unsigned char notes_played;  // bits 0-7 map C-D notes

在您的ISR中:

ISR(TIMER0_COMPA_vect)
{
    //...

    int values[7] = {sin_C[C],sin_D[D],sin_E[E],sin_F[F],sin_G[G],sin_A[A],sin_B[B]};

    int ocr_value = 0;
    for (unsigned char mask = 1, i = 0; mask < 0x80; mask <<= 1, ++i)
    {
        if (mask & note_played)
           ocr_value += (signed char)(values[i] - 16);  // hints that using signed values 
                                                        // in sine tables may be more 
                                                        // efficient.
    }

    // need to 'clip' OCR here?
    if (ocr_value < -(OCR_RANGE / 2))
        ocr_value = -(OCR_RANGE / 2);
    if (ocr_value > (OCR_RANGE / 2))
        ocr_value = (OCR_RANGE / 2);

    OCRA0 = (unsigned char)((ocr_value + OCR_ZERO) & 0xFF);
}

我添加了OCR_RANGE和OCR_ZERO,它控制滤波输出的交流电压范围。范围:0-255。 (OCR_RANGE = 256可能会溢出)。 OCR_ZERO控制中间点(通常为127),您应该在setup()中将初始OCRA0设置为OCR_ZERO。

最后一点说明。在声明你的正弦表时:

static uint8_t sin_B[65] =       //...  This gets store in data space, which is limited
static const uint8_t sin_B[65] = //... This gets stored on flash, which has plenty of room.

使正弦表尽可能大,并尽可能快地中断可以真正改善音质。