Перейти к содержанию
    

Кнопки, таймеры, АЦП, взрыв мозга

1. Включить ШИМ.

2. Подождать 1 сек

3. Выключить ШИМ.

Как-то так наверное :laughing:

 

Да, мне удалось. К счастью, я познакомился с командой __delay_cycles. Спасибо всем.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, мне удалось. К счастью, я познакомился с командой __delay_cycles.

К несчастью, этот макрос совсем не для того, чтобы делать секундные задержки.

Так что курите дальше таймеры, системные тики и общепринятые методики отсчета временных интервалов.

К мсп430, правда, это уже никак не относится.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

К несчастью, этот макрос совсем не для того, чтобы делать секундные задержки.

Так что курите дальше таймеры, системные тики и общепринятые методики отсчета временных интервалов.

К мсп430, правда, это уже никак не относится.

 

Спасибо, не курю, веду здоровый образ жизни. Ладно, шутка плоская. Я находил всевозможные способы, но для ACLK это самое ненапряжное. Да и пихать её можно куда угодно. Разве это может не относится к MSP430? Это же всё особенности её работы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Разве это может не относится к MSP430? Это же всё особенности её работы.

Ну вот раз уж у вас в коде встречается LPM3, то вероятно, речь идет об экономии батареек.

И в таком случае логично было бы секунду спокойно спать, вместо того чтобы молотить впустую __delay_cycles, которые к тому же будут плавать в зависимости от прерываний.

Можно настроить таймер на секундную задержку, но это не всегда возможно, да и задач много, а таймеров обычно намного меньше.

Поэтому обычно таймер настраивается на какой-нибудь типовой интервал (так называемый системный тик), а в прерывании инкрементируется переменная "системного времени" и выполняются необходимые проверки.

Это напрямую к архитектуре не относится, я это имел в виду. И вообще, лучше не привязываться к архитектурным особенностям конкретного контроллера без особых на то причин, если можно решить задачу программным способом.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну вот раз уж у вас в коде встречается LPM3, то вероятно, речь идет об экономии батареек.

И в таком случае логично было бы секунду спокойно спать, вместо того чтобы молотить впустую __delay_cycles, которые к тому же будут плавать в зависимости от прерываний.

Можно настроить таймер на секундную задержку, но это не всегда возможно, да и задач много, а таймеров обычно намного меньше.

Поэтому обычно таймер настраивается на какой-нибудь типовой интервал (так называемый системный тик), а в прерывании инкрементируется переменная "системного времени" и выполняются необходимые проверки.

Это напрямую к архитектуре не относится, я это имел в виду. И вообще, лучше не привязываться к архитектурным особенностям конкретного контроллера без особых на то причин, если можно решить задачу программным способом.

 

Да, вы правы, речь идёт об экономии. И, да, вы правы, таймеров намного меньше чем задач. Но на ошибках я постепенно совершенствую свой код, и стараюсь использовать ресурсы с умом. Я попробую воспользоваться вашим советом. Хотя частота тактового генератора в моём экономном случае 12кГЦ, и с ней трудно играть в точность. Мой код уже разросся, и сейчас я как раз хочу привести его в человеческий вид, увести всё что можно и где это возможно, в спячку. Не совсем понял про переменную "системного времени", я всё-таки ещё нуб в терминах.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не совсем понял про переменную "системного времени", я всё-таки ещё нуб в терминах.

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

unsigned int uiRealTime;

 

Настраиваем таймер на фиксированный интервал, например, 10мс.

В прерывании увеличиваем нашу переменную на величину интервала (10), получим время в мс. Или на 1, получим количество тиков.

Далее, в основной программе для каждого интервала заводим переменную-таймер.

Например:

unsigned int uiTimer1;

 

Засекаем временной интервал:

uiTimer1 = uiRealTime + SOME_TIME_INTERVAL;

 

Дальше нужно проверять, когда значение текущего времени превысит значение переменной uiTimer1.

Для экономии энергии это лучше сделать в прерывании таймера.

Далее, либо устанавливать флаг события и ловить его в основном цикле, либо выполнять действия прямо в прерывании.

Тут уже вариантов множество.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Засекаем временной интервал:

uiTimer1 = uiRealTime + SOME_TIME_INTERVAL;

 

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

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

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'ом.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Все не так :(

 

Таймер (физический) настраивается один раз и тикает постоянно. Как часы.

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

 

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

 

void delay_ms(uint16_t  ms)
{
    static uint16_t delay_timer;

    delay_timer = uiRealTime + ms;

    while(uiRealTime < delay_timer);
}

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Все не так :(

 

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

 

Таймер (физический) настраивается один раз и тикает постоянно. Как часы.

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

 

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

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

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:

 

Прошу прощения, ступил. Никто за меня переменную uiTimer1 не обнулил, вот полтергейст и возникал. :biggrin:

#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 остаётся открытым. Там наверняка тоже всё на поверхности лежит, только я не догнал ещё.

 

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

 

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

 

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

 

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

 

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

 

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

 

#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 таймер, определённая частота гудения - второй таймер, а ведь у меня в проге ещё и светодиоды разной частотой мигают. Если только при помощи этих системных таймеров возможно будет этими светодиодами управлять (менять их частоту мигания). Я уже как-то совсем запутался :( :wacko: Может у меня всё хорошо было для моего случая

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ещё такой вопрос: как долго счётчик uiRealTime будет считать? Ведь однажды он переполнится, и тогда что?

 

 

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

 

"Замечание по использованию типа unsigned int:

Когда переменная типа int в следствие арифметической операции достигает своего максимального значения, она "перескакивает" на самое минимальное значение и наоборот"

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

:help: Буду очень благодарен, если кто-нибудь объяснит, как же всё-таки будет выглядеть функция delay_ms

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В общем посмотрите примеры на обучающем вики от техаса: ссылка

Так же про таймеры написано в ихнем учебнике: 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; // <<== не забываем
    }
}

 

Изменено пользователем thodnev

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...