Проблема с оператором switch

Вопросы настройки, программирования, прошивки микроконтроллеров и микросхем программируемой логики
Закрыто
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт май 05, 2011 19:53:40
Откуда: Харьков

Сообщение Rondo »

Доброго времени суток.
Возникла проблема: пишу прогу симулятор светофора (учусь мигать светодиодами) и возникла такая проблема
оператор switch не входит в тело функции, точнее проходит только ее 1е значение (1й case) и выходит из нее
Вот код

Код: Выделить всё

#include <mega16.h>
unsigned int n;
unsigned int sec;
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
TCNT0=0x06;
n++;
    if(n==4)
    {
    sec++;
    n==0;
    }
}
void main(void)
{
DDRB=0xFF;
PORTB=0xFF;
TCCR0=0x05;
TCNT0=0x06;
OCR0=0x00;
TIMSK=0x01;
#asm("sei")

while (1)
      {
      switch (sec)
        case   1: PORTB=00011110;   //зажигаем красный
        case 30: PORTB=00011101;   //зажигаем желтый
        case 35: PORTB=00011101;   //тушим красный 
        case 40: PORTB=00011011;   //тушим желтый, зажигаем зеленый
        case 70: PORTB=00011111;   //тушим зеленый
        case 75: PORTB=00011011;   //зажигаем зеленый
        case 80: PORTB=00011111;   //тушим зеленый
        case 85: PORTB=00011011;   //зажигаем зеленый
        case 90: PORTB=00011101;   //тушим зеленый, зажигаем желтый
        case 95: PORTB=00011110;   //зажигаем красный
        
      }
}
Кратко о настройках:
- 8Мгц кристал
- таймер Т/С0 с частотой 7813 Гц (системный такт/1024)
- таймер начинает тикать со значения 6 (TCNT0=0x06;), т.е. по 250 мс и инкрементируем переменную n до 4х раз (n==4) и получаем секунду (переменная sec)
- далее главная функция и алгоритм

Вот тут то вся веселуха и начинается ....
Оператор switch "заходит" только на case 1: PORTB=00011110;
и весело с него вылетает :)
Изображение
Что токлько не было перепробовано ничего не смог получить, гугл как и поиск по этому форуму - молчит как партизан выдавая только уроки по С++ и C#.

Прошу помочь с моим говнокодом)) и обьяснить мне почемуже не пашет этот СВИТЧ

PS возможно напорол чтото с таймером оО


Заранее всем спасибо !
Контактная информация:
Реклама
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 2029
Зарегистрирован: Сб ноя 15, 2008 10:09:56
Откуда: г. Тула

Сообщение IfoR »

Всё что принадлежит switch, нужно заключать в блок { }

Код: Выделить всё

switch (a)
{
case 1:
break;
case 2:
break;
}
Вам компилятор, собственно, и говорит, что все, кроме 1-го, case не находятся внутри switch.
Бряки в конце каждого ответвления не забывайте ставить, а то у вас весь нижележайший код тоже будет выполняться. :)

А вообще, лучше для начала почитать книжки по C - там всё расписано.
Изображение
/dev/urandom - гигабайты информации.

OS: openSUSE 13.2 (x86_64)
Контактная информация:
Реклама
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт май 05, 2011 19:53:40
Откуда: Харьков

Сообщение Rondo »

Спс за совет - помогло, все скомпилилось, НО
теперь в протеусе оно както не так как надо симулируется ... все диоды тупо горят оО
что я мог не так зделать ? мб таймер сбит или не так настроен ?
Контактная информация:
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 335
Зарегистрирован: Чт май 21, 2009 13:54:07
Откуда: Москва

Сообщение romazan »

break; поставить забыл, поэтому программа проходит все пункты и не выходит при выполнении условия.
case 1: PORTB=00011110; //зажигаем красный
break;
case 30: PORTB=00011101; //зажигаем желтый
break;
Контактная информация:
Реклама
Эиком - электронные компоненты и радиодетали
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 335
Зарегистрирован: Чт май 21, 2009 13:54:07
Откуда: Москва

Сообщение romazan »

вот ещё
if(n==4)
{
sec++;
n=0;
}
Контактная информация:
Реклама
Опытный кот
Аватара пользователя
Сообщения: 703
Зарегистрирован: Вс янв 18, 2009 21:12:49

Сообщение asteroid7 »

volatile unsigned int sec;

...

switch (sec)
{
default: sec = 0; break; - это моя ошибка, в данном случае, так делать нельзя! Спасибо IfoR, что заметил.
case ... break;
...
}
Последний раз редактировалось asteroid7 Вт июл 26, 2011 21:31:57, всего редактировалось 1 раз.
Реклама
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 2029
Зарегистрирован: Сб ноя 15, 2008 10:09:56
Откуда: г. Тула

Сообщение IfoR »

asteroid7, зачем? Тогда у него этот дефолт только и будет выполняться. Просто sec = 0; (и за одно и n=0;) надо поставить где нить в начале main.

А вообще тут ещё бы не помещало бы вводить процессор в Idle режим, после каждой проверки: :))

Код: Выделить всё

int main(void)
{
 DDRB=0xFF;
 PORTB=0xFF;
 TCCR0=0x05;
 TCNT0=0x06; 
 OCR0=0x00;
 TIMSK=0x01; 
 MCUCR = 1<<6;
 #asm("sei")

 while (1)
 {
      #asm("sleep")
      switch (sec)
      {
        case   1: PORTB=00011110;   //зажигаем красный
        case 30: PORTB=00011101;   //зажигаем желтый
        case 35: PORTB=00011101;   //тушим красный
        case 40: PORTB=00011011;   //тушим желтый, зажигаем зеленый
        case 70: PORTB=00011111;   //тушим зеленый
        case 75: PORTB=00011011;   //зажигаем зеленый
        case 80: PORTB=00011111;   //тушим зеленый
        case 85: PORTB=00011011;   //зажигаем зеленый
        case 90: PORTB=00011101;   //тушим зеленый, зажигаем желтый
        case 95: PORTB=00011110;   //зажигаем красный
       }
 }

return 0;
}
Изображение
/dev/urandom - гигабайты информации.

OS: openSUSE 13.2 (x86_64)
Контактная информация:
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт май 05, 2011 19:53:40
Откуда: Харьков

Сообщение Rondo »

ага ... вроде втолок зачем нужен break
а зачем обьявлять переменную sec как volatile ???
а по поводу спячки, это как я понял пока тикает таймер и натикивает секунды нужные для свич/кейс проц так сказать спит и мало есть и просыпается только в установленых мною значениях свич ?

И да ... большое вам спасибо IfoR, romazan и asteroid7
Контактная информация:
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 2029
Зарегистрирован: Сб ноя 15, 2008 10:09:56
Откуда: г. Тула

Сообщение IfoR »

volatile говорит компилятору, что данную переменную оптимизировать не надо. Применяется там, где компилятор может вырезать кусок кода обработки данной переменной, посчитав, что сиё действие бессмысленно. Например, постоянная проверка переменной, которая внутри не изменяется, но может измениться в результате обработки прерывания. Ну у вас, вижу, и без этого всё работает. :)

По поводу спячки. Ну почти правильно поняли. В режиме Idle процессор засыпает до срабатывания любого прерывания (ну есть ещё несколько будящих источников, например, завершение преобразования АЦП). После обработки прерывания, он продолжает работу сразу после SLEEP, в данном случае, обработка переменной sec и очередной уход в Idle режим. Т.к. у вас срабатывание прерывание 4 раза за секунду, то и цикл будет выполняться 4 раза в секунду. Всё равно 3 срабатывания бессмысленны, но это не то, что было бы без SLEEP. :)
Изображение
/dev/urandom - гигабайты информации.

OS: openSUSE 13.2 (x86_64)
Контактная информация:
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт май 05, 2011 19:53:40
Откуда: Харьков

Сообщение Rondo »

Теперь Шпротеус вообще ничем не мигает, и не могу открыть пошаговую отладку....
И кстати переменная return 0; как пишет "не может быть ретурнед ибо функция воид

и еще ... как обнулять правильно переменные ? присваивать им значение ноль n=0 или приравнивать их нулю n==0 ???
Контактная информация:
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 335
Зарегистрирован: Чт май 21, 2009 13:54:07
Откуда: Москва

Сообщение romazan »

присваивание просто =, а проверка на равенство ==
Контактная информация:
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 2029
Зарегистрирован: Сб ноя 15, 2008 10:09:56
Откуда: г. Тула

Сообщение IfoR »

Посмотрите внимательнее на объявление функции:

Код: Выделить всё

int main(void)
Функцию нужно объявить типа int.

На счёт операторов:
= - это оператор присваивания. Он заносит правый операнд в левый.
== - Это оператор сравнения. Логическое значение: равно (антоним: != - не равно). Данный оператор сравнивает два операнда и возвращает значение типа bool - ложь или истина. С операндами он ничего не делает.

Итого выражение n==0; не имеет особого смысла: здесь сравниваются n и 0, а возвращаемое значение... нигде не используется! Это всё рано, что записать просто true; или false;
Ну а если записать n=0; то тогда в n запишется значение 0, т.е. обнулится.

Странно, что я вам рассказываю об основах основ C. :wink:

На счёт того, что не мигает, попробуй пока закомментировать строку со sleep-ом.
Изображение
/dev/urandom - гигабайты информации.

OS: openSUSE 13.2 (x86_64)
Контактная информация:
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт май 05, 2011 19:53:40
Откуда: Харьков

Сообщение Rondo »

по поводу функции main как int выдает ошибку =\
и я окончательно запутался =\
мейн оно вообще не хочет видить ничего кроме войд ... почемуто
может дело в компиляторе и мы говорим о разном ? оО

А про книги и синтаксис, уже перечитал 2 книги по CodeVisionAVR и програмирование на С много понятного, но и есть кое что не понятное...
поэтому как я понял лучше на практике поломать голову 1 раз и запомнить навсегда вот и начал быдлокодить)

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

вот собственно поэтому я и тут :(
Контактная информация:
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 2029
Зарегистрирован: Сб ноя 15, 2008 10:09:56
Откуда: г. Тула

Сообщение IfoR »

Хех, так ладно. Начнём с начала. :) Покажите ещё раз весь текущий код.
Изображение
/dev/urandom - гигабайты информации.

OS: openSUSE 13.2 (x86_64)
Контактная информация:
Опытный кот
Аватара пользователя
Сообщения: 848
Зарегистрирован: Ср мар 02, 2011 07:47:39
Откуда: Уфа

Сообщение Psych »

да пусть будет -- void main(void) и return тоже к едрене фене...и протэус тоже туда же :))
Опытный кот
Аватара пользователя
Сообщения: 703
Зарегистрирован: Вс янв 18, 2009 21:12:49

Сообщение asteroid7 »

IfoR писал(а):asteroid7, зачем? Тогда у него этот дефолт только и будет выполняться...
Да. Точно. Это моя ошибка.
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт май 05, 2011 19:53:40
Откуда: Харьков

Сообщение Rondo »

Вот код который удалось налепить за эти долгие дни советов форумов и какоето читание книг)

Код: Выделить всё

#include <mega16.h>
unsigned int n;
unsigned int sec;
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
TCNT0=0x06;
n++;
    if(n==4)
    {
    sec++;
    n=0;
    }
}
void main(void)
{
DDRB=0xFF;
PORTB=0x00;
TCCR0=0x05;
TCNT0=0x06;
OCR0=0x00;
TIMSK=0x01;
MCUCR = 1<<6;
#asm("sei")

while (1)
    {
      #asm("sleep")
      switch (sec)
      {
        case 29: PORTB=00011110;
        break;                     //зажигаем красный
        case 30: PORTB=00011101;
        break;   //зажигаем желтый
        case 35: PORTB=00011101;
        break;   //тушим красный 
        case 40: PORTB=00011011;
        break;   //тушим желтый, зажигаем зеленый
        case 70: PORTB=00011111;
        break;   //тушим зеленый
        case 75: PORTB=00011011;
        break;   //зажигаем зеленый
        case 80: PORTB=00011111;
        break;   //тушим зеленый
        case 85: PORTB=00011011;
        break;   //зажигаем зеленый
        case 90: PORTB=00011101;
        break;   //тушим зеленый, зажигаем желтый
        case 95: PORTB=00011110;
        break;   //зажигаем красный
        }
        sec=0;
     }  
     
} 
Но сразу мазолит глаз ваш break, так как у нас к примеру бреак стои

case 29: PORTB=00011110;
break; //зажигаем красный

то наш свич сразу заканчивается и переменная sec обнуляется
Тоесть далее 1го кейса дело не пойдет. поэтому как я писал раннее я думаю стоит без брейка писать цикл

И резко удар в спину от таймера .... сегодня в свободное время на работе пролистывал в голове код и понял, что таймер у нас 8ми битный и переполнения соответственно происходят по достижению 255
а настроил я его на частоту 7.9 кГц то есть для формирования секунды надо далеко не 4 переполнения)
И тутже вопрос ... как настроить Т/С0 на такт 1 сек, или не парить моск и заюзать 16ти битный таймер ???

Если есть какието варианты и решения - пожалуйста пишите ВСЕ ! Мне это поможет :)

Зараннее спс
Контактная информация:
Опытный кот
Аватара пользователя
Сообщения: 848
Зарегистрирован: Ср мар 02, 2011 07:47:39
Откуда: Уфа

Сообщение Psych »

уберите sec=0;

В прерывании по таймеру поставьте if (sec>=100)sec=0;

Break - всего лишь даёт "ускорение" оператора switch. Как только совпадет sec с каким то case остальные case проверяться не будут.
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 335
Зарегистрирован: Чт май 21, 2009 13:54:07
Откуда: Москва

Сообщение romazan »

опередил :(
Контактная информация:
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 2029
Зарегистрирован: Сб ноя 15, 2008 10:09:56
Откуда: г. Тула

Сообщение IfoR »

Rondo писал(а): case 29: PORTB=00011110;
break; //зажигаем красный
Ну всё правильно. После свитча у вас написано вот что: Что явно не к месту.
Наверное, нужно его засунуть суда:

Код: Выделить всё

witch (sec)
      {
        case 29: PORTB=00011110;
        break;                     //зажигаем красный
        
        ...

        case 95: PORTB=00011110;
        sec=0;  // Последний этап. Возвращаемся на начало.
        break;   //зажигаем красный
      }
Т.е. sec так будет обновляться только тогда, когда выполнится последняя 95 секунда.
По поводу таймеров. Вот тут дальше идёт занимательная арифметика. Мне уже пора статьи писать на эту тему. :roll:
Выводить формулу расчета не буду - надоело, т.ч. вот для 8-битного таймера с использование регистра:
t = (256-T0) * P/F * R (1)
здесь: t = время выполнения; T0 - начальное значение счетчика таймера; P - предделитель таймера; F - частота ядра; R - число повторений (значение в регистре)
Т.к. нам нужна 1 секунда, частота 8МГц, а предделитель равен 1024, то получаем следующее:
(256-T0)/7812.5 * R = 1 (2)
Ну а теперь надо зафиксировать какую либо переменную и найти другую. Наша цель: найти такие целые положительные числа (либо близкие к целым) не превышающие 255, чтобы уравнение выполнялось. Предлагаю зафиксировать R, тогда:
T0 = 256 - 7812.5/R
Здесь если брать R целое, то T0 никогда целым не будет, т.е. при таких предделитель и частота невозможно точно выдержать задержку. Найдём значения R, при которых условие выполняется.
Условие: T0 находится в отрезке [0;255], тогда:
0 <= 256 - 7812.5/R <= 255
или после преобразований и учитывая, что R - целое и не больше 255:
31 <= R <= 255
Проверим значение 31: T0 = 3.983 ~= 4
Вот и нашли довольно близкое решение при данных условиях: R=31; T0 = 4. Из формулы (1) получим, что при таких условиях задержка равна 0,999936 с
Точное решение можно получить, если взять другой предделитель: 256. По формуле (1) при таких условия максимальная задержка возможна в 2.1 секунды, что больше 1 секунды.
При новых условиях формула (2) примет следующий вид:
(256-T0)/31250 * R =1
Зафиксировав R получим:
T0 = 256 - 31250/R
Как видно, теперь есть вероятность решения с целыми числами. Определим диапазон R:
123 <= R <= 255
Возьмём отсюда наименьший R, который делится на 31250: 125, тогда T0 = 6
Итого, при предделителе в 256 есть точное решение: R = 125, T0 = 6. Подставив в (1) получим t = 1 c.

Далее.
Если использовать просто 16-битный таймер, то формула (1) принимает следующий вид:
t = (65536-T0) * P/F (3)
Здесь неизвестна только одна переменная, которую и выразим:
T0 = 65536 - F/P (4)
Если предделитель равен 1024, тогда T0 = 57723.5 ~=57724. Подставив в (3) получим задержку в 0,999936 с.
Если предделитель равен 256, тогда получим точный результат: T0 = 34286. Подставив в (3) получим задержку в 1 с.

Ну вот. Проще тут, конечно, использовать 16-битный таймер, но и на 8-битном можно вполне заделать.
Изображение
/dev/urandom - гигабайты информации.

OS: openSUSE 13.2 (x86_64)
Контактная информация:
Закрыто

Вернуться в «Микроконтроллеры и ПЛИС»