diff options
Diffstat (limited to 'RADIO')
| -rw-r--r-- | RADIO/ds18b20.md | 493 | ||||
| -rw-r--r-- | RADIO/РЕЛЕ понижение потребления.md | 16 |
2 files changed, 509 insertions, 0 deletions
diff --git a/RADIO/ds18b20.md b/RADIO/ds18b20.md new file mode 100644 index 0000000..199bfd3 --- /dev/null +++ b/RADIO/ds18b20.md @@ -0,0 +1,493 @@ +# Подключаем датчик температуры DS18B20 + +Продолжаем изучать датчики компании **DALLAS**. + +И сегодняшним представителем будет **DS18B20**, который является датчиком температуры. + +Измеряет температуру данный датчик в градусах по цельсию. Измерения могут быть как 9-битной дискретности, так и 12-битной. + +Распределяются данные биты следующим образом. + +Мы будем измерять температуру в 12-битном виде и самые младшие 4 бита будут хранить показания долей градуса, а остальные старшие 8 — сами градусы. + +Подключается данный датчик по однопроводной технологии (1-wire). + +Диапазон измерений — от -55 0C до +125 0C, но наивысшая точность показаний достигается в диапазоне от -10 0C до +85 0C/ + +Погрешность данного датчика — 0,5 градуса, поэтому нам нет смысла использовать все младшие биты и мы их будем просто отсекать. + +Режим 1-wire у нас не организован аппаратно в контроллере, поэтому мы будем весь протокол программировать. + +Чем удобен для нас данный датчик? Удобен он тем, что мы можем его в часовой модуль, использованный на прошлых занятиях просто впаять и использовать. Для этого на модуле выведен отдельный контакт. У датчика всего 3 ножки. 2 из них ножки питания и одна — ножка данных. + +Данный датчик существует в двух видах корпуса — Dip и TO-92. Мы будем использовать второй тип. + +Таких датчиков, как пишут, на 1 провод можно повесить несколько, но я не пробовал. + +В связи с этим у каждого датчика есть уникальный 64-битный код, записанный в его ROM, записанного постредством лазерных технологий. + +Но, так как мы будем использовать только один датчик, то мы будем к нему обращаться другим способом, не используя ROM. + +Мы подаём на датчик команду 44h последовательным кодом, тем самым заставляя датчик конвертировать температуру. + +Посмотрим регистры, в которых хранится значение температуры после преобразования + + + +В четырех младших битах младшего регистра хранятся доли градусов, в четырех старших, а также в трех младших битах старшего регистра — целые значения градусов. + +В остальных битах — знак. Если 0, то плюс, если 1 — то минус. + +Также в технической документации написано, что нужно обязательно подтянуть резистор на информационную ножку датчика к питанию. + +Также написан ряд временных и других характеристик, которые мы уже будем рассматривать по мере того, как будем писать код. + +Проект был создан новый и назван **My1820LCD**, все файлы проекта [**прошлого занятия**](https://narodstream.ru/avr-urok-19-sobiraem-chasy-na-mikrosxeme-ds1307-i-lcd-1602/) **MyClock1307LCD** были вставлены в проект, а код главного модуля также был скопирован в главный модуль нового проекта. Всё это сделано потому, что мы работаем с тем же модулем, просто мы также впаяли туда датчик температуры, а время мы считывать не перестанем, пусть часы также ходят. + +Добавим ещё файлы **DS18B20.c** и **DS18B20.h** для созадния библиотеки работы именно с датчиком температуры и заголовочный файл также подключим в main.h по нашему обыкновению, ну и его же подключим в одноименный c-файл. + +Сначала создадим функцию конвертирования температуры в файле DS18B20.c. Надобность данной функции обусловлена тем, что датчик нам передаёт показания в двух байтах в определённом виде, рассмотренном нами выше, который без преобразования будет непонятен + +//функция преобразования показаний датчика в температуру + +**int dt_check(void)** + +**{** + +**}** + +Создадим две переменных, которые нам пригодятся в дальнейшем в теле нашей функции + +int dt_check(void) + +{ + + **unsigned char bt;//переменная для считывания байта** + + **unsigned int tt=0;** + +Сразу сделаем прототип нашей функции в заголовочном файле + +#include «main.h» + +**int dt_check(void); //функция преобразования показаний датчика в температуру** + +Чтобы нам дальше писать тело функции, нам будет нужна ещё функция. Напишем её выше, чтобы не создавать прототип. Данная функция будет узнавать, есть ли датчик на шине + +//функция определения датчика на шине + +**char dt_testdevice(void) //dt — digital termomether | определим, есть ли устройство на шине** + +**{** + +**}** + +Теперь, прежде чем писать тело данной функции, самое время посмотреть схему подключения датчика к контроллера. Посмотрим опять же упрощённый вариант в протеусе (нажмите на картинку для увеличения изображения) + +[](https://narodstream.ru///wp-content/uploads/2016/12/Image02-11.png) + +Мы видим, что датчик своим информационным портом подключен к ножке порта МК PD1, также мы видим подтягивающее сопротивление на 4,7 килоом с данного провода на шину питания. + +И вот теперь на данной ножке мы должны как-то иммитировать определённые имплуьсы. То есть мы как-то должны инициализировать то ноль, то единицу. Делать мы это сможем благодаря тому, что у нас есть подтягивающий резистор. Мы будем её то отпускать от общей шины, то притягивать к ней. Соответственно, когда мы её отпустим, то через резистор на ней окажется высокий уровень, а когда подтянем к земле, то низкий. + +Теперь вопрос, как это всё сделать. А сделать это можно вполне с помощью определённого бита регистра DDRB, который отвечает, как мы знаем, за направление работы определённых ножек порта. Но мы воспользуемся тем, что когда мы объявляем ножку на выход, она у нас притягивается к земле, понятно что при условии, что соответствующий бит регистра PORTD будет также нулём. А если мы уже в соответствующий бит регистра DDRB записываем значение, при котором соответствующая этому биту ножка объявится на вход, то, соответственно, от общей шины она. ясное дело отпустится, иначе как контроллер определит её состояние. Ну и, как и было написано выше, на этой ножке засчет подтягивающего резистора установится высокий логический уровень. + +Также, начиная процесс обмена с датчиком, мы должны запретить прерывания, чтобы не получить искаженных данных. Также, прежде чем запретить прерывания, нам необходимо сохранить регистр статуса контроллера, или стек. Это регистр SREG. Поэтому в самом начале тела нашей функции проверки присутствии датчика на шине мы это и сделаем, сохранив его в определённую переменную, ну и затем уже запретим прерывания + +char dt_testdevice(void) //dt — digital termomether | определим, есть ли устройство на шине + +{ + + **char stektemp=SREG;// сохраним значение стека** + + **cli(); //запрещаем прерывания** + +Добавим ещё одну переменную в теле данной функции для возврата результата функции + +cli(); //запрещаем прерывание + +**char dt;** + +Теперь для удобства работы с ножками порта давайте напишем некоторые дефайновые замены в заголовочном файле DS18B20.h + +#include «main.h» + +**#define PORTTEMP PORTD** + +**#define DDRTEMP DDRD** + +**#define PINTEMP PIND** + +**#define BITTEMP 1** + +Также здесь давайте сделаем ещё некоторые макроподставновки + +#include «main.h» + +**#define NOID 0xCC //Пропустить идентификацию** + +**#define T_CONVERT 0x44 //Код измерения температуры** + +**#define READ_DATA 0xBE //Передача байтов ведущему** + +Здесь мы присваиваем удобные имена командам датчикам. + +Ну давайте их посмотрим. + +Первая команда **0xCC** + +Посмотрим теперь все тайминги инициализации датчика + + + +Здесь видно что мы здесь притягиваем шину, держим её в таком состоянии как минимум 480 микросекунд, затем отпускаем её, ждём максимум 60 мкс (мы подождём побольше — 65 мкс) ответа датчика, датчик должен нам ответить таким же притягиванием шины к земле за данное время. Если обнаружится ноль, то значит устройство на шине есть, а если шина так и останется висеть в воздухе, то данного датчика на шине нет. + +Ну. и на остове вышепрочитанного продолжим нашу функцию определения наличия датчика на шине + +char dt; + +**DDRTEMP |= 1<<BITTEMP; //притягиваем шину** + +**_delay_us(485); //задержка как минимум на 480 микросекунд** + +**DDRTEMP &= ~(1<<BITTEMP); //отпускаем шину** + +**_delay_us(65); //задержка как минимум на 60 микросекунд** + +**if ((PINTEMP & (1<<BITTEMP))==0)//проверяем, ответит ли устройство** + +**{** + + **dt=1;//устройство есть** + +**}** + +**else dt=0;//устройства нет** + +В условии у нас идёт проверка, опустилась ли шина. + +Затем мы стеку вернём его первоначальное состояние + +else dt=0;//устройства нет + +**SREG = stektemp;// вернем значение стека** + +Отдельной командой после этого разрешать прерывания не требуется, так как все эти команды как раз и управляют определёнными битами регистра **SREG**. Если прерывания были разрешены до сохранения стека, то они, ясное дело, опять разрешатся, так как все биты регистра мы возвращаем в прежнее положение. + +Дальше. судя по диаграмме, которая у нас находится выше, мы должны выдержать время максимум 480 микросекунд, но так как после последней задержки у нас уже прошло некоторое количество времени, то хватит и 420, тем более задержка эта не фиксированная, а максимальная. Ну, и добавим эту задержку в тело функции далее, и в оконцовке вернём результат функции. + + **SREG = stektemp;// вернем значение стека** + + **_delay_us(420); //задержка как минимум на 480 микросекунд, но хватит и 420, тк это с учетом времени прошедших команд** + + **return dt; //вернем результат** + +} + +Теперь по окончании написания кода данной функции, мы можем продолжить начатую в предыдущей части урока функцию преобразования. Но, всё-таки, чтобы её продолжить, нам нужна будет ещё одна немаловажная, а может и вообще самая важная функция в нашем проекте — функция считывания байта с датчика температуры. Создадим данную функцию, а также в её теле сразу объявим две локальные переменные + +**//функция чтения байта с устройства** + +**unsigned char dt_readbyte(void)** + +**{** + + **char c=0;** + + **char i;** + +**}** + +Прежде чем писать дальше тело данной функции, мы должны вспомнить что каждый байт у нас состоит из восьми бит, поэтому целесообразно для того, чтобы не нагромождать много кода в одну функцию, создать ещё одну выше для чтения отдельного бита с датчика температуры. Создадим пока каркас данной функции с комментарием, объясняющем назначение этой функции + +**//функция чтения бита с устройства** + +**char dt_readbit(void)** + +**{** + +**}** + +В данной функции мы также сохраним регистр статусов микроконтроллера, запретим прерывания и создадим переменную для дальнейшего возврата результата. Результат у нас будет в виде обычной знаковой 8-битной величины, по большому счёту мы в ней будем использовать только младший бит + +//функция чтения бита с устройства + +char dt_readbit(void) + +{ + + **char stektemp=SREG;// сохраним значение стека** + + **cli(); //запрещаем прерывание** + + **char bt; //переменная хранения бита** + +Для написания дальнейшего кода посмотрим временную диаграмму, как вообще читать бит с данного датчика. Данная диаграмма в технической документации универсальна и для чтения и для записи бита в устройство + + + +Мы здесь видим, что после притягивания шины к земле мы ждём минимум одну микросекунду, ну, подождём две микросекунды на всякий случай, перед этим притянув шину к земле + +char bt; //переменная хранения бита + +**DDRTEMP |= 1<<BITTEMP; //притягиваем шину** + +**_delay_us(2); //задержка как минимум на 2 микросекунды** + +Смотрим дальше. Мы должны шину отпустить от земли, а затем задержка для считывания результата. Здесь стоит общая задержка 15 микросекунд, так как мы уже две подождали, осталось нам 13 + +_delay_us(2); //задержка как минимум на 2 микросекунды + +**DDRTEMP &= ~(1<<BITTEMP); //отпускаем шину** + +**_delay_us(13);** + +После данной задержки результат уже на шине должен быть. Считаем его аналогично, как в предыдущей функции мы узнавали присутствие усройства на шине + +_delay_us(13); + +**bt = (PINTEMP & (1<<BITTEMP))>>BITTEMP; //читаем бит** + +До считывания следующего байта, судя по диаграмме мы ждём 45 микросекунд, затем возвращаем значение стека в стек и возвращаем результат функции считывания бита на шине + + **bt = (PINTEMP & (1<<BITTEMP))>>BITTEMP; //читаем бит** + + **_delay_us(45);** + + **SREG = stektemp;// вернем значение стека** + + **return bt; //вернем результат** + +} + +Теперь, используя данную функцию, допишем тело функции считывания с датчика целого байта, последовательно занося байты в переменную **c**, применяя битовый сдвиг, а затем ещё же и возвращая. + + **char i;** + + **for(i=0;i<8;i++)** + + **c|=dt_readbit()<<i; //читаем бит** + + **return c;** + +} + +Ну вот. Самая, можно сказать, важная функция написана. + +Продолжим функцию конвертирования считанных байтов + + **unsigned int tt=0;** + + **if(dt_testdevice()==1) //если устройство нашлось** + + **{** + + **}** + + **return tt;** + +} + +В теле условия по определению присутствия датчика на шине, прежде чем что-то считать с нашего датчика, мы должны ему будем передать команды. А чтобы их передать, мы должны будем написать также функцию отправки байта в устройство. + +давайте напишем функцию передачи байта в датчик, в её теле мы созадим локальную переменную для дальнейшего цикла передачи каждого байта + +**//функция записи байта на устройство** + +**void dt_sendbyte(unsigned char bt)** + +**{** + + **char i;** + +**}** + +Ну и, соответственно, чтобы нам передать байт, нам нужно сначала научиться передавать бит, так как без бита не бывает байта. Для этого выше создадим другую функцию, написав её теле пока стандартный код сохранения стека и запрета прерываний + +**//функция записи бита на устройство** + +**void dt_sendbit(char bt)** + +**{** + + **char stektemp=SREG;// сохраним значение стека** + + **cli(); //запрещаем прерывание** + +**}** + +Затем смотрим в диаграмму записи бита в устройство, которую мы смотрели в даташите датчика в предыдущей части. В ней видно, что мы должны также притянуть шину к земле, подождать минимум 1 микросекунду, мы опять же подождём две. Это нужно для того, чтобы датчик «успел понять» что шина притянулась. + +cli(); //запрещаем прерывание + +**DDRTEMP |= 1<<BITTEMP; //притягиваем шину** + +**_delay_us(2); //задержка как минимум на 2 микросекунды** + +Дальше мы уже будем решать, что именно мы будем посылать. если будем посылать 0, то мы шину отпускать не будем, а, если нам нужно послать 1, то будем шину отпускать + +_delay_us(2); //задержка как минимум на 2 микросекунды + +**if(bt)** + + **DDRTEMP &= ~(1<<BITTEMP); //отпускаем шину** + +Дальше мы ждём от 60 до 120 микросекунд, ну, подождём 65, как обычно. То есть если мы предаём ноль, то мы просто выжидаем с притянутой шиной данный временной интервал, а если передаём единицу, то делаем то же самое, но отпустив перед этим шину, так как сработает условие в коде. Затем мы отпускаем шину. То есть, если мы передаём единицу, то она уже у нас отпущена, если ноль, то отпускаем. ну и затем возвращаем сохраненное значение в стек + + if(bt) + + DDRTEMP &= ~(1<<BITTEMP); //отпускаем шину + + **_delay_us(65); //задержка как минимум на 60 микросекунд** + + **DDRTEMP &= ~(1<<BITTEMP); //отпускаем шину** + + **SREG = stektemp;// вернем значение стека** + +} + +Теперь продолжим, используя данную функцию, функцию записи (или отправки) байта в шину + + char i; + + **for(i=0;i<8;i++)//посылаем отдельно каждый бит на устройство** + + **{** + + **if((bt & (1<<i)) == 1<<i)//посылаем 1** + + **dt_sendbit(1);** + + **else //посылаем 0** + + **dt_sendbit(0);** + + **}** + +} + +Здесь мы также используем цикл и в цикле мы, используя также битовый сдвиг, постепенно по одному биту считываем посланный в функцию в качестве входного аргумента байт и посылаем каждый считанный бит в функцию передачи бита в виде переменной, равной 0 или 1. + +Теперь, написав все эти рутинные функции, мы смело можем продолжить так и не дописанную в предыдущих частях функцию конвертирования показания температуры. + +Как мы помним, мы написали в данной функции код условия наличия датчика на шине, и теперь можем писать тело данного условия. Начнём с передачи команды разрешения на то, чтобы нам не считывать идентификатор датчика + +if(dt_testdevice()==1) //если устройство нашлось + +{ + + **dt_sendbyte(NOID); //пропустить идентификацию, тк у нас только одно устройство на шине** + +Затем мы уже отправляем команду на считывание температуры + +dt_sendbyte(NOID); //пропустить идентификацию, тк у нас только одно устройство на шине + +**dt_sendbyte(T_CONVERT); //измеряем температуру** + +Данная команда — это только команда, а не само считывание температуры. По данной команде датчик начнёт измерение температуры и конвертацию её в цифровой код и занесение его в регистровую пару. Данный процесс у датчика длится значительно долго, около 750 милисекунд, не микро. Данное время необходимо, чтобы датчик успел записать все 12 бит + + + +Здесь мы видим, что если нам особая точность не нужна, то мы можем считать другое количество бит, тем самым сэкономив значительное количество времени. Но мы напишем идеально 750, так как мы никуда не спешим + +dt_sendbyte(T_CONVERT); //измеряем температуру + +**_delay_ms(750); //в 12битном режиме преобразования — 750 милисекунд** + +Далее мы опять проделываем те же манипуляции с шиной. как и при проверке наличия устройства на ней, и опять посылаем команду работы без идентификации + +_delay_ms(750); //в 12битном режиме преобразования — 750 милисекунд + +**dt_testdevice(); //снова используем те же манипуляции с шиной что и при проверке ее присутствия** + +**dt_sendbyte(NOID); //пропустить идентификацию, тк у нас только одно устройство на шине** + +И затем мы уже даем команду датчику считывать байты с регистра. Судя по временным диаграммам, они там уже должны быть + +dt_sendbyte(NOID); //пропустить идентификацию, тк у нас только одно устройство на шине + +**dt_sendbyte(READ_DATA); //даем команду на чтение данных с устройства** + +Затем мы считываем байты с регистра и укладываем их в переменную tt, потом, выйдя из тела условия, возвращаем их. + + dt_sendbyte(READ_DATA); //даем команду на чтение данных с устройства bt = dt_readbyte(); //читаем младший бит + + **tt = dt_readbyte(); //читаем старший бит MS** + + **tt = (tt<<8)|bt;//сдвигаем старший влево, младший пишем на его место, тем самым получаем общий результат** + + **}** + + **return tt;** + +**}** + +Мы получили хоть и не раздельный результат в виде двух различных байтов, но все равно результат этот сырой, так как в нём есть и биты знака, и также шестнадцатые доли градуса, которые мы не можем показать на дисплее. Поэтому нужна будет ещё одна функция конвертирования результата в удобный для глаза вид. + +Мы вообще упростим нашу задачу и не будем использовать доли, а будем использовать только целые показания, и бит отрицания мы также не будем использовать, мы будем измерять только положительные температуры. Вот такая поэтому будет простая у нас функция + +**//преобразование температуры в единицы** + +**char converttemp (unsigned int tt)** + +**{** + + **char t = tt>>4;//сдвиг и отсечение части старшего байта** + + **return t;** + +**}** + +Напишем на данную функцию прототип в хедер-файле + +int dt_check(void); //функция преобразования показаний датчика в температуру + +**char converttemp (unsigned int tt); //преобразование температуры в единицы** + +Теперь мы, наконец-то вернёмся в наш главный модуль в его главную функцию **main()**, а то мы уже тут очень давно не были. Создадим здесь локальную переменную для хранения показаний температуры + +int main(void) + +{ + + **unsigned int tt=0; //переменная для хранения температуры** + +Напишем код для вызова функции измерения температуры в бесконечном цикле + +sendcharlcd(sec%10+0x30);//Преобразуем число в код числа + +sendcharlcd(' '); + +**tt = converttemp(dt_check()); //измеряем температуру** + +Отобразим показания температуры на дисплее + + tt = converttemp(dt_check()); //измеряем температуру + + **sendcharlcd(tt/10+0x30);//Преобразуем число в код числа** + + **sendcharlcd(tt%10+0x30);//Преобразуем число в код числа** + + **sendcharlcd('*');** + + **sendcharlcd('C');** + +} + +Прежде чем собирать код и прошивать контроллер, посмотрим подключение нашей всей схемы (нажмите на картинку для увеличения изображения) + +[](https://narodstream.ru///wp-content/uploads/2016/12/Image10-10.png) + +Посмотрим также поближе модуль с припаянным датчиком + + + +Вот так выглядит модуль с другой стороны, желтым прямоугольником отмечен контакт данных термодатчика + + + +Соберём код и прошьём контроллер. А вот и результат + + + +Так как при измерении температуры у нас происходит очень большая задержка, а мы всё равно отсекаем считанные биты, впоследствии мы. когда будем писать часы с термометром, то мы данную задержку значительно убавим.
\ No newline at end of file diff --git a/RADIO/РЕЛЕ понижение потребления.md b/RADIO/РЕЛЕ понижение потребления.md new file mode 100644 index 0000000..265dc49 --- /dev/null +++ b/RADIO/РЕЛЕ понижение потребления.md @@ -0,0 +1,16 @@ +| | | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [](https://www.rlocman.ru/shem/pdf.html?di=175183) | | +| _Рисунок 1._ | **_RC-цепочка, включенная последовательно с обмоткой, обеспечивает надежное <br>включение реле полным током, а затем снижает ток для экономии энергии._** | + +Хорошо известно, что возбужденная катушка реле будет поддерживать замкнутое состояние контактов даже после некоторого снижения управляющего напряжения. В течение многих лет мы использовали очень простой подход к снижению управляющего напряжения реле, не требующий ни неиспользуемых контактов, ни сложных схем. Все, что нужно сделать, это добавить два простых компонента – включенные параллельно резистор и конденсатор. + +Пара резистор/конденсатор разрывает обычное соединение с «землей» эмиттерного вывода управляющего транзистора (Рисунок 1). Когда управляющий транзистор выключен, резистор R2 гарантирует, что конденсатор разряжен. Когда внешняя команда включает управляющий транзистор, этот незаряженный электролитический конденсатор в течение некоторого времени представляет собой короткое замыкание, обеспечивая протекание максимального тока через обмотку реле и бездребезговое замыкание его контактов. + +Однако по мере того, как конденсатор заряжается, напряжение на обмотке и ток через нее уменьшаются. Схема достигает установившегося состояния, когда конденсатор заряжается до такой степени, что весь ток обмотки реле проходит через резистор R2. Контакты реле будут оставаться замкнутыми до тех пор, пока не будет снято внешнее управляющее напряжение. + +В этом примере сопротивление резистора примерно в два раза превышает сопротивление обмотки реле: 390 Ом для сопротивления обмотки 200 Ом. Выбранный конденсатор 150 мкФ/25 В образует комбинацию, которая хорошо работает при номинальном напряжении обмотки 12 В. Если бы эмиттер управляющего транзистора был просто подключен к «земле», цепь реле потребляла бы около 58 мА. С добавлением резистора/конденсатора установившийся ток составляет всего 20 мА, что дает хорошую экономию энергии. Дополнительные контакты реле не требуются, а два добавочных компонента стоят всего несколько центов. + +![[Снимок экрана 2025-05-14 в 00.35.18.png]] + +![[Снимок экрана 2025-05-14 в 00.38.05.png]]
\ No newline at end of file |
