Русский
Русский
English
Статистика
Реклама

Attiny13a

Из песочницы Дистанционное управление громкостью IP TV приставки при помощи Atiny13A

22.10.2020 14:22:35 | Автор: admin

Как-то мне позвонили с Ростелекома и предложили подключить IP TV. Ну что же, решил я, пусть жена с сыном смотрят в спальне мультики и согласился. И вот принесли мне заветную коробочку. Т.к. отдельного телевизора для неё у меня нет, то решил я значит подключить её к старому монитору, через переходник HDMI-VGA. Для звука у меня были старые компьютерные колонки. Решено сделано. Всё прекрасно завелось с одним но: с пульта, который шёл в комплекте с приставкой, невозможно регулировать громкость звука. Как так то? Честно сказать никогда с таким не сталкивался. Особо я в причинах не разбирался, но вроде как пульт от Ростелекома прописывается в телевизоре, так что с пульта меняется громкость на самом телевизоре, а не на выходе из приставки. Удобно? Конечно, если подключить приставку к современному телевизору. А вот вставать с кровати и крутить крутилку на колонках каждый раз, когда нужно поменять громкость неудобно. Решением этого вопроса и займёмся. Соберём отдельное устройство, которое будет регулировать громкость на наших колонках по сигналу с пульта.


Для начала давайте посмотрим, что за сигналы у нас генерирует пульт при нажатии кнопок "громкость вверх", "громкость вниз" и "mute". В качестве приёмника сигналов с пульта я использовал VS1838B.


Это удобный приёмник, т.к. он уже демодулирует 38кГц инфракрасный сигнал от пульта.


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


График сигнала с логического анализатора


Всего пульт генерирует 24 изменения уровня. Длительность короткого импульса (низкого и верхнего уровней) около 900 мкс, а длительность длинного импульса (также как низкого, так и верхнего уровней) около 1800 мкс. Обозначим короткий импульс нулём, а длинный единицей, тогда полученные наблюдения можно свести в таблицу:


Таблица 1. Сигналы нажатий кнопок от пульта ТВ приставки.


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Громкость вверх 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0
Громкость вверх 2 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0
Громкость вниз 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0
Громкость вниз 2 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0
Mute 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0
Mute 2 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0

Теперь нужно выбрать техническую реализацию изменения громкости. Физически громкость в моих колонках меняется при помощи крутилки на корпусе, а крутилка вращает сдвоенный потенциометер на 50К. Т.е. вращением ручки громкости мы как бы одновременно вращаем два переменных резистора, где один отвечает за громкость левого канала, а другой за громкость правого. Для удалённого регулирования громкости нам потребуется "ручной" сдвоенный потенциометр заменить на два электронных. У меня завалялась пара X9C103P. Это электронный потенциометр на 10 К. Я глянул в даташит микросхемы, на которой построен усилитель моих колонок, и оказалось, что 10К для потенциометра для неё тоже нормально. Поэтому будем использовать эти микросхемы.


Принимать сигналы с пульта будет вышеупомянутый ИК приёмник VS1838B. Осталось разобраться с управляющей электроникой. Я быстро набросал скетч на ардуино, который детектировал сигналы с пульта, но потом подумал, что это будет не так интересно. Во первых, готовое устройство получится громадным, а во вторых, захотелось попробовать более интересное программирование. И я выбрал Attiny13A. Преимущества: размер микроконтроллера с четверть ногтя, очень дешёвый. Недостатки: всего 1кб флэш памяти и 64 байта SRAM. Для сравнения в ардуине (Atmega 328) 32кБ флэш и 2кБ SRAM.


Чтобы уместить в очень ограниченную память паттерны, обозначенные в таблице выше, надо эти данные как-то сокращать. Если в прототипе на ардуино я прямо создавал массивы с временами длительности импульса, например:


const unsigned long UP1_DATA[] =   {860, 900, 1750, 900, 860, 900, 860, 900, 860, 900, 860, 900, 860, 900, 860, 1750, 1750, 900, 860, 900, 860, 900, 860};

То, в attiny такой фокус не пройдёт. В идеале нужно уместить паттерн для одного варианта сигнала в один байт. И как видно из таблицы это не такая сложная задача, т.к. в ней в основном нули. А именно: первые два столбца, столбцы с 6-го по 15-ый, 19-ый и 20-ый и последний столбец нули. Не будем их записывать в память микроконтроллера. Ещё нулевой столбец четвёртый. Оставим его. В итоге получим ровно 8 бит на один сигнал (таблица 2).


Таблица 2 сокращённый вариант таблицы 1


1 2 3 4 5 6 7 8
Громкость вверх 1 1 0 0 1 1 0 0 0
Громкость вверх 2 0 0 1 1 1 0 0 0
Громкость вниз 1 0 0 1 1 1 0 0 1
Громкость вниз 2 1 0 0 1 1 0 0 1
Mute 1 0 0 1 0 0 1 1 1
Mute 2 1 0 0 0 0 1 1 1

В коде это выглядит так:


#define UP1_DATA      0b00011001#define UP2_DATA      0b00011100#define DOWN1_DATA    0b10011100#define DOWN2_DATA    0b10011001#define MUTE_ON_DATA   0b11100100#define MUTE_OFF_DATA   0b11100001

Но теперь нужно восстановить полный набор данных. Напишем функцию, которая будет возвращать ожидаемую длительность импульса (низкого или верхнего уровней) в зависимости от его номера. Полученный результат будем сравнивать с фактической длительностью полученного импульса от приёмника ИК сигналов. Таким образом, сможем определить какая кнопка была нажата.


Время на нашем микроконтроллере будем измерять по прерыванию переполнения единственного таймера. В нём будем инкрементировать счётчик _timer:


volatile unsigned long _timer = 0;ISR(TIM0_OVF_vect){    _timer++;}

Максимальная частота Attiny13A 9,6 МГц. Прерывание случается каждые 256 тактов, значит за одну секунду происходит 37500 прерываний по переполнению таймера. Чтобы отмерить 900 мкс нам нужно отсчитать примерно 33 прерывания, а чтобы отмерить 1800 мкс нужно примерно 67 прерываний. Как видно, частоты 9,6 МГц с запасом хватает, чтобы детектировать сигнал с пульта от ТВ приставки.


Функция getExpectedTime, которая возвращает ожидаемую длительность импульса в зависимости от порядкового номера текущего импульса, поступившего с приёмника
#define SHORT_TIME 33UL#define LONG_TIME 67ULuint8_t _counter = 0;unsigned long getExpectedTime(uint8_t data){    uint8_t index;    if (_counter >= 2 && _counter <= 4)    {        index = _counter - 2;    }    else if (_counter >= 15 && _counter <= 17)    {        index = _counter - 12;    }    else if (_counter >= 20 && _counter <= 21)    {        index = _counter - 14;    }    else    {        return SHORT_TIME;    }    if (data & (1 << index)) return LONG_TIME;    return SHORT_TIME;}


Переменная _counter глобальная инкрементируется после изменения уровня сигнала на ИК приёмнике. Параметр data это один из 6 паттернов сигналов: UP1_DATA, UP2_DATA, DOWN1_DATA, DOWN2_DATA, MUTE_1_DATA, MUTE_2_DATA.

Т.е. задача сводится к тому, чтобы микроконтроллер реагировал на изменение уровня сигнала с ножки ИК приёмника, вычислял длительность сигнала (низкого или высокого уровня) и при помощи функции getExpectedTime определял совпадает ли реальная длительность с ожидаемой. Если набирается один из паттернов кнопочных сигналов значит соответствующая кнопка и была нажата.


Длительность импульсов будем вычислять в прерывании


volatile bool _hasPulse = false;volatile unsigned long _RXPreviousTime = 0;volatile unsigned long _pulseDuration = 0;ISR(INT0_vect){    _pulseDuration = _timer - _RXPreviousTime;    _RXPreviousTime = _timer;    _hasPulse = true;    _rxPinStatus = !!(PINB & (1 << RX_PIN)); //Аналог digitalRead на Ардуино.}

Итак,


Функция incrementCounter, которая возвращает номер нажатой кнопки
#define SIZE_OF_PATTERNS 6#define PAUSE_TIME 375UL //10000 мкс#define HAS_PATTERN_START 0b00111111#define ERROR_VALUE 19UL#define SIZE_OF_DATA 23#define UP1_BT 0#define UP2_BT 1#define DOWN1_BT 2#define DOWN2_BT 3#define MUTE_ON_BT 4#define MUTE_OFF_BT 5//Индекс паттернов в массиве должен быть в соответствии со значениями: UP1_BT, UP2_BT, DOWN1_DATA, DOWN2_DATA, MUTE_ON_BT, MUTE_OFF_BTconst uint8_t PATTERNS[] = {UP1_DATA, UP2_DATA, DOWN1_DATA, DOWN2_DATA, MUTE_ON_DATA, MUTE_OFF_DATA};uint8_t _hasPattern = HAS_PATTERN_START;//Если паттерн получен полностью, то возвращаем номер кнопки в массиве PATTERNS.uint8_t incrementCounter() //Если паттерн получен полностью, то возвращаем номер кнопки в массиве PATTERNS.{    if (_pulseDuration > PAUSE_TIME)    {        _counter = 0;        _hasPattern = HAS_PATTERN_START;        return 255;    }    if (_hasPattern)    {        unsigned long eTime;        for (uint8_t i = 0; i < SIZE_OF_PATTERNS; i++)        {            if (_hasPattern & (1 << i)) //Если раньше шаблон совпадал.            {                eTime = getExpectedTime(PATTERNS[i]);                if (!((_rxPinStatus ^ !!(_counter % 2)) && _pulseDuration >= eTime - ERROR_VALUE && _pulseDuration <= eTime + ERROR_VALUE)) //Шаблон не совпадает.                {                    _hasPattern &= ~(1 << i);                }            }        }        _counter++;        if (_counter == SIZE_OF_DATA)        {            if (_hasPattern) //Какая-то кнопка совпала            {                switch (_hasPattern)                {                    case 1: return UP1_BT;                    case 2: return UP2_BT;                    case 4: return DOWN1_BT;                    case 8: return DOWN2_BT;                    case 16: return MUTE_ON_BT;                    case 32: return MUTE_OFF_BT;                    default: return 255;                }            }            else            {                return 255;            }        }        else        {            return 255; //Пока никакая кнопка не совпала        }    }    else    {        return 255; //Никакая кнопка не совпала    }}

В этой функции переменная _hasPattern это байт, первые шесть бит которого соответствует одному из шести вариантов кнопок. Изначально биты для всех кнопок равны 1. По мере получения сигналов с ИК-приёмника, если паттерн для заданной кнопки нарушается, то соответствующий бит сбрасывается в 0. В конце должен остаться только один ненулевой бит, если совпал какой-либо паттерн. По положению этого бита определяем какая кнопка была нажата и возвращаем её номер в массиве PATTERNS.


Теперь остаётся только сделать мигание светодиода при получении сигналов с пульта, и управление микросхемой X9C103P.


Размер итоговой прошивки получился 1020 байт из 1024 доступных, прямо тютелька в тютельку, с учётом включённой оптимизацией по размеру (-Os). Полный код доступен в репозитории на GitHub. При разработке использовалась плата ардуино UNO, т.к. отлаживать в ней код проще, чем в Attiny13A. Также на другой плате ардуино UNO был собран эмулятор сигналов пульта, опять-таки для отладки. Все скетчи также есть в репозитории.


Физическая реализация устройства была выполнена на печатной плате (см рисунок), изготовленной при помощи ЛУТ на однослойном текстолите.


Печатная плата


Внутри колонок производится питание 9 вольт, что много для нашего микроконтроллера, поэтому снизим напряжение при помощи линейного преобразователя 78L05.


Всего к устройству подводится от колонок 8 проводов: 3 на один потенциометр, 3 на другой и два для питания. Очень удобно для этих целей использовать сетевой UTP кабель, в котором как раз 8 проводников. Теперь, вместо ручки громкости из колонок будет выходить сетевой кабель с нашим устройством на конце.


Плата колонки


Фото готового устройства:


Вид сверху


Вид снизу


Опыта разводки и изготовления печатных плат у меня очень мало, поэтому, конечно, я накосячил и перепутал контакты ИК-приёмника. Хорошо, что GND посередине, получилось просто развернуть сам приёмник. Исходник уже поправил. И да, smd резистора на 100 Ом я у себя не нашёл, решил временно поставить обычный.


От изготовления корпуса для устройства пока отказался, просто покрыл плату акриловым лаком.


Ну и видео работы:


Подробнее..

24x01 I2C на ATTINY13 без TWI

19.12.2020 14:20:36 | Автор: admin

Казалось бы что тут такого сложного, ну I2C ну без TWI.

Моя реальная задача обстояла чуть шире, в устройстве устанавливался чип в режиме "Reset" контроллера, для экономии места в программируемой логике, задача этого чипа состояла в том чтобы получив на входе короткий импульс как сигнал к началу генерации "длинного" сброса, чип удерживал одну из линий в течении довольно длительного промежутка времени в низком уровне (в программируемой логике можно было сделать цепочку из триггеров в качестве счетчика, но линейка триггеров занимала почти 80 ячеек в CPLD EPM240T100 и место там ой как нужно) к тому же этот чип должен был хранить данные полученный от внешнего устройства по шине I2C в режиме 1 (mode1).

Вообще существует три режима I2C, 2-ой и 3-ий режимы рассчитаны на то что на шине I2C висит гирлянда из устройств, таким образом прежде чем начать работать с устройством на шине в режиме 2 и 3 нужно сначала выбрать устройство, затем уже отправлять в него адреса и данные. Режим 1 это простейший режим работы I2C, пользователь сразу отправляет в чип адрес по которому необходимо записать или прочитать данные и работает с данными.

Вот собственно о чем я говорю (MODE1):

А это MODE2:

В общем-то тут всё итак понятно, но! Здесь есть три задачи:

  1. В ATTINY13 нет аппаратного I2C.

  2. Чип должен выполнять ещё и сброс (хотя для данной статьи это не особо важно).

  3. И финальная проблема 24x01 предполагает объем EEP 128 байт, а в данном чипе EEPROM объемом только 64 байта.

Первая проблема ставит задачу реализации протокола, в том числе отслеживание ошибок на шине, кодом, - тут для нас ассемблер, вторая задача это не особо сложная реализация алгоритма сброса, а вот третья задача выглядит чуть сложнее. Теоретически мы можем задействовать часть памяти FLASH не занятой нашей реализацией, но EEPROM можно писать побайтно, а FLASH только страницами, поэтому прежде чем записать во FLASH полученные данные я обязан сохранить состав всей страницы в памяти микроконтроллера, "подменить" полученные данные внутри страничного буфера, стереть страницу, записать новый состав страницы. При этом запись может попасться в смежные страницы, то есть на стыках. Таким образом я должен произвести запись в два этапа:

В общем я так понимаю зачастую лирика мало кого интересует, тут CTRC+C и CTRL+V чаще применяется, поэтому выкладываю уже код.

Только сперва Коментарии:

  1. CHIP_PINOUTS - Как расположить выводы на чипе (FT24Cxx - стандартная раскладка чипа I2C, InDRIVE_v4 - раскладка под реализацию с контроллером сброса).

  2. I2C_MODE - Режим работы I2C (MODE_1 и MODE_2).

  3. I2CPAGESIZE - размер страницы I2C при страничном режиме записи.

  4. I2CEEPSIZE - общий объем памяти чипа (реализация в коде сначала использует 64 байта EEPROM, затем растягивает "остатки" во FLASH).

  5. RESET_CNT - наличие контроллера сброса (ON - присутствует, OFF - отсутствует).

Собственно перейдем к проверке, достаем TLL866

Загружаем откомпилированную программу (AVR Studio v4), я пролистал в конец чтобы показать состав "псевдо EEP":

Ставим правильные конфигурационные биты и записываем микропрограмму в нашу ATTINY13:

Ага, а теперь не извлекая чип пишем его в режиме I2C EEPROM Mode1 с проверкой:

Работает. Что и требовалось.

Код I2C EEP для ATTINY13
/*InDRIVE v4 Application (chip like: FT24C01, Selfprg Must Be Enabled!!! )Fuse BYTESLow(0x7A) : High(0xEE) :SPIEN = 1SELFPRGEN = 1EESAVE = 0DWEN = 0WDTON = 0BODLEVEL1 = 0CKDIV8 = 0BODLEVEL0 = 0SUT1 = 0RSTDISBL = 1SUT0 = 1CKSEL1 = 0CKSEL0 = 1*/.include "tn13def.inc"/* On/Off Defines */.equOFF=0.equON=1/* Chip Pinouts */.equFT24Cxx=0.equInDRIVE_v4=1/* Reset Controller Enable/Disable */.equRESET_CNT=OFF/* Chip Pinouts Define: InDrivev4/Regular I2C Chip */.equCHIP_PINOUTS=FT24Cxx.equDEBUG=OFF/* I2C Mode Define MODE 1/MODE 2 */.equMODE_1=1.equMODE_2=2.EQUI2C_MODE=MODE_1/* Define I2C Parameters *//* Page Size for Write in BYTES 4/8/16 *//* 128/256 Size in MODE 1 Protocol can't receive more than 128 Bytes */.IF I2C_MODE == MODE_2.MESSAGE "Compile for I2C Mode 2".equI2C_PAGE_SIZE=8.equI2C_EEP_SIZE=256.ELSE.MESSAGE "Compile for I2C Mode 1".equI2C_PAGE_SIZE=4.equI2C_EEP_SIZE=128.ENDIF/* Modes DefineA - AddressiD - Data From MasteroD - Data To MasterMODE 1Write - [START][AAAAAAA0][iD + 0]...[iD + I2C_PAGE_SIZE - 1][STOP]Read -  [START][AAAAAAA1][oD + 0]...[oD + I2C_EEP_SIZE - 1][STOP]MODE 2 Write - [START][10100000][AAAAAAAA][iD + 0]...[iD + I2C_PAGE_SIZE - 1][STOP]Read -  [START][10100001][oD + 0]...[oD + I2C_EEP_SIZE - 1][STOP]*//*I2C slave, fSCL = 400kHzFULLY Implemented I2C Protocol for 24xx01/24xx02And RESET Controller FOR InDRIVE(v4)**************************************************************   ATTINY13A I2C Configuration**************************************************************      pin configuration InDRIVE v4:                       ,---_---.    (RESET/PB5)     nc |1     8| VCC          (PB3)  inRST |2     7| SCL  (PB2)          (PB4) outRST |3     6| EMU24X (PB1) SDA pin from CORE                   GND |4     5| nc (PB0) unused SDA pin                       `-------'   pin configuration FT24C01:                       ,---_---.    (RESET/PB5)     nc |1     8| VCC          (PB3)  nc |2     7| nc  (PB2)          (PB4) nc |3     6| SCL (PB1) SCL pin I2C                   GND |4     5| SDA (PB0) SDA pin I2C                       `-------'   pin configuration FT24C02:                       ,---_---.    (RESET/PB5)     A0 |1     8| VCC          (PB3)  A1 |2     7| nc  (PB2)          (PB4) A2 |3     6| SCL (PB1) SCL pin I2C                   GND |4     5| SDA (PB0) SDA pin I2C                       `-------'*//* Pins Define */.IF CHIP_PINOUTS == InDRIVE_v4.MESSAGE "Chip Pinouts: InDRIVE v4".equEMU24X= 1.equ SCL = 2.ELSE.equEMU24X= 0.equ SCL = 1.MESSAGE "Chip Pinouts: Regular 24Cxx".ENDIF.equ SDA = EMU24X.equ ACK = SDA.equinRST= 3.equoutRST= 4.defTMPnoINT=R19.defCounter=R3.defCounterInWrite=R4.defSREGST=R5/* Real ATTINY13A FLASH Page SIZE In Bytes */.equPAGESIZEB=(PAGESIZE*2)/* SRAM Mapping */.DSEG.IF I2C_MODE == MODE_2_I2c_device_inaddr:  .BYTE 1/* MODE 2 Region */_I2c_device_myaddr:  .BYTE 1.ENDIF_valSPMCSR: .BYTE 1_I2c_data_buffer:.BYTE I2C_PAGE_SIZE_I2c_FLASH_buffer:.BYTE PAGESIZEB.cseg/* Read/Write Pointers */.defI2c_start_addr=R16.defI2c_wr_counter=R17.defI2c_wr_pointer=R18.defI2c_rd_pointer=R21.def_PINB=R20.IF I2C_MODE != MODE_2.IF I2C_MODE != MODE_1.error "Invalid mode for I2C Selected, please correct I2C_MODE define".ENDIF.ENDIF.cseg.org 0//Reset.IF RESET_CNT == ONrjmpWaitinRSTHI;RESETreti;INT0addr= 0x0001; External Interrupt 0rjmpResetInProcess;PCI0addr= 0x0002; External Interrupt Request 0.ELSErjmpmain;RESETreti;INT0addr= 0x0001; External Interrupt 0reti;PCI0addr= 0x0002; External Interrupt Request 0.ENDIFreti;OVF0addr= 0x0003; Timer/Counter0 Overflowreti;ERDYaddr= 0x0004; EEPROM Readyreti;ACIaddr= 0x0005; Analog Comparatorreti;OC0Aaddr= 0x0006; Timer/Counter Compare Match Areti;OC0Baddr= 0x0007; Timer/Counter Compare Match Breti;WDTaddr= 0x0008; Watchdog Time-outreti;ADCCaddr= 0x0009; ADC Conversion Complete// *******************************************************************************************// **   Reset Processor                                                       **// *******************************************************************************************.IF RESET_CNT == ON.MESSAGE "Reset Controller Function is: ON"ResetInProcess:;Proccess resetsbicpinb,inRST;Process reset on falling edgeretiWaitinRSTHI:cli;Wait ~80 mS 0x04E360 on 9.6MHzldiR16,0x10ldiR17,0xE3ldiR18,0x60;Set RESET EnablecbiPORTB,outRST_CntLO:decR18BRNE_CntLO_CntME:decR17BRNE_CntLO_CntHI:decR16BRNE_CntLO; And Clear Iterrupt flag for normal exitldiR16,(1 << PCIF)outGIFR,R16.ELSE.MESSAGE "Reset Controller Function is: OFF".ENDIF// RESET_CNT == ON// *******************************************************************************************************// **   Main Programm                                                         **// *******************************************************************************************************main:;init STACKldiTMPnoINT,low(RAMEND)outSPL,TMPnoINT;init IOldiTMPnoINT,0x00outMCUCR,TMPnoINT;Outputs to HI (Pull UP)ldiTMPnoINT,0xFFoutPORTB,TMPnoINT.IF RESET_CNT == ON;Output enable for outRST, all other for inputsbiPORTB, outRSTsbiPINB, inRSTsbiDDRB,outRST; Output Enable;Init Interrupt    ldi TMPnoINT, (1 << inRST); set pin change interrupt for inRST    out PCMSK, TMPnoINTldiTMPnoINT, (1<<PCIE); unmask interrupt PCIEoutGIMSK,TMPnoINTSEI;Enable PCIE Int Processing.ELSECLI; Disable Reset Controller.ENDIF.IF I2C_MODE == MODE_2/* Set I2C Device Address */ldiTMPnoINT, 0xA0sts_I2c_device_myaddr,TMPnoINT.ENDIF/* Clear Buffer Pointers */clrI2c_wr_counterclrI2c_wr_pointerclrI2c_start_addrclrI2c_rd_pointermain_loop:lI2c_get:sbi portb,acksbis pinb,scl                                            ;wait for SCL&SDA=1rjmp lI2c_getsbis pinb,sdarjmp lI2c_getlI2c_wait_for_start:sbis pinb,scl                                            ;wait for SCL=1,SDA=0 (START)rjmp lI2c_getsbic pinb,sdarjmp lI2c_wait_for_startlI2c_get_0:                                                  ;clear receive area.IF I2C_MODE == MODE_2clr r22sts _I2c_device_inaddr,r22.ENDIFlI2c_get_1:                                                  ;get 1st bytesbi portb,acklI2c_10:                                                     ;wait for SCL=0sbic pinb,sclrjmp lI2c_10ldi r22,8                                                ;bits to receive=8.IF I2C_MODE == MODE_2lds r23,_I2c_device_myaddr                               ;I2C address->R23.ENDIFlI2c_11:in_PINB, pinbsbrs _PINB,scl                                            ;wait for SCL=1rjmp lI2c_11sbrc _PINB,sdarjmp lI2c_13lI2c_12:                                                     ;if SDA=0sbic pinb,sdarjmp lI2c_wait_for_start                                  ;  SDA 0->1? I2CSTOP! (unexpected: wait for next start)sbic pinb,sclrjmp lI2c_12                                              ;  loop while SCL=1clc                                                      ;  SDA=0->Crjmp lI2c_15lI2c_13:                                                     ;if SDA=1sbis pinb,sdarjmp lI2c_get_1                                           ;  SDA 1->0? I2CSTART! (repeated start)sbic pinb,sclrjmp lI2c_13                                              ;  loop while SCL=1sec                                                      ;  SDA=1->ClI2c_15:rol r24dec r22brne lI2c_11                                              ;loop to next bit.IF I2C_MODE == MODE_2sts _I2c_device_inaddr,r24SUB r24,r23                                              ;my address?cpi r24,2brlo lI2c_ack_1rjmp lI2c_exit                                            ;  no: exit and wait for next start.ENDIFlI2c_ack_1:                                                  ;  yes: generate ackclrI2c_wr_counter; Clear Write Buffer PointersclrI2c_wr_pointerclrI2c_start_addrcbi portb,acksbi ddrb,ack                                             ;pinb.ack = output (ACK)lI2c_ack_10:sbis pinb,scl                                            ;wait for SCL=1rjmp lI2c_ack_10lI2c_ack_11:sbic pinb,scl                                            ;wait for SCL=0rjmp lI2c_ack_11/**************************************************************************Select Read/Write**************************************************************************/.IF I2C_MODE == MODE_2cpi r24,0breq lI2c_get_2.ELSEmovI2c_start_addr,r24; Extract Received Addresslsr I2c_start_addr; And Read/Write BITmov I2c_rd_pointer,I2c_start_addr; Upadate Read Addressbrcs lI2c_send_new_byte rjmplI2c_ack_25.ENDIF/**************************************************************************Sending Data to Master**************************************************************************/lI2c_send_new_byte:                                             ;read address receivedsbi ddrb,sda                                             ;pinb.sda = output (will send data)/* Read From Flash */andi I2c_rd_pointer, (I2C_EEP_SIZE - 1)LDIZH,high(MemoryBlockFLASH<<1)LDIZL,low(MemoryBlockFLASH<<1)add ZL,I2c_rd_pointerinc I2c_rd_pointerlpmR24, Zldi r22,8.IF DEBUG == 1rjmp lI2c_s1/* Read From SRAM */ReadFromSram:andi I2c_rd_pointer, (I2C_EEP_SIZE - 1)LDIZH,high(_I2c_device_inaddr)LDIZL,low(_I2c_device_inaddr)add ZL,I2c_rd_pointerinc I2c_rd_pointerldR24, Zldi r22,8.ENDIFlI2c_s1:cbi portb,sdasbrc r24,7sbi portb,sdalI2c_s2:sbis pinb,scl                                            ;wait for SCL=1rjmp lI2c_s2lsl r24lI2c_s3:sbic pinb,scl                                            ;wait for SCL=0rjmp lI2c_s3dec r22brne lI2c_s1sbi portb,sda                                            ;pinb.sda = 0 (will generate ACK)cbi ddrb,sda                                             ;pinb.sda = input (will receive data)lI2c_s4:                                            ;wait acknowloge receivesbis pinb,scl                                            ;wait for SCL=1rjmp lI2c_s4lI2c_s5:sbic pinb,scl                                            ;wait for SCL=0rjmp lI2c_s5sbis pinb,sda;if answer received, - continuerjmplI2c_send_new_byterjmp lI2c_wait_for_start_stop                             ;wait for next start/stop/**************************************************************************Receiving Data from Master**************************************************************************/.IF I2C_MODE == MODE_2lI2c_get_2:                                                  ;write address received: get 2nd bytecbi ddrb,ack                                             ;pinb.ack = output (ACK)lI2c_20:                                                     ;wait for SCL=0sbic pinb,sclrjmp lI2c_20ldi r22,8                                                ;bits to receive=8lI2c_21:sbis pinb,scl                                            ;wait for SCL=1rjmp lI2c_21sbic pinb,sdarjmp lI2c_23lI2c_22:                                                     ;if SDA=0sbic pinb,sdarjmp lI2c_stop                                            ;  SDA 0->1? I2CSTOP! (finish this sequence)sbic pinb,sclrjmp lI2c_22                                              ;  loop while SCL=1clc                                                      ;  SDA=0->Crjmp lI2c_25lI2c_23:                                                     ;if SDA=1sbis pinb,sdarjmp lI2c_get_1                                           ;  SDA 1->0? I2CSTART! (repeated start)sbic pinb,sclrjmp lI2c_23                                              ;  loop while SCL=1sec                                                      ;  SDA=1->ClI2c_25:rol r24dec r22brne lI2c_21                                              ;loop to next bitmov I2c_start_addr,r24                                         ;store received I2C addressmov I2c_rd_pointer,I2c_start_addr; Upadate Read Address.ENDIFReceiveAcknowloge:cbiportb,sdasbiddrb,sdalI2c_ack_21:sbis pinb,scl                                            ;wait for SCL=1rjmp lI2c_ack_21sbic pinb,sdarjmp lI2c_ack_23lI2c_ack_22:                                                     ;if SDA=0sbic pinb,sdarjmp lI2c_stop                                            ;  SDA 0->1? I2CSTOP! (finish this sequence)sbic pinb,sclrjmp lI2c_ack_22                                              ;  loop while SCL=1rjmp lI2c_ack_25lI2c_ack_23:                                                     ;if SDA=1sbis pinb,sdarjmp lI2c_get_1                                           ;  SDA 1->0? I2CSTART! (repeated start)sbic pinb,sclrjmp lI2c_ack_23                                              ;  loop while SCL=1lI2c_ack_25:cbi ddrb,ack                                             ;pinb.ack = inputLDIZH,high(_I2c_data_buffer)LDIZL,low(_I2c_data_buffer)lI2c_get_3:                                                  ;get 3rd bytelI2c_30:                                                     ;wait for SCL=0sbic pinb,sclrjmp lI2c_30ldi r22,8                                                ;bits to receive=8movTMPnoINT, I2c_wr_pointerandi TMPnoINT, (I2C_PAGE_SIZE - 1)add ZL,TMPnoINTlI2c_31:sbis pinb,scl                                            ;wait for SCL=1rjmp lI2c_31sbic pinb,sdarjmp lI2c_33_lI2c_32:                                                     ;if SDA=0sbic pinb,sdarjmp lI2c_stop_                                            ;  SDA 0->1? I2CSTOP! (finish this sequence)sbic pinb,sclrjmp lI2c_32                                              ;  loop while SCL=1clc                                                      ;  SDA=0->Crjmp lI2c_35lI2c_33_:lI2c_33:                                                     ;if SDA=1sbis pinb,sdarjmp lI2c_start_                                           ;  SDA 1->0? I2CSTART! (repeated start)sbic pinb,sclrjmp lI2c_33                                              ;  loop while SCL=1sec                                                      ;  SDA=1->ClI2c_35:rol r24dec r22brne lI2c_31                                              ;loop to next bitinc I2c_wr_counterinc I2c_wr_pointerinc I2c_rd_pointerstZ,R24rjmp ReceiveAcknowlogelI2c_wait_for_start_stop:                                    ;wait for start/stoplI2c_ss_1:sbis pinb,scl                                            ;wait for SCL=1rjmp lI2c_ss_1sbic pinb,sdarjmp lI2c_ss_3lI2c_ss_2:                                                   ;if SDA=0sbic pinb,sdarjmp lI2c_stop                                            ;  SDA 0->1? I2CSTOP! (finish this sequence)sbic pinb,sclrjmp lI2c_ss_2                                            ;  loop while SCL=1rjmp lI2c_ss_1lI2c_ss_3:                                                   ;if SDA=1sbis pinb,sdarjmp lI2c_get_1                                           ;  SDA 1->0? I2CSTART! (repeated start)sbic pinb,sclrjmp lI2c_ss_3                                            ;  loop while SCL=1rjmp lI2c_ss_1                                            ;  SDA=1->ClI2c_stop_:lI2c_stop:                                                   ;if stop,cbiddrb, sdacpiI2c_wr_counter,0x00brne WriteFlashRomlI2c_exit:rjmp lI2c_getlI2c_start_:rjmp lI2c_get_1WriteFlashRom:cli; Disable INTERRUPTSrcall WriteReceivedDataclrI2c_wr_counter; Clear Write Buffer PointersclrI2c_wr_pointersei; Enable INTERRUPTSrjmp lI2c_get/************************************************************ Write internal FLASH by page************************************************************///--------- Erase/Program Page FROM FLASH to SRAMBackupFlashPage:ldiYH,high(_I2c_FLASH_buffer) ; Load SRAM Buffer ldiYL,low(_I2c_FLASH_buffer)movTMPnoINT, I2c_start_addrandiTMPnoINT, ((I2C_EEP_SIZE - 1) & ~(PAGESIZEB - 1))ldiZH,high(MemoryBlockFLASH << 1)ldiZL,low(MemoryBlockFLASH << 1)adcZL,TMPnoINTbrccNoIncBackupPageincZHNoIncBackupPage:ldiR22, PAGESIZEBBackupPageLoop:lpmTMPnoINT,Z+stY+,TMPnoINTdecR22brneBackupPageLoopret//--------- Add received data to SRAM PageWriteReceivedData:/* Have Data For Write ? */cpiI2c_wr_counter,0x00brneDataForWritePresentretDataForWritePresent:rcall BackupFlashPageldiYH,high(_I2c_FLASH_buffer) ; Load SRAM Backup Buffer AddressldiYL,low(_I2c_FLASH_buffer)ldiXH,high(_I2c_data_buffer) ; Load SRAM Receive Buffer AddressldiXL,low(_I2c_data_buffer)movTMPnoINT, I2c_start_addrandiTMPnoINT, (PAGESIZEB - 1)adcYL,TMPnoINTbrccNoIncPreparePageincYHNoIncPreparePage:movCounterInWrite,TMPnoINT/* Make PAGE SIZE Window */cpiI2c_wr_counter, I2C_PAGE_SIZEbrloPreparePageLoopldiI2c_wr_counter, I2C_PAGE_SIZEPreparePageLoop:ldR22,X+stY+,R22decI2c_wr_counterbreqNormalPageWritemovTMPnoINT,CounterInWritecpiTMPnoINT, (PAGESIZEB - 1)brneNoWritePageSizeExceededrcallErase_page_by_SPMrcallWrite_Current_PageincCounterInWriteincI2c_start_addrdecI2c_wr_pointerrjmpWriteReceivedDataNoWritePageSizeExceeded:incCounterInWriteincI2c_start_addrdecI2c_wr_pointerrjmpPreparePageLoopNormalPageWrite:rcallErase_page_by_SPMrcallWrite_Current_PageincI2c_start_addrdecI2c_wr_pointerret//--------- Erase pageErase_page_by_SPM:ldiZH,high(MemoryBlockFLASH << 1)ldiZL,low(MemoryBlockFLASH << 1)movTMPnoINT,I2c_start_addrandi TMPnoINT, ((I2C_EEP_SIZE - 1) & ~(PAGESIZEB - 1)) /* Mask Maximum Data Size and Mask Out Real Flash Page Size */adcZL,TMPnoINTbrccNoIncErasePageincZHNoIncErasePage:/* Load Erase Instruction */ldiTMPnoINT, (1<<PGERS) | (1<<SPMEN)sts_valSPMCSR, TMPnoINTrcallWait_spm;CPU Halted while eraseret//Wite pageWrite_Current_Page:ldiXH,high(_I2c_FLASH_buffer) ; Load SRAM Backup Buffer AddressldiXL,low(_I2c_FLASH_buffer)ldiZH,high(MemoryBlockFLASH << 1)ldiZL,low(MemoryBlockFLASH << 1)movTMPnoINT,I2c_start_addrandi TMPnoINT, ((I2C_EEP_SIZE - 1) & ~(PAGESIZEB - 1)) /* Mask Maximum Data Size and Mask Out Real Flash Page Size */adcZL,TMPnoINTbrccNoIncWritePageincZHNoIncWritePage:ldiTMPnoINT, (PAGESIZE)movCounter, TMPnoINTmovwY,Z; Store Z in Y regclrZL;Clear ZclrZHFill_Page_Buffer_loop:ldR0, X+ldR1, X+ldiTMPnoINT, (1<<SPMEN);Write word into page buffersts_valSPMCSR, TMPnoINTrcallWait_spm;CPU Halted while eraseadiwZ, 2decCounterbrneFill_Page_Buffer_loopmovwZ,Y; Restore Z from Y regldi TMPnoINT, (1<<PGWRT) | (1<<SPMEN)sts_valSPMCSR, TMPnoINTrcallWait_spm;CPU Halted while eraseretDo_spm:; check for previous SPM completeWait_spm:inTMPnoINT, SPMCSRsbrc TMPnoINT, SPMENrjmp Wait_spmWait_ee:sbic EECR, EEWErjmp Wait_ee; SPM timed sequenceldsTMPnoINT, _valSPMCSRout SPMCSR, TMPnoINTspmret.cseg.org(0x200 - (I2C_EEP_SIZE/2))MemoryBlockFLASH:          ; Bytes in Flash   .DB 0x55, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F   .DB 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F   .DB 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F   .DB 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F   .DB 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F   .DB 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F   .DB 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F   .DB 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D   ; Mark End of EEP DATA.org(0x1FF).db 0x7E,0x7F.MESSAGE "I2C For Read/Write Processing SELFPRGEN FUSE Must Be Enabled!!!".esegMemoryBlockEEP:          ; Bytes in EEP   .DB 0x55, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F   .DB 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F   .DB 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F   .DB 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F

Всем добра и успехов!!!

Спасибо за внимание.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru