GUM

Проблема с TWI(I2C)

3 сообщения в этой теме

GUM    0

Всем привет! Начал разбираться с протоколом TWI(I2C) на авр, а именно atmega 8. Почитал статьи на изиэлектроник, написал обработку на прерываниях(подсматривая в пример), чтобы вникнуть. Начал тестить в протеус иии... Неизвестная бага не дает мне покоя. Старт производит, адрес слэйва передает(с битом на запись), адрес нужной ячейки передает, а уже при записи пишет ерунду, все время одно и то же, первый 0x00, второй 0xff, потом стоп(стоп запланирован) и какие бы байты я не писал в обработчике, передает 2 одинаковых 00 и ff... Ошибок не выдает, я поставил флаг на ошибки(1 на ноге), в нужное подпрерывание попадает ровно столько раз, сколько надо(тое ставил флаги туда), да и ставил флаги в другие обработчики прерываний(в которые не надо попадать в данном случае), но все норм. Я скину сюда часть кода, эквиваленты, озу нужные, макрос на отправку, и обработчики прерываний, которые используются для отправки байтов. Пишите, если нужна будет доп информация.

Кстати, все прогнал по трассировщику в студии,  просто сделал rcall на нужную последовательность обработчиков

.equ F_CPU = 8000;частота процессора
    
    
    .equ i2c_sarp   = 0b00000000    // Start-Addr_R-Read-Stop                   Это режим простого чтения. Например из слейва или из епрома с текущего адреса
    .equ i2c_sawp   = 0b10000000            // Start-Addr_W-Write-Stop          Это режим простой записи. 

    .equ i2c_sawsawp    = 0b10000001                // Start-Addr_W-WrPageAdr-Write-Stop    Это режим с предварительной записью нужного адреса страницы в 1 байт  а потом запись
    .equ i2c_saw2sawp       = 0b10000011            // Start-Addr_WrPageAdrH-WrPageAdrL-Write-Stop  Это режим с предварительной записью нужного адреса страницы в 2 байт  а потом запись

    .equ i2c_sawsarp        = 0b00000001            // Start-Addr_W-WrPageAdr-rStart-Addr_R-Read-Stop       Это режим с предварительной записью нужного адреса страницы в 1 байт  а потом чтение
    .equ i2c_saw2sarp       = 0b00000011

 

.DSEG
        
        ;"******" - нужно вносить изменения в основном цикле!!!!!!
        I2C_SlaveAdres: .byte 1;регистр с адресом слэйва******************

        I2C_busy: .byte 1; байт флага занятости шины и2с 1-занято! 0-свободно!
        I2C_ERR: .byte 1;байт для ошибок
        
        I2C_DO: .byte 1;задание, читать или записывать, 2 байта адрес или 1?**********

        I2C_LOW_ADRES: .byte 1;младший байт адреса ячейки для чтения/записи(если адрес однобайтовый, то младший единственный)********
        I2C_HIGH_ADRES: .byte 1;старший байт адреса ячейки для чтения/записи***************

        I2C_KOL_BYTE: .byte 1; сколько байт нужно прочесть или записать? писать количетсво!!!!***********

        I2C_BUFER: .byte 5; байты, которые хотим пропихнуть*************

        I2c_ADRES_BUFER_IN: .byte 2;младший и старший байты адреса начала буфера данных для приема!***************

        I2C_ADRES_BUFER_OUT: .byte 2;младший и старший байты адреса начала буфера данных для отправки!****************

        I2C_INDEX_DATA: .byte 1;размер смещения,т.е. сколько байт уже отправлено, вначале отправки должен ранвться 0!

 

TWSI:;перрывание i2c
PUSHF;р16 и sreg в стэк
PUSH R17;в стек
PUSH R18
PUSH R0;в стек
PUSH R30;в стек
PUSH R31;в стек
CLR R0;нужен 0
LDI ZH,HIGH(TWI_TABLE);заносим в индексную пару адрес таблицы причины прерывания и2с
LDI ZL,LOW(TWI_TABLE);и младший байт
UIN R16,TWSR;кода прерывания в р16
LSR R16;сдвиг влево
LSR R16;еще
LSR R16;и еще
ADD ZL,R16;прибавляем значение к адресу таблицы
ICALL;переходим на причину прерывания


POP R31;достаем из стека z пару
POP R30;
POP R0;р0 из стека
POP R18
POP R17;р17 из стека
POPF;достаем р16 и sreg
RETI;выход из прерывания

TWI_TABLE:;таблица прерываний
RJMP TWI_0x00;0x00 Bus Fail Автобус сломался… эээ в смысле аппаратная ошибка шины. Например, внезапный старт посреди передачи бита.

RJMP TWI_0x08;0x08 Start Был сделан старт. Теперь мы решаем что делать дальше, например послать адрес ведомого

RJMP TWI_0x10;0x10 ReStart Был обнаружен повторный старт. Можно переключиться с записи на чтение или наоборот. От логики зависит.

RJMP TWI_0x18;0x18 SLA+W+ACK Мы отправили адрес с битом записи, а в ответ получили ACK от ведомого. Значит можно продолжать.

RJMP TWI_0x20;0x20 SLA+W+NACK Мы отправили адрес с битом записи, а нас послали NACK. Обидно, сгенерим ошибку или повторим еще раз.

RJMP TWI_0x28;0x28 Byte+ACK Мы послали байт и получили подтверждение, что ведомый его принял. Продолжаем.

RJMP TWI_0x30;0x30 Byte+NACK Мы послали байт, но подтверждение не получили. Видимо ведомый уже сыт по горло нашими подачками или он захлебнулся в данных. 
;Либо его ВНЕЗАПНО посреди передачи данных украли инопланетяне.

RJMP TWI_0x38;0x38 Collision А у нас тут клановые разборки — пришел другой мастер, 
;по хамски нас перебил, да так, что мы от возмущения аж заткнулись. Ничего I’l be back! До встречи через n тактов!

RJMP TWI_0x40;0x40 SLA+R+ACK Послали адрес с битом на чтение, а ведомый отозвался. Хорошо! Будем читать.

RJMP TWI_0x48;0x48 SLA+R+NACK Крикнули в шину «Эй ты, с адресом ХХХ, почитай нам сказки» А в ответ «Иди NACK!» 
;В смысле на запрос адреса с битом чтения никто не откликнулся. Видимо не хотят или заняты. Также может быть никого нет дома.

RJMP TWI_0x50;0x50 Receive Byte Мы приняли байт. И думаем что бы ответить ведомому. ACK или NACK.

RJMP TWI_0x58;0x58 Receive Byte+NACK Мы приняли байт от ведомого и сказали ему «иди NACK!» И он обиженый ушел, освободив шину.

TWI_0x10: ;повторный старт
TWI_0x08:;произвели старт!
LDS R16,I2C_SlaveAdres;адрес слэйва
LDS R17,I2C_DO;что нужно, запись или чтение?
CPI R17,0
BRNE TWI_0x10_WR
ORI R16,1<<0
TWI_0x10_WR:
OUT TWDR,R16;отправляем адрес слейва по шине и2с
OUTI TWCR,0<<TWEA|0<<TWSTA|0<<TWSTO|1<<TWEN|1<<TWIE|1<<TWINT ;флаг преывания, блок тви и разрешаем прерывания
;TWINT флаг прерывания
;TWEA 1-ACK, 0-NACK
;TWSTA 1-start, 0-no start
;TWSTO 1-stop
;TWEN блок TWI включен
;TWIE - разрешаем прерывания TWI
RET;выход из подпрерывания

 

TWI_0x18:;подтверждение после посылки байта адреса и бита записи
LDS R16,I2C_DO;загружаем тех задание в регистр
SBRS R16,0;проверяем, нужно ли нам только записать байт? если да, то переход БЕЗ АДРЕСОВ!
RJMP TWI_0x18_sawp;чтение тут невозможно! т.к. на чтении будет другое подпрерывание
SBRC R16,0;проверяем, а не нужно ли нам послать адрес ячейки? в которую хотим писать
RJMP TWI_0x18_sawsawp;если нужно , то переход. Адрес ячейки состоит из одного байта! соответственно младшего
;если ничего не подошло, значит нужно отправить адрес состоящий из двух байт
;передачу начинаем со старшего байта, затем просто в тех задании поправим на пересылку оставшегося младшего байта
LDS R17,I2C_HIGH_ADRES;загружаем старший байт
ANDI R16,~(1<<0);обнуляем первый бит тех задания, тем самым переводим его на отправку еще одного байта адреса(младшего)
STS I2C_DO,R16
OUT TWDR,R17;загружаем старший байт адреса в тви
RET;выход из подпрерывания

  

TWI_0x18_sawsawp:;ЭТО КЛОООООН!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Запись только младшего байта адреса
LDS R17,I2C_LOW_ADRES;загружаем младший байт адреса ячейки слейва
DEC R16;первращаем sawsawp/sawsarp в просто в sawp или sarp!!!, т.е. загружаем в задание только запись! нужный байт адреса мы уже отослали! теперь только запись/чтение 
STS I2C_DO,R16 ;закидываем в задания
OUT TWDR,R17;загружаем адрес ячейки в тви для записи
OUTI TWCR,0<<TWEA|0<<TWSTA|0<<TWSTO|1<<TWEN|1<<TWIE|1<<TWINT; передача младшего байта адреса!
RET;выход из подпрерывания

 

TWI_0x18_sawp:;ЭТО КЛОООООН!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! запись самих байтов

;(сюда я вставлял счетчик, сколько раз здесь бывает процессор, и все сходится)

LDS R16,I2C_KOL_BYTE;считываем, сколько байт нам нужно записать????
LDS R18,I2C_INDEX_DATA;счетчик, сколько байт скинули
CP R16,R18; кончились байты то?
BREQ TWI_0x18_STOP;если да, то переход и СТОП!!!! говорим слейву: хватит братан, но передаем последний байт)
LDS ZL,I2C_ADRES_BUFER_OUT            //младший байт адреса буфера данных озу, откуда брать байты на отправку
LDS ZH,I2C_ADRES_BUFER_OUT+1       //старший байт адреса буфера данных озу, откуда брать байты на отправку
ADD ZL,R18;прибавляем к адресу  количество байт, которые отправили
ADC ZH,R0
INC R18;увеличиваем счетчик на 1
STS I2C_INDEX_DATA,R18;отправляем в озу
LD R17,Z;берем один из.. байтов по адресу индексной пары 

;(сюда я вставлял строчку LDI R17,1    но отправляет в итоге не одни 1, а опять таки 00 и ff)
OUT TWDR,R17;отправляем в регистр для передачи
OUTI TWCR,0<<TWEA|0<<TWSTA|0<<TWSTO|1<<TWEN|1<<TWIE|1<<TWINT ;передача очередного байта данных!
RET;выход из подпрерывания

 

TWI_0x18_STOP:;СТОПЭ братан слейв,наелся ты байтов, отдохни ЭТО КЛОООН!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
STS I2C_busy,R0;освобождаем линию тви
OUTI TWCR,1<<TWEA|0<<TWSTA|1<<TWSTO|1<<TWEN|1<<TWIE|1<<TWINT ;запись ОКОНЧЕНА!
RET;выход из подпрерывания

TWI_0x28:
LDS R16,I2C_DO;и так, м ыздесь потому что: 1-отправили адрес и нужна запись байтов в ячейки
;2- отправили адрес и нужно чтение и 3 - отправили старший байт памяти, нужно отправить младший.
CPI R16,i2c_sawp;нужна запись?
BREQ TWI_0x18_sawp;переход
SBRC R16,0;нужно записать оставшийся младший байт?
RJMP TWI_0x18_sawsawp;переход!
;а иначе повторный старт!!!!
OUTI TWCR,1<<TWEA|1<<TWSTA|0<<TWSTO|1<<TWEN|1<<TWIE|1<<TWINT ;повторный старт, т.к. чтение!
RET;выходи из подпрерывания

 

.MACRO I2C_OUT;НУЖНО ПРЕДВАРИТЕЛЬНО ЗАКИНУТЬ ОТПРАВЛЯЕМЫЕ ДАННЫЕ В И2С БУФЕР!!!
    ;0-адрес слейва, младший байт = 0!!!
    ; 1-запись или чтение? если с адресом, то сколькибитный?
    ;2-младший байт адреса регистра слэйва
    ;3-старший байт адреса регистра слейва(если не нужен, то 0)
    ;4-сколько байт нужно записать, прочитать?
    ;5-какая скорость передачи нужна?
    ;6-откуда брать байты?
    CLR R16
    STS I2C_INDEX_DATA,R16
    LDI R16,1
    STS I2C_BUSY,R16
    OuTI I2C_SlaveAdres,@0
    //OUTI TWAR,@0;заружаем адрес слэйва
    
    LDI R16,@1;загружаем задание
    STS I2C_DO,R16
    
    .if @3>0x00
    LDI ZL,LOW(@2);адрес регистра слэйва для приема/передачи
    LDI ZH,HIGH(@3)
    STS I2C_LOW_ADRES,ZL
    STS I2C_HIGH_ADRES,ZH
    .else
    LDI ZL,LOW(@2)
    STS I2C_LOW_ADRES,ZL
    .endif


    LDI R16,@4;загружаем значение количества байт для приема/передачи
    STS I2C_KOL_BYTE,R16
    
    OUTI TWSR,0<<TWPS1|0<<TWPS0;предделитель 1

    LDI R16,((F_CPU/@5)-16)/2;расчетскорости приема/передачи данных
    UOUT TWBR,R16

    LDI ZL,LOW(@6)
    LDI ZH,HIGH(@6)
    STS I2C_ADRES_BUFER_OUT,ZL
    STS I2C_ADRES_BUFER_OUT+1,ZH

    OUTI TWCR,1<<TWINT|1<<TWEA|1<<TWSTA|0<<TWSTO|1<<TWEN|1<<TWIE;запуск! отправляем старт! 
;TWINT флаг прерывания
;TWEA 1-ACK, 0-NACK
;TWSTA 1-start, 0-no start
;TWSTO 1-stop
;TWEN блок TWI включен
;TWIE - разрешаем прерывания TWI
   .ENDM

Тело:

OUTI I2C_BUFER,1
OUTI I2C_BUFER+1,2

I2C_OUT 0xA2,i2c_sawsawp,0x03,0,2,200,I2C_BUFER - сюда больше не вернется

Main:

зацикливание

Rjmp main

П.С. вот что пишет i2c debuger в протеусе: S A2 A 03 A 00 A FF A P

Изменено пользователем GUM
Дополнение

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
GUM    0

Новая информация! Запустил отладчик в протеусе конкретно TWI, ошибка следующая, При отправке любого байта сначала пишется строчка [AVR TWI] Initiating data write 0xA2 [U3](я так понял это то, что я отправил в регистр TWDR), затем идет строчка [AVR TWI] Actual data read written 0xA2 [U3] (это то, что отправили слейву в данном случае). После отправки адреса ячейки для записи у меня одна песня играет, сначала идет строчка PC=0x00F2. [AVR TWI] Initiating data write 0x01 [U3], затем PC=0x0210. [AVR TWI] Actual data read written 0x00 [U3] (так записывает первый байт) последующие отправляет одинаково но по другому в отличие только от первого: PC=0x00F2. [AVR TWI] Initiating data write 0x01 [U3], PC=0x0210. [AVR TWI] Actual data read written 0xFF [U3] -второй байт(должен быть 0x01), PC=0x00F2. [AVR TWI] Initiating data write 0x01 [U3], PC=0x0210. [AVR TWI] Actual data read written 0xFF [U3] - третий, такой же четвертый и тд. То есть в регистр записываются нормальные значения, а по итогу отправляет совсем не те байты! Моет кому помогло понять, в чем ошибка... А то уже всю голову сломал! Брал тупо код обработчика прерываний на асме с сайта изиэлектроник, заменив там только макросы, ошибка идентичная! оставил там только свой макрос на отправку.
 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
GUM    0

И так друзья) спасибо всем откликнувшимся :-D спустя 2 недели мучений, ковыряний протеуса, осмотра всех режимов отладки, все пошаговые и не пошаговые, я следил за состоянием всех регистров, как TWI , так и РОН,  снижал частоту процессора, что только не делал. Заметил, что пока PC крутиться в главном цикле, который зациклен на main, rjmp main, т.е. он ничего не делает, регистр TWDR самостоятельно меняется на 00, а при передаче второго, третьего и тд байтов на FF не заходя в прерывание, и понял, что что-то тут не так! Уже хотел писать код без прерываний, так сказать быдло код, но решил проверить на микросхеме eeprom, о чудо, все работает, передает нужные байты. Тут надо сказать: Братан, а чего ты раньше не пробовал другие микрухи, а яв от пробовал! Т.к. схема связана с часами, то решил заменить на аналог DSхххх не помню точное название и там была та же самая хрень, только вместо первого байта 00 и последующий ff, он передавал немного другие, например первый 0x02, второй и последующие 0x55 , а я программировал на передачу только 0x01. Ясно, микруха фигово реализована в протеусе. Но все же она мне нужна, моя заказанная еще не пришла, стал копать , что же не так? Пробежался по даташиту, не нашел ничего специфического. Решил передавать байты в разные ячейки, скажем 10 раз по 3 байта каждый раз и того 30 ячеек проверю. И наконец-то обнаружил суть проблемы, по неизвестным причинам при первой отправке байта(ов), не важно, отправлять 1 байт или 1000000, возникает моя проблема , первый байт всегда 0х00, а второй и последующие(хоть миллион байтов) 0хff. А при последующей отправке во второй, третий и тд, все работает, как "часы".  Может кто столкнулся с такой проблемой, надеюсь помогу кому в будущем) Тему можно закрывать!

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Впервые на русском языке! Работаем с микроконтроллерами STM32F7(на основе STM32F7 Online Training)

Цикл материалов на основе STM32F7 Online Training от компании STMicroelectronics. Описаны функциональные блоки и инструменты разработки для семейства микроконтроллеров STM32F7, охватывающие тематику системной периферии, памяти, безопасности, аналоговой периферии, цифровой периферии, таймеров, экосистемы.

Подробнее>>

Создайте аккаунт или войдите в него для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать аккаунт

Зарегистрируйтесь для получения аккаунта. Это просто!

Зарегистрировать аккаунт

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас


  • Похожие публикации

    • Автор: shuks
      Добрый день!
      От товарища поступила просьба собрать на ардуино (либо изготовить плату на avr) детектор короткого замыкания.
      Есть у него на производстве ряд БП 12в 2квт и десяток потребителей на каждом, таких блоков порядка сотни, и поступила "хотелка" навесить на каждый БП ардуину (питание будет отдельное независимое) которая будет посылать по wifi уведомление когда будет возникать КЗ в блоке/потребителе (важна возможность определения именно КЗ).
      Опыта нет, первое что пришло в голову это:
      1) мониторить потерю напряжения 12в на БП (при КЗ блок уходит в защиту)
      2) при потере напряжения 12в подавать напряжение 3.3в от питания МК с управлением через ключ от ножки МК на "+" питания БП и переход на пп.3
      3) ноль от БП подключить к АЦП МК и замерять: если 0в то все норм и КЗ нет (поломка блока без КЗ либо отключение от сети 220в), если на АЦП будет 3.3в то имеем КЗ и отправляем уведомление.
      Т.к. опыта схемотехники почти нет, прошу помощи по схеме в даже в мелочах
       
    • Автор: Cheshire Cat

      Помогите правильно подобрать элементы. Ниже я опишу своё понимание точно, что нужно для этой схемы, если там будут ошибки поправьте меня.
      Мне нужна:
      1) Источник питания на 5В
      2) Светодиод
      3) Резистор. Сопротивление завит от рабочего напряжение светодиода, а рассеивающая мощность от тока от источника. 
      4) Кнопка
      5) Земля?
      6) 2 конденсатора на 2 нф
      7) Кварцовый генератор на 4 МГц

      Вопросы у меня насчет питания:
      1) Что использовать как источник питания
      2) Как подключать? Вижу куда + подключать, а куда -?
      3) Как питается контроллер
      4) Почему если подать в PB.0 единицу, светодиод потухнет?
    • Автор: Cheshire Cat
      Здравствуйте, почитав ответы на предыдущий вопрос понял, что эффективнее всего будет изучать программирование МК купив себе этот самый МК. Но тогда возникает следующий вопрос:

      Что и где покупать ? Я неработающий студент, поэтому хотелось бы несильно дорого. Локация - Казань.
    • Автор: Cheshire Cat
      Здравствуйте, только начал свой путь в программировании микроконтроллеров. Прошу советов в целом и ответов на вопросы в частности(они ниже).

      1) Какие книги читать? По порядку от начального уровня и далее. Я сейчас читаю книгу Белова А. В. "Микроконтроллеры AVR. От азов программирования до создания практических устройств".

      2) Какие программы использовать? В целом, не знаю какие вообще нужны программы для этого дела. Я сижу на Lunux, но могу и с вириальной машины запускать.

      2.1) Можно ли для написания программы использовать C++? я немного знаю C, но сильно хуже чем C++. 

      3) Можно ли обойтись без покупки реальных схем и инструментов? Хотя бы по первой. Понятно дело, что если развиваться в этом направлении, то без этого не обойтись. Но есть ли такая программа в которой можно собрать схему, в ней в микроконтроллер загрузить программу и посмотреть как она работает?
    • Автор: Григорий101
      Здравствуйте ! Я захотел поставить Bascom AVR на компьютер , но при установке возникли ошибки. Может у вас есть идея как помочь ?