Раньше это было о звук И свет .
Наконец-то отступать больше некуда и пришло время собрать машину в нечто целое и практически неделимое.
Остался один крошечный вопрос: как эта машина будет включаться и выключаться? А как насчет традиции продолжить тему предыдущего в следующем посте?
Чтобы не нарушать традицию, продолжу тему из предыдущего поста: моргание светодиодами.
Что с ними не так? В них много чего не так, от кривой времени задержки до ужасного обработчика прерываний по таймеру.
Сначала нам нужно сделать нормальную функцию задержки.
Установите TMR1 на период 1 мс и добавьте его в код.
Таким простым способом мы получим функцию задержки, не зависящую от частоты/наличия другого кода (главное, чтобы таймер тикал) до 65 секунд. Для наших целей этого будет достаточно с большим запасом.volatile uint16_t millis=0; void DelayMS(uint16_t m) { millis=0; while(millis<m); } .
void TMR1_ISR(void) { millis++; .
Теперь посмотрим на светодиодный код зажигания.
Как-то некрасиво делать задержки в прерывании (да ещё и в цикле), плюс кучу сравнений, которые по-хорошему надо унести куда-то подальше.
Нет, для домашнего проекта это нормально, но для промышленного проекта не годится.
Давайте еще раз изменим алгоритм.
В чем смысл? Позвольте мне рассмотреть пример порта C, который полностью предназначен для светодиодов.
Младшие 3 бита отвечают за цвета.
Старший 3 – для первых 3 светодиодов.
Мысленно включим первый светодиод на красный, второй на зеленый, а третий на «оранжевый» (или красный+зеленый).
Что будет в порту? Первый шаг, «красный» канал 0b00101001 Второй шаг, зеленый канал 0b00110010 Третий шаг, синий канал 0b00000100 Как мы видим, нам в принципе достаточно раз за разом отправлять в порт один байт, чтобы получить один и тот же функционал.
А формирование этих байтов мы отдадим функции setLed — она вызывается гораздо реже кода в прерывании и может позволить себе всякие вычисления, циклы и прочие излишества.
Хорошо, что мы знаем, что в порт C можно засунуть что угодно — его 2 старших бита никуда не подключены (судя по даташиту и коду).
Но что делать с портом А, где нам нужно изменить только один бит, не трогая остальные? Для этого можно использовать замечательные функции «бит И» и «бит ИЛИ».
Чтобы было понятнее, опишу это на примере: 0b00 0 01010 Чтение текущего значения из порта.
Я выделил тот бит, который нам нужен — нам нужно лишь изменить его значение.
Остальные были размещены случайным образом.
Делаем «бит ИЛИ» (где-то должна быть 1, чтобы результат тоже был 1) с заранее подготовленной маской функцией setLed. Чтобы включить бит, он должен быть 0b00. 1 00000. Допустим, нам нужно включить бит 0b00 0 01010 | 0b00 1 0000 = 0b00 1 01010 Всё, нужный бит установлен, остальные не трогаем, значит полученное значение можно записать обратно в порт. А теперь сбросим тот же бит с помощью «бита И» (оба должны быть 1, чтобы на выходе тоже была 1) 0b00101010 и 0b11011111 = 0b00 0 01010 Все кажется логичным.
Давайте перепишем код.
В общем, перед функцией setLed стоит задача подготовить 6 байт того, что мы отправим в порт. Измените файлы LED.c и LED.h. // basic colors
#define RED 1
#define GREEN 2
#define BLUE 3
#define OFF 0
extern volatile uint8_t lc[3];
extern volatile uint8_t la[3];
void setLed(uint8_t n,uint8_t c)
{
uint8_t b;
if(n==0) b=0b11110111;
if(n==1) b=0b11101111;
if(n==2) b=0b11011111;
if(c>0)
{
c--;
if(n==3)
{
la[0]=1;
la[1]=1;
la[2]=1;
la[c]=0;
}
else
{
lc[c]=lc[c]&b;
}
}
else
{ // turn off channel N
b=b^0xff;
if(n==3)
{
la[0]=1;
la[1]=1;
la[2]=1;
}
else
{
lc[0]=lc[0]|b;
lc[1]=lc[1]|b;
lc[2]=lc[2]|b;
}
}
И в обработчике прерывания пишем LATC=lc[current_led];
_LED4_ = la[current_led];
current_led++;
if(current_led==3) current_led=0;
Согласитесь, намного короче предыдущего варианта (однако работу с битами я оставил для порта С, а порт А оказалось легче тянуть «вручную»).
Если он короче, это означает, что он работает быстрее.
А поскольку он работает быстрее, это означает, что общая производительность контроллера также выше.
Полный бонус во всем.
Правда, чтобы получить «белое», вам теперь придется трижды вызывать setLed, но это мелочи.
Теперь осталось решить самый главный вопрос: как его включить и как выключить? В принципе у меня есть вариант, предложенный первоначальными создателями машины - кнопка на двери.
Но прежде чем заниматься прикладным творчеством, нужно разобраться, «стоит ли оно того»?
Пишем следующий код setLed(3, RED);
setLed(2, BLUE);
PWM3_LoadDutyValue(49*2);
TMR2_LoadPeriodRegister(49);
uint8_t c,q;
for(q=0;q<1;q++) {
setLed(0, RED);
setLed(1, OFF);
DelayMS(100);
for (c = 0; c < 10; c++) {
setLed(0, OFF);
DelayMS(50);
setLed(0, RED);
DelayMS(50);
}
setLed(1, BLUE);
setLed(0, OFF);
DelayMS(100);
for (c = 0; c < 10; c++) {
setLed(1, OFF);
DelayMS(50);
setLed(1, BLUE);
DelayMS(50);
}
}
PWM3_LoadDutyValue(0);
TMR2_LoadPeriodRegister(0);
setLed(0, OFF);
setLed(1, OFF);
setLed(2, OFF);
setLed(3, OFF);
DelayMS(10);
SLEEP();
Обратите внимание на последнюю инструкцию — SLEEP() переводит микроконтроллер в спящий режим, когда все выключено и вывести его из этого состояния можно только каким-то прерыванием.
Перебоев у нас пока не было, поэтому я просто включаю и выключаю его вручную при измерении.
Подключаю мультиметр для измерения тока потребления.
Мой показал, что при горящих светодиодах и ноющем динамике ток потребления составляет 2,97-3мА.
Если не использовать динамик, то 2,85-2,9. А когда все это заканчивается, то 0,01 мА или меньше (просто у моего мультиметра нижний предел измерения 0,01 мА).
Разница между тактовой частотой 1 МГц и 4 МГц также минимальна – буквально 0,2 мА.
Согласитесь, отличные цифры для 12 мигающих светодиодов? Если бы мы подключили их «как обычно», мы бы получили ток 30-40 мА.
Поскольку у меня будут 2 батарейки типа ААА (их средняя емкость 1000 мАч), я легко прикину, сколько времени прослужит машинка.
Если он все время пищит и мигает - 1000мАч/3мА/24ч=13 дней.
А если все время спать - 1000/0,1/24= 416 дней.
Понятно, что в реальности цифры будут ниже, так как ни одна батарея не позволит себя разрядить в ноль.
Но даже в этом случае год в спячке – это очень хорошо для машины.
В общем, отдельный переключатель не нужен и поэтому я решил использовать уже готовую заготовку для кнопки.
Кнопку подключаю между RA4 и минусом, в Конфигураторе кода разрешаем эту ногу для чтения и не забываем включить подтягивающий резистор и генерацию прерываний для этой ноги.
В Код Конфигураторе почему-то забыли добавить код включения прерываний (хотя галочки стоят) и из-за этого приходится открывать даташит на процессор и искать какие биты за что отвечают (на самом деле это очень полезное занятие).
Включаем прерывания от периферийных устройств и позволяем генерировать их тем, кто подключен к порту А4. INTCONbits.IOCIE =1;
IOCANbits.IOCAN4 =1;
И мы проверяем еще раз.
В итоге по нажатию кнопки все просыпается, пару раз моргает и засыпает. У меня уже есть красивый флешер (код выше), и с частотами сирены мне помогли в комментариях.
Сирена скорой помощи: частотно-модулированный треугольный сигнал с изменяющейся во времени частотой генерации от 850 Гц до 1550 Гц.
со скоростью 0,2 герца.
Полицейская сирена от 650 Гц.
до 1350 Гц.
И не спрашивайте меня, как можно измерить скорость в герцах :) Но звук очень похож.
Теперь осталось собрать всё, что наработано (звук, мигания, сон) в один кусок кода.
В лучших традициях обучающих программ я оставлю это сделать вам.
Почти готовый можно взять как обычно, у меня multik.org/pic/policecar.rar Теги: #Сделай сам или Сделай сам #на батарейках #игрушка #PIC16 #PIC16 #mplab
-
Стеллараторы Круче Токамаков
19 Oct, 24 -
Sgvsbg8Gd29Ybgqh Или История Base64
19 Oct, 24 -
Чистые Компакт-Диски
19 Oct, 24 -
Подкаст Unclesoky – Эпизод №13
19 Oct, 24 -
Контроль Товарных Остатков В Системах 1С.
19 Oct, 24