Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Кнопки, таймеры, АЦП, взрыв мозга
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > MSP430
Turbo_enot
Здравствуйте!
Есть у меня задачка, которую никак не могу решить. У меня есть 6 кнопок (картинка прилагается), подключены они по две штуки к одному пину. Необходимо сделать так, чтобы при нажатии на каждую кнопку, происходило какое-то своё действие: светодиод мигал с разной чистотой, или динамик гудел различными тонами. И, вот, если одну пару кнопок я уже научился контролировать, то другие никак не могу. Подробности в комментариях к коду.
Сам я программировать и разбираться с МК начал меньше месяца назад. Прошу не судить строго за любые возможные глупые ляпы. Буду благодарен вам за любую помощь! Мозг уже разрывается на части wacko.gif
Прогаю в IAR EW на MSP430f2112.

Код
#include "msp430f2112.h"

#define RED_LEDS   (BIT0) //красненький светодиодик
#define GREEN_LED  (BIT2) //зелёненький

#define SPKR       (BIT3) // Динамик (здесь я смотрю результат через осциллограф)

#define BUTTON1    (BIT0) // Две кнопки (SB1 и SB2), подсоединённые к P2.0
#define BUTTON2    (BIT3) // Две кнопки (SB3 и SB4), подсоединённые к P2.3
#define BUTTON3    (BIT4) // Две кнопки (SB5 и SB6), подсоединённые к P2.4

volatile char flash  = 0;
volatile char flash2 = 0;

  void TA_init(void);
  void ADC_init(void);

//***********************Главная функция*************************

void main(void)
{
  WDTCTL = WDTPW + WDTHOLD; // Останавливаем сторожевой таймер

  BCSCTL3 = LFXT1S1; // Источником сигнала ACLK будет низкочастотный встроенный генератор со сверхнизким
                     // энергопотреблением VLO. Частота ACLK = 12 кГц

  P1DIR |=  GREEN_LED; // Устанавливаем порт на выход
  P1OUT &= ~GREEN_LED; // Отключаем диод
  P1DIR |=  RED_LEDS;
  P1OUT &= ~RED_LEDS;
  P1DIR |=  SPKR;
  P1REN |=  SPKR;     // Устанавливаем резистор подтяжки, в нашем случае он уменьшает громкость динамика в ~4раза
  P1OUT &= ~SPKR;

  TA_init();
  ADC_init();

  __bis_SR_register(GIE); // Разрешаем прерывания

while(1)
   {
    /*ADC10CTL0 &= ~ENC;
    ADC10CTL1 = INCH_4;
    ADC10CTL0 |= ENC + ADC10SC;          // Если здесь снять комментарии, то мигать светодиод будет
    LPM3;                                // только при нажатии трёх! кнопок одновременно.
    ADC10CTL0 &= ~ENC;                   // Т.е., допустим, чтобы мигать по таймеру А0, нужно
    ADC10CTL1 = INCH_3;                  // нажать SB1, SB3 и SB5. По отдельности они вызывают
    ADC10CTL0 |= ENC + ADC10SC;          // какие-то отдельные колебания, которые только складываясь
    LPM3;*/                              // вместе, дают желаемый сигнал.
    ADC10CTL0 &= ~ENC;
    ADC10CTL1 = INCH_0;
    ADC10CTL0 |= ENC + ADC10SC;
    LPM3;
    }
}

//***********************Инициализируем АЦП*************************

void ADC_init(void)
{
  ADC10CTL0 = MSC + SREF_1 + ADC10SHT_2 + REFON + ADC10ON + ADC10IE;  // настраиваем и включаем АЦП, разрешаем прерывания
  ADC10CTL1 = ADC10SSEL_1 + CONSEQ_0; // Буду благодарен, если кто-нибудь объянит мне, что меняет,
                                      // и на практике делает CONSEQ, т.к. в моём случае, его изменение ни к чему не приводит.
                                      // и я не понимаю чего люди пытаются им добиться.
  ADC10AE0 |= BUTTON1 + BUTTON2 + BUTTON3; // Пины с кнопками как входы АЦП
}

//***********************Инициализируем Таймер*************************

void TA_init(void)
{
  //Создаём таймер для удобного моргания через прерывания
  TA0CCR0 = 56;
  TA0CCR1 = 28;
  TA0CCTL0 = CCIE;    // Разрешаем прерывание таймера по достижении CCR0
  TA0CCTL1 = OUTMOD_3 + CCIE;
  TA0CTL = TASSEL_1 + MC_1 + TAIE;  // ACLK, Прямой счёт
}

//***********************Инициализируем Прерывания*************************

#pragma vector = TIMER0_A0_VECTOR
__interrupt void T0_A0(void)
{
        P1OUT ^= flash;                 // если flash == 0, светодиод не горит
                                        // если flash == GREEN_LED, мигаем светодиодом
        LPM3_EXIT;

}

#pragma vector = TIMER0_A1_VECTOR
__interrupt void T0_A1(void)
{
   P1OUT ^= flash2;                    // если flash2 == 0, светодиод не горит
                                       // если flash2 == GREEN_LED, мигаем светодиодом

   TA0IV = 0x00;  //чистим регистр вектора
   LPM3_EXIT;
}

#pragma vector = ADC10_VECTOR
__interrupt void ADC10_ISR (void)
{
      if (0x03FC < ADC10MEM && ADC10MEM < 0x0400)  // Если нажата SB1
      {
      P1OUT &= ~RED_LEDS;
      //flash  =  GREEN_LED; //мигает светодиод по таймеру A0
      flash = SPKR; // или гудит динамик
      flash2 = 0;
      }
      else if (0x025A < ADC10MEM && ADC10MEM < 0x03FA) // Если кнопки не нажаты
      {
       P1OUT &= ~GREEN_LED;
       P1OUT &= ~RED_LEDS;
       flash = 0;
       flash2 = 0;
      }
      else if (0x0246 < ADC10MEM && ADC10MEM < 0x025A) // Если обе кнопки нажаты
      {
       P1OUT &= ~GREEN_LED;
       P1OUT |=  RED_LEDS;
       flash = 0;
       flash2 = 0;
      }
      else if (0x017F < ADC10MEM && ADC10MEM < 0x0193) // Если нажата SB2
      {
       P1OUT &= ~RED_LEDS;
       //flash2 =  GREEN_LED; //мигает светодиод по таймеру A1
       flash2 = SPKR; //или гудит динамик
       flash = 0;
      }
      LPM3_EXIT;
  }

Нажмите для просмотра прикрепленного файла
Turbo_enot
Прошу прощения, в комментариях немного ошибся: Что делает CONSEQ я знаю, я OUTMOD имел ввиду.

Добавил ещё один таймер, и сделал небольшой опрос в прерывании АЦП. Теперь работают две пары кнопок. Но какую частоту выдаёт мне TAxCCR1? Явно не ту, которую я ожидаю. Если динамик и гудит различными тонами, то светодиод ведёт себя неадекватно. (Когда вывожу на диод, то TACCR задаю, естественно, выше)

CODE
//Создаём ещё один таймер для удобного моргания через прерывания
//TA1R = 112;// смещение относительно первого - не знаю нужно или нет
TA1CCR0 = 14; // Задержка таймера для мигания
TA1CCR1 = 7;
TA1CCTL0 = CCIE; // Разрешаем прерывание таймера по достижении CCR0
TA1CCTL1 = OUTMOD_0 + CCIE;
TA1CTL = TASSEL_1 + MC_1 + TAIE; // + TACLR; // ACLK, Прямой счёт, обнуление таймера
//*******************************
#pragma vector = ADC10_VECTOR
__interrupt void ADC10_ISR (void)
{
if ((ADC10CTL1 & INCH_3) == INCH_3)
{
if (0x03FC < ADC10MEM && ADC10MEM < 0x0400) // Если нажата SB1
{
P1OUT &= ~RED_LEDS;
//flash3 = GREEN_LED;
flash3 = SPKR;
flash4 = 0;
}
else if (0x025A < ADC10MEM && ADC10MEM < 0x03FA) // Если кнопки не нажаты
{
P1OUT &= ~GREEN_LED;
P1OUT &= ~RED_LEDS;
flash3 = 0;
flash4 = 0;
}
else if (0x0246 < ADC10MEM && ADC10MEM < 0x025A) // Если обе кнопки нажаты
{
P1OUT &= ~GREEN_LED;
P1OUT |= RED_LEDS;
flash3 = 0;
flash4 = 0;
}
else if (0x017F < ADC10MEM && ADC10MEM < 0x0193) // Если нажата SB2
{
P1OUT &= ~RED_LEDS;
//flash3 = GREEN_LED;
flash4 = SPKR;
flash3 = 0;
}
}
else if ((ADC10CTL1 & INCH_0) == INCH_0)
{
if (0x03FC < ADC10MEM && ADC10MEM < 0x0400) // Если нажата SB1
{
P1OUT &= ~RED_LEDS;
//flash = GREEN_LED;
flash = SPKR;
flash2 = 0;
}
else if (0x025A < ADC10MEM && ADC10MEM < 0x03FA) // Если кнопки не нажаты
{
P1OUT &= ~GREEN_LED;
P1OUT &= ~RED_LEDS;
flash = 0;
flash2 = 0;
}
else if (0x0246 < ADC10MEM && ADC10MEM < 0x025A) // Если обе кнопки нажаты
{
P1OUT &= ~GREEN_LED;
P1OUT |= RED_LEDS;
flash = 0;
flash2 = 0;
}
else if (0x017F < ADC10MEM && ADC10MEM < 0x0193) // Если нажата SB2
{
P1OUT &= ~RED_LEDS;
//flash2 = GREEN_LED;
flash2 = SPKR;
flash = 0;
}
}
LPM3_EXIT;
}
d7d1cd
Я может чего то не понимаю, но как микроконтроллер поймет, что нажата та или иная кнопка, каждая из которых подключена к одному и тому же пину?
Turbo_enot
Цитата(d7d1cd @ Aug 2 2013, 16:41) *
Я может чего то не понимаю, но как микроконтроллер поймет, что нажата та или иная кнопка, каждая из которых подключена к одному и тому же пину?

АЦП. При помощи него я измеряю напряжение на пине. На схеме, которую я прикрепил, видно, что напряжение будет меняться при различных вариантах нажатия. Кнопки две - варианта четыре. Это всё находится у меня в прерывании АЦП - взгляните.
MrYuran
Вообще-то, на один вход АЦП можно повесить все ваши кнопки, используя резистивную матрицу
Запараллелив на вход компаратора, можно будет получать прерывание по нажатию/отпусканию (но не по смене комбинации)



Статью гуглите сами.

Хотя, вот она.
Turbo_enot
Цитата(MrYuran @ Aug 5 2013, 10:33) *
Вообще-то, на один вход АЦП можно повесить все ваши кнопки, используя резистивную матрицу
Запараллелив на вход компаратора, можно будет получать прерывание по нажатию/отпусканию (но не по смене комбинации)



Статью гуглите сами.

Хотя, вот она.


Это здорово, что можно повесить все, но у меня всё именно так, как на схеме, которую я приложил в самом начале. И это не исправить. Простите, из вашего ответа не пойму: АЦП или компаратор? Я понимаю, что компаратор тоже АЦП, но в моём случае это 10-битное АЦП, и в ссылке, что вы мне скинули, тоже АЦП. Там лишь небольшое упоминание о компараторе в конце. Но он не имеет регистра, в котором хранит текущее значение, которое (ADC10MEM) я так удобно сравниваю, дабы выискать нажатую кнопку))) Тогда как? Сопротивление измерять? Буду очень рад, если дообъясните)
И, да, да, насчёт "прерывание по нажатию/отпусканию (но не по смене комбинации)" - Какие комбинации? Я вроде через нажатие/отпускание делаю... sad.gif
MrYuran
Цитата(Turbo_enot @ Aug 5 2013, 12:44) *
Там лишь небольшое упоминание о компараторе в конце. Но он не имеет регистра, в котором хранит текущее значение, которое (ADC10MEM) я так удобно сравниваю, дабы выискать нажатую кнопку))) Тогда как? Сопротивление измерять? Буду очень рад, если дообъясните)
И, да, да, насчёт "прерывание по нажатию/отпусканию (но не по смене комбинации)" - Какие комбинации? Я вроде через нажатие/отпускание делаю... sad.gif

Компаратор фиксирует момент нажатия (переход через порог), формирует прерывание, а дальше запускается АЦП.
Это альтернатива постоянному опросу.
Turbo_enot
Цитата(MrYuran @ Aug 5 2013, 13:20) *
Компаратор фиксирует момент нажатия (переход через порог), формирует прерывание, а дальше запускается АЦП.
Это альтернатива постоянному опросу.


Идея хороша, НО:
Вы имеете ввиду, если напряжение, которое фиксируется компаратором больше некоего порога, то -> АЦП прерывание, верно?
Если я понял верно, ТО: у меня напряжение при нажатии 2й кнопки < напряжения при нажатии обеих кнопок < напряжения при не нажатых кнопках < напряжения при нажатой 1й. -> у меня не порог, а некие определённые значения, которые мне как раз и приходится опрашивать при помощи АЦП.
Если я понял неверно, то простите мне мою тупость, и я подумаю тщательнее.
Turbo_enot
В принципе, проблему мне удалось решить. Все диоды мигают по-разному. Правда, с динамиками так не выходит. Всё равно пробелы в знаниях и навыках остались..
MrYuran
Код
ADC10CTL1 = ADC10SSEL_1 + CONSEQ_0; // Буду благодарен, если кто-нибудь объянит мне, что меняет,
                                      // и на практике делает CONSEQ, т.к. в моём случае, его изменение ни к чему не приводит.
                                      // и я не понимаю чего люди пытаются им добиться.

CONSEQ - это режим выборки. От него зависит однократный/многократный запуск и распределение данных по регистрам ADC_MEM.
Но поскольку здесь регистр данных всего один, назначение не столь очевидно. А вот в ADC12 забавные вещи можно делать. Например, за один проход последовательно опросить все входы и разложить данные в соответствующие регистры. Или оцифровать подряд несколько раз один вход и по прерыванию получить уже не одну, а несколько выборок, разложенных в разные регистры. Ну и всякие промежуточные комбинации.
С ADC10, как я понял, подобные вещи можно делать, используя модуль DTC, который каждую выборку автоматически переносит из ADC10MEM в заданные ячейки памяти
Turbo_enot
Цитата(MrYuran @ Aug 5 2013, 16:25) *
Код
ADC10CTL1 = ADC10SSEL_1 + CONSEQ_0; // Буду благодарен, если кто-нибудь объянит мне, что меняет,
                                      // и на практике делает CONSEQ, т.к. в моём случае, его изменение ни к чему не приводит.
                                      // и я не понимаю чего люди пытаются им добиться.

CONSEQ - это режим выборки. От него зависит однократный/многократный запуск и распределение данных по регистрам ADC_MEM.
Но поскольку здесь регистр данных всего один, назначение не столь очевидно. А вот в ADC12 забавные вещи можно делать. Например, за один проход последовательно опросить все входы и разложить данные в соответствующие регистры. Или оцифровать подряд несколько раз один вход и по прерыванию получить уже не одну, а несколько выборок, разложенных в разные регистры. Ну и всякие промежуточные комбинации.
С ADC10, как я понял, подобные вещи можно делать, используя модуль DTC, который каждую выборку автоматически переносит из ADC10MEM в заданные ячейки памяти


Спасибо. Надо бы внимательнее изучить ADC12. Любопытные вещи.
А можете человеческим языком объяснить что OUTMOD в TACCTL1 означает? a14.gif
MrYuran
Цитата(Turbo_enot @ Aug 6 2013, 09:11) *
А можете человеческим языком объяснить что OUTMOD в TACCTL1 означает? a14.gif

Означает, что можно управлять соответствующими ногами "железно" по совпадению TA и TACCRx, без входа в прерывание и соответственно без джиттера и накладных расходов.
Turbo_enot
Цитата(MrYuran @ Aug 6 2013, 11:03) *
Означает, что можно управлять соответствующими ногами "железно" по совпадению TA и TACCRx, без входа в прерывание и соответственно без джиттера и накладных расходов.


Спасибо!
Turbo_enot
Ребят, ещё такой вопрос: Как сделать короткий ШИМ сигнал? Нужно, чтобы динамик пикнул на секунду, одновременно с нажатием кнопки. Подскажите свои идеи, пожалуйста.
VasyaT
Цитата(Turbo_enot @ Aug 9 2013, 13:44) *
Ребят, ещё такой вопрос: Как сделать короткий ШИМ сигнал? Нужно, чтобы динамик пикнул на секунду, одновременно с нажатием кнопки. Подскажите свои идеи, пожалуйста.

1. Включить ШИМ.
2. Подождать 1 сек
3. Выключить ШИМ.
Как-то так наверное laughing.gif
Turbo_enot
Цитата(VasyaT @ Aug 9 2013, 23:43) *
1. Включить ШИМ.
2. Подождать 1 сек
3. Выключить ШИМ.
Как-то так наверное laughing.gif


Да, мне удалось. К счастью, я познакомился с командой __delay_cycles. Спасибо всем.
MrYuran
Цитата(Turbo_enot @ Aug 12 2013, 09:46) *
Да, мне удалось. К счастью, я познакомился с командой __delay_cycles.

К несчастью, этот макрос совсем не для того, чтобы делать секундные задержки.
Так что курите дальше таймеры, системные тики и общепринятые методики отсчета временных интервалов.
К мсп430, правда, это уже никак не относится.
Turbo_enot
Цитата(MrYuran @ Aug 12 2013, 10:21) *
К несчастью, этот макрос совсем не для того, чтобы делать секундные задержки.
Так что курите дальше таймеры, системные тики и общепринятые методики отсчета временных интервалов.
К мсп430, правда, это уже никак не относится.


Спасибо, не курю, веду здоровый образ жизни. Ладно, шутка плоская. Я находил всевозможные способы, но для ACLK это самое ненапряжное. Да и пихать её можно куда угодно. Разве это может не относится к MSP430? Это же всё особенности её работы.
MrYuran
Цитата(Turbo_enot @ Aug 12 2013, 13:44) *
Разве это может не относится к MSP430? Это же всё особенности её работы.

Ну вот раз уж у вас в коде встречается LPM3, то вероятно, речь идет об экономии батареек.
И в таком случае логично было бы секунду спокойно спать, вместо того чтобы молотить впустую __delay_cycles, которые к тому же будут плавать в зависимости от прерываний.
Можно настроить таймер на секундную задержку, но это не всегда возможно, да и задач много, а таймеров обычно намного меньше.
Поэтому обычно таймер настраивается на какой-нибудь типовой интервал (так называемый системный тик), а в прерывании инкрементируется переменная "системного времени" и выполняются необходимые проверки.
Это напрямую к архитектуре не относится, я это имел в виду. И вообще, лучше не привязываться к архитектурным особенностям конкретного контроллера без особых на то причин, если можно решить задачу программным способом.
Turbo_enot
Цитата(MrYuran @ Aug 12 2013, 14:36) *
Ну вот раз уж у вас в коде встречается LPM3, то вероятно, речь идет об экономии батареек.
И в таком случае логично было бы секунду спокойно спать, вместо того чтобы молотить впустую __delay_cycles, которые к тому же будут плавать в зависимости от прерываний.
Можно настроить таймер на секундную задержку, но это не всегда возможно, да и задач много, а таймеров обычно намного меньше.
Поэтому обычно таймер настраивается на какой-нибудь типовой интервал (так называемый системный тик), а в прерывании инкрементируется переменная "системного времени" и выполняются необходимые проверки.
Это напрямую к архитектуре не относится, я это имел в виду. И вообще, лучше не привязываться к архитектурным особенностям конкретного контроллера без особых на то причин, если можно решить задачу программным способом.


Да, вы правы, речь идёт об экономии. И, да, вы правы, таймеров намного меньше чем задач. Но на ошибках я постепенно совершенствую свой код, и стараюсь использовать ресурсы с умом. Я попробую воспользоваться вашим советом. Хотя частота тактового генератора в моём экономном случае 12кГЦ, и с ней трудно играть в точность. Мой код уже разросся, и сейчас я как раз хочу привести его в человеческий вид, увести всё что можно и где это возможно, в спячку. Не совсем понял про переменную "системного времени", я всё-таки ещё нуб в терминах.
MrYuran
Цитата(Turbo_enot @ Aug 12 2013, 15:03) *
Не совсем понял про переменную "системного времени", я всё-таки ещё нуб в терминах.

Заводим специальную переменную, которая будет считать миллисекунды (допустим) или системные "тики".
unsigned int uiRealTime;

Настраиваем таймер на фиксированный интервал, например, 10мс.
В прерывании увеличиваем нашу переменную на величину интервала (10), получим время в мс. Или на 1, получим количество тиков.
Далее, в основной программе для каждого интервала заводим переменную-таймер.
Например:
unsigned int uiTimer1;

Засекаем временной интервал:
uiTimer1 = uiRealTime + SOME_TIME_INTERVAL;

Дальше нужно проверять, когда значение текущего времени превысит значение переменной uiTimer1.
Для экономии энергии это лучше сделать в прерывании таймера.
Далее, либо устанавливать флаг события и ловить его в основном цикле, либо выполнять действия прямо в прерывании.
Тут уже вариантов множество.
Turbo_enot
Цитата(MrYuran @ Aug 13 2013, 08:29) *
Засекаем временной интервал:
uiTimer1 = uiRealTime + SOME_TIME_INTERVAL;


Это же условная запись, да? Т.е. просто заносим в uiTimer1 некоторое значение (в основной программе), до которого рано или поздно дорастёт uiRealTime, верно? А проверять дорос или нет следует в прерывании, вроде понял. Спасибо.

Turbo_enot
Да, кстати, вот ещё что. Создал я небольшой примерчик для проработки сказанного вами:

CODE
void delay_ms(uint16_t ms);
void InitSystem(void);

#pragma vector = TIMER0_A0_VECTOR
__interrupt void T0_A0(void)
{
uiRealTime += 1;
if (uiRealTime > uiTimer1)
{
uiRealTime = 0;
P1OUT ^= flash;
LPM3_EXIT;
}

}
// Задержка в мииллисекундах
void delay_ms(uint16_t ms)
{
uint16_t i;

for (i = ms; i != 0; i--)
{
TA1CCTL0 &= ~CCIFG; // Сбрасываем флаг прерывания
TA1CTL |= TACLR + MC_1; // Очищаем счетчик и начинаем отсчет периода времени в 1 мс

while ((TACCTL0 & CCIFG) == 0)
;
}

TA1CTL &= ~MC_1; // Останавливаем таймер
}

// Инициализация системы
void InitSystem(void)
{
uint8_t i;

WDTCTL = WDTPW + WDTHOLD; // Останавливаем собаку

// Инициализируем ACLK
BCSCTL3 = LFXT1S_2;

// Устанавливаем источник частоты для таймера Timer_A
TA0CCTL0 = CCIE; // Разрешаем прерывание таймера по достижении CCR0
TA0CTL |= TASSEL_1 + MC_1 + TACLR; // (ACLK) = 12 kHz
TACCR0 = 11; // (12 kHz / TACCR + 1) = 1 kHz, T = 1 мc

TA1CTL &= MC_0; // Останавливаем таймер
TA1CTL |= TASSEL_1; // ACLK = 12 kHz
TA1CCR0 = 11; // (12 MHz / TACCR + 1) = 1 kHz, T = 1 мc
}

//***********************Главная функция*************************

void main(void)
{
InitSystem();
P1DIR |= GREEN_LED;
P1OUT &= ~GREEN_LED;
P1DIR &= ~BUTTON3;
P1IES = BUTTON3;
P1IE = BUTTON3;
uiTimer1 = 250;
uiTimer2 = 500;
__bis_SR_register(GIE + LPM3_bits);
while (1)
{
LPM3;
}
}

#pragma vector = PORT1_VECTOR
__interrupt void P1_isr(void)
{
P1IFG &= ~BUTTON3;
if (flash == GREEN_LED)
{
flash = 0;
P1OUT &= ~GREEN_LED;
}
else
flash = GREEN_LED;
delay_ms(250); // от дребезга
}


Там всё сумбурно, но надеюсь понятно.
Просто я создал программный тик, задержку в мс, и, ВСЁ! - двух таймеров как не бывало. А учитывая, что uiRealTime приходится обнулять, то выходит, что нельзя создать несколько uiTimer'ов на один uiRealTime. Получается их целую армию клонов создавать придётся, если программа большая: на каждый интервал свой uiRealTime со своим uiTimer'ом.
MrYuran
Все не так sad.gif

Таймер (физический) настраивается один раз и тикает постоянно. Как часы.
На него можно вешать неограниченное количество программных таймеров, представляющих из себя переменную, хранящую интервал и проверку на окончание интервала.

Тогда функция delay() будет выглядеть примерно так:

Код
void delay_ms(uint16_t  ms)
{
    static uint16_t delay_timer;

    delay_timer = uiRealTime + ms;

    while(uiRealTime < delay_timer);
}


Turbo_enot
Цитата(MrYuran @ Aug 13 2013, 16:09) *
Все не так sad.gif


Уж простите, я не только в MSP430 новичок, но и в программировании sad.gif Чем больше вы рассказываете, тем более глупым я себя чувствую. Вам большое спасибо за объяснения a14.gif

Цитата(MrYuran @ Aug 13 2013, 16:09) *
Таймер (физический) настраивается один раз и тикает постоянно. Как часы.
На него можно вешать неограниченное количество программных таймеров, представляющих из себя переменную, хранящую интервал и проверку на окончание интервала.


Наконец-то я это уловил cool.gif Разбираю "по косточкам". Завтра выложу исправленную версию, дабы убедиться, что я всё понял. smile3046.gif

Turbo_enot
Всё довольно грустно. Даже загадочно. Вот код:

CODE
void delay_ms(uint16_t ms);
void InitSystem(void);

#pragma vector = TIMER0_A0_VECTOR
__interrupt void T0_A0(void)
{
uiRealTime += 1;
if (uiRealTime < uiTimer1)
{
P1OUT ^= SPKR;
}
}
// Задержка в мииллисекундах
void delay_ms(uint16_t ms)
{
static uint16_t delay_timer;

delay_timer = uiRealTime + ms;

while(uiRealTime < delay_timer);

}

// Инициализация системы
void InitSystem(void)
{
WDTCTL = WDTPW + WDTHOLD; // Останавливаем собаку

// Инициализируем ACLK
BCSCTL3 = LFXT1S_2;

// Устанавливаем источник частоты для таймера Timer_A
TACCR0 = 11; // (12 kHz / TACCR + 1) = 1 kHz, T = 1 мc
TA0CCTL0 = CCIE; // Разрешаем прерывание таймера по достижении CCR0
TA0CTL |= TASSEL_1 + MC_1 + TACLR; // (ACLK) = 12 kHz
}

//***********************Главная функция*************************

void main(void)
{
InitSystem();
P1DIR &= ~BUTTON3;
P1IES = BUTTON3;
P1IE = BUTTON3;
P1DIR |= SPKR;
P1REN |= SPKR;
P1OUT &= ~SPKR;
__bis_SR_register(GIE);
while (1)
{

}
}

#pragma vector = PORT1_VECTOR
__interrupt void P1_isr(void)
{
P1IFG &= ~BUTTON3;
uiTimer1 = uiRealTime + 1000;
//delay_ms(1000); // от дребезга
}


Под SPKR понимается выход на динамик, с которого я осциллографом снимаю импульсы. Я исправил код, но результат очень странный: если проверяю на подключённой по JTAG плате, то при нажатии кнопки как надо инициируется секундный писк. ОДНАКО, после некоторого времени тишины, писк неожиданно самопроизвольно включается вновь, но уже на БОЛЕЕ длительное время, и так же внезапно отключается. Если плата отключена, то писк срабатывает только при первом нажатии, а затем уходит в глухую оборону... Думаю факт подключения здесь особую роль не играет, а просто я опять где-то накосячил и чего-то не понял. А функция delay_ms, вроде как уходит в бесконечный цикл, после строчки while(uiRealTime < delay_timer); И если я её (функцию delay_ms) добавляю в конце своего кода, то, соответственно, ничего не происходит. help.gif

Прошу прощения, ступил. Никто за меня переменную uiTimer1 не обнулил, вот полтергейст и возникал. biggrin.gif
CODE
#pragma vector = TIMER0_A0_VECTOR
__interrupt void T0_A0(void)
{
uiRealTime += 1;
if (uiRealTime < uiTimer1)
{
P1OUT ^= SPKR;
}
else
uiTimer1 = 0; // Вот, обнулил её здесь, теперь ничего без моего нажатия не гудит
}


Хотя не совсем понимаю, как она могла влезать в неравенство if (uiRealTime < uiTimer1)

А вот вопрос с delay_ms остаётся открытым. Там наверняка тоже всё на поверхности лежит, только я не догнал ещё.

Раньше я делал так:

Был у меня таймер

CODE
TA1CCR0 = 6; // Частота писка = (12000/TA1CCR0+1)
TA1CTL = TASSEL_1 + MC_1 + TACLR; // ACLK, Прямой счёт, обнуление таймера


И, был следующий код:

CODE
if (m >= 0) //Если выполняется условие
{
TA1CCTL0 = CCIE; // Разрешаем прерывание таймера
__delay_cycles(50000);
flash3 = SPKR;
}


Возникало прерывание:

CODE
#pragma vector = TIMER1_A0_VECTOR // Прерывания таймера на динамик
__interrupt void T1_A0(void)
{
if (l == 200) // Если моргнули 200 раз
{
TA1CCTL0 &= ~CCIE; // Запрещаем прерывания (писк)
flash3 = 0; // Говорим, что flash3 уже не спикер
P1OUT &= ~SPKR; // Спикер отключаем
l = 0; // Обнуляем кол-во морганий сигналами
}
if (flash3 == SPKR) // если flash3 == SPKR,
{
P1OUT ^= flash3; // мигаем сигналом

l++; // Говорим, что моргнули сигналом очередной раз
}
LPM3_EXIT;
}


Сейчас разбираюсь с системными тиками, и мне всё больше кажется, что они так не смогут, ведь тогда мне нужны часы - 1 таймер, определённая частота гудения - второй таймер, а ведь у меня в проге ещё и светодиоды разной частотой мигают. Если только при помощи этих системных таймеров возможно будет этими светодиодами управлять (менять их частоту мигания). Я уже как-то совсем запутался sad.gif wacko.gif Может у меня всё хорошо было для моего случая
Turbo_enot
Ещё такой вопрос: как долго счётчик uiRealTime будет считать? Ведь однажды он переполнится, и тогда что?


Опять вопрос был глупый, всё нашёл:

"Замечание по использованию типа unsigned int:
Когда переменная типа int в следствие арифметической операции достигает своего максимального значения, она "перескакивает" на самое минимальное значение и наоборот"
Turbo_enot
help.gif Буду очень благодарен, если кто-нибудь объяснит, как же всё-таки будет выглядеть функция delay_ms
thodnev
В общем посмотрите примеры на обучающем вики от техаса: ссылка
Так же про таймеры написано в ихнем учебнике: PDF; источник
Код
#pragma vector = TIMERA0_VECTOR
__interrupt void CCR0_ISR(void) {

    // где-то глобально указана переменная i
    // далее просто выставляем действия на каждый n-й тик
    // при этом не забываем обнулить i на тике с максимальным n
    // иначе возникнет переполнение!
    
    ++i; // собственно по каждому прерыванию таймера инкрементируем.

    switch (i) {
        case 20: Call_Cthulhu(); break;
        case 500: Blink_Led(); break;
        case 1000: PWM_Switch(); i=0; break; // // <<== не забываем обнулить i
        default:;
    }
}


Итого у нас таймер отщелкивает допустим с частотой 1000 Гц. Тогда 2 раза в секунду будет мигать светодиод, и на секунду будет включаться-выключаться ШИМ динамика. Можно сделать по операциям(ака конечный автомат):

Код
#pragma vector = TIMERA0_VECTOR
__interrupt void CCR0_ISR(void) {

    ++i; // в данном случае i показывает следующую элементарную
           // операцию, которую необходимо выполнить

    switch (i) {
        case 1: Wake_up(); break;
        case 2: Drink_coffee(); break;
        case 3: Start_working(); break;
        case 4: Stop_working(); break;
        case 5: Fall_asleep(); break;
        default: i=0; // <<== не забываем
    }
}

Turbo_enot
Спасибо за советы, подразобрался biggrin.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.