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

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

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

Сообщение 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 возможно напорол чтото с таймером оО


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

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

Сообщение IfoR »

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

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

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

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

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

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

Сообщение Rondo »

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

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

Сообщение romazan »

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

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

Сообщение romazan »

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

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

Сообщение asteroid7 »

volatile unsigned int sec;

...

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

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

Сообщение 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)
Rondo
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт май 05, 2011 19:53:40
Откуда: Харьков
Контактная информация:

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

Сообщение Rondo »

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

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

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

Сообщение IfoR »

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

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

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

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

Сообщение Rondo »

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

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

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

Сообщение romazan »

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

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

Сообщение 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)
Rondo
Открыл глаза
Сообщения: 42
Зарегистрирован: Чт май 05, 2011 19:53:40
Откуда: Харьков
Контактная информация:

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

Сообщение Rondo »

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

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

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

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

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

Сообщение IfoR »

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

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

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

Сообщение Psych »

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

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

Сообщение asteroid7 »

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

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

Сообщение 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ти битный таймер ???

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

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

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

Сообщение Psych »

уберите sec=0;

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

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

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

Сообщение romazan »

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

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

Сообщение 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)
Закрыто

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