Обсуждаем контроллеры компании Atmel.
Ответить

ATtiny13 и I2CSlave

Сб янв 06, 2018 09:37:02

Добрый день, форумчане!
Обычно не обращаюсь за помощью, но тут убил почти неделю на безрезультатный поиск по просторам интернетов.
Есть задача запилить I2c слейв на ATtiny13. Именно на ATtiny13 (26,45,85 - не устраивает по причине большого количества имеющихся уже в наличии 13х).
Задача банальная. Отреагировать согласно протоколу на свой адрес, если чтение: отдать 3-5 байт из массива (ОЗУ), если запись: забрать 3-5 байт от мастера и поместить в массив (ОЗУ). На этом все.
Крайне желательно не использовать INT0 и таймер (хотя он там и не нужен для i2c).
В ATtiny13 нет TWI (USI).
На немецком сайте нашел нужную мне реализацию, но она на асме. Я в асме дуб дубом (хоть когда-то и писал под Z80 :write:).
Если кто-то подкинет стабильную реализацию на C (в любом виде, включая попсовую ардуину).
Пожалуйста, не пинайте... я за 5-8 лет ничего не просил у народа вообще... тут реально нужна подмога :beer:

Re: ATtiny13 и I2CSlave

Сб янв 06, 2018 12:07:47

Ежли просто ОЗУ для хранения до 20 байт - можно использовать DS1307 - там даже опция энергонезхависимого хранения прилагается...
:wink:

Re: ATtiny13 и I2CSlave

Сб янв 06, 2018 12:55:36

Нет. Не просто ОЗУ. А тиньке декодер дифференцированного манчестер-кода из радиоэфира. Данные складываются в 3-5 байт ОЗУ. После чего их нужно отдать по запросу мастера i2c.

Re: ATtiny13 и I2CSlave

Сб янв 06, 2018 14:42:39

нашел асм - выложи, если своих мыслей не будет (после праздников о чемто серьёзном думать тяжело) хоть проанализируем...

Re: ATtiny13 и I2CSlave

Сб янв 06, 2018 14:48:02

АСМ
Спойлер
Код:
; i2c_slave implementation
; needs int0
; needs 2 high and 1 low register and one pointer (z)

.include "tn13def.inc"

.equ   i2c_pin      = PINB
.equ   i2c_port   = PORTB
.equ   i2c_dir      = DDRB

;.equ   i2c_bitSCL   = 0
;.equ   i2c_bitSDA   = 1
.equ   i2c_bitSCL   = 2
.equ   i2c_bitSDA   = 1

.equ   i2c_valSCL   = 1<<i2c_bitSCL
.equ   i2c_valSDA   = 1<<i2c_bitSDA

.def   i2c_data   = r16
.def   i2c_state   = r17
.def   i2c_SREG   = r8

.equ   i2c_slaveAddr   = 0x52

;macros
.macro i2c_waitSCL_low
i2c_waitSCL_low0:   sbic i2c_pin, i2c_bitSCL
      rjmp i2c_waitSCL_low0
.endm

;macros
.macro i2c_waitSCL_high
i2c_waitSCL_high0:   sbis i2c_pin, i2c_bitSCL
      rjmp i2c_waitSCL_high0
.endm


.macro i2c_readAddress
   ; reads the address into @0
   ; this is shorter than readByte, because we do not need to detect a stop condition
   ldi @0, 0x01
i2c_readAddress0:
   i2c_waitSCL_high
   clc
   sbic i2c_pin, i2c_bitSDA
      sec
   rol @0
   i2c_waitSCL_low
   brcc i2c_readAddress0
.endm

.macro i2c_readByte   ; returns when scl is low, after last bit
   ; reads one byte into @0
   ; read the first bit (here could also come a stop condition or a rep-start)
   i2c_waitSCL_high
   in @0, i2c_pin
   andi @0, i2c_valSDA | i2c_valSCL
i2c_readByte2:
   in i2c_state, i2c_pin
   andi i2c_state, i2c_valSDA | i2c_valSCL
   cp i2c_state, @0
   breq i2c_readByte2   ; nothing happend
   
   andi i2c_state, i2c_valSCL
   breq i2c_readByte3   ; first bit recieved
   
   andi @0, i2c_valSDA
   brne i2c_repStart   ; rep-start-condition
   i2c_return      ; stop-condition

i2c_readByte3:
   andi @0, i2c_valSDA
   breq i2c_readByte4
      ldi @0, 0x03
i2c_readByte4:
   ori @0, 0x02      ; end-mark for finish
i2c_readByte0:
   i2c_waitSCL_high
   clc
   sbic i2c_pin, i2c_bitSDA
i2c_readByte1:
      sec
   rol @0
   i2c_waitSCL_low
   brcc i2c_readByte0
.endm

.macro i2c_writeByte   ;returns when scl is low, after last bit
   sec
i2c_writeByte0:
   rol @0
   breq i2c_writeByte3
   brcc i2c_writeByte1
      cbi i2c_dir, i2c_bitSDA
      rjmp i2c_writeByte2
i2c_writeByte1:
      sbi i2c_dir, i2c_bitSDA
      ;rjmp i2c_writeByte2
i2c_writeByte2:   ;sda = bit now
   i2c_waitSCL_high   ; wait one clock pulse
   i2c_waitSCL_low
   clc
   rjmp i2c_writeByte0
i2c_writeByte3:   ; transmittion finished
   cbi i2c_dir, i2c_bitSDA
.endm

.macro i2c_getAck   ; returns when scl is low, after ack
   i2c_waitSCL_high
   andi @0, 0xFE
   sbis i2c_pin, i2c_bitSDA
      ori @0, 0x01
   i2c_waitSCL_low
.endm

.macro i2c_putAck   ; returns if scl is low, after ack
   sbi i2c_dir, i2c_bitSDA
   i2c_waitSCL_high
   i2c_waitSCL_low
   cbi i2c_dir, i2c_bitSDA
.endm

.macro i2c_handleInterrupts   ; this must be called between a putWaitState and remWaitState
   ; needs a high register for temporary usage
   ; remove interrupt-bit from GIFR
   ldi @0, 0x40
   out GIFR, @0
   sei   ; switch interrupts to on
   nop   ; do nothing
   nop   ; max. number of 2 interrupts
   cli   ; switch interrupts to off
.endm
.macro i2c_putWaitState
   sbi i2c_dir, i2c_bitSCL
.endm
.macro i2c_remWaitState
   cbi i2c_dir, i2c_bitSCL
.endm

.macro i2c_putByte   ; store a recieved byte, could be a call
   ; this must be the address -> z-register
   mov zl, @0
   clr zh
.endm

.macro i2c_getByte   ; get a byte for transmittion, could be a call
   cpi zl, 10
   brlt i2c_getByte0
      clr zl
      clr zh
i2c_getByte0:
   ld @0, z+
.endm

.macro i2c_addressed   ; this device has been addressed, could be a call
         ; and take more than only a few clock cycles
.endm

.macro i2c_return   ; return from interrupt, restore SREG, restore Interrupts
   ldi i2c_data, 0x40
   out GIFR, i2c_data   ; clear interrupt flag
   out SREG, i2c_SREG
   reti
.endm
   

i2c_int_data:
   ; only proceed on when start condition happened:
   sbis i2c_pin, i2c_bitSCL
      reti         ; the scl line was low -> normal change of sda line
   
   in i2c_SREG, SREG      ; the scl line was high -> start condition detected

i2c_repStart:
   i2c_waitSCL_low
   
   ; handle the i2c_start_condition
   i2c_readAddress i2c_data      ; read the address byte
   
   ; decode the address
   mov i2c_state, i2c_data
   andi i2c_state, 0x01
   andi i2c_data, 0xFE
   cpi i2c_data, i2c_slaveAddr
   breq i2c_addressMatch      ; if address matches, handle this
      
      i2c_return   ; else return and wait for next start condition

i2c_addressMatch:
   ; this device has been addressed -> give ack
   i2c_putAck
   i2c_putWaitState
   i2c_addressed
   rjmp i2c_readNextByte1
   
;i2c_write:   ; master write mode
;   i2c_putAck
i2c_readNextByte:
   i2c_putWaitState
i2c_readNextByte1:
   i2c_handleInterrupts i2c_data   ; handle some interrupts if needed
   
   sbrc i2c_state, 0      ; if master-read-mode
      rjmp i2c_read      ; read the thing

   i2c_remWaitState      ; remove wait-state
               ; else master-write
   i2c_readByte i2c_data      ; read one byte. this must also detect a stop-condition

   i2c_putAck         ; give the ack
   i2c_putWaitState      ; put the wait state
   
   i2c_putByte i2c_data      ; store the recieved byte internally
   
   i2c_remWaitState
   
   rjmp i2c_readNextByte1      ; and wait for next byte

i2c_read:   ; master read mode
   i2c_getByte i2c_data      ; get the next byte to be read
   
   i2c_remWaitState
   i2c_writeByte i2c_data
   
   i2c_getAck i2c_state

   sbrc i2c_state, 0
      rjmp i2c_readNextByte   ; read the next byte
   
   ; no more bytes to read, the master didn't acknowled
   i2c_return
   
i2c_init:
   ; initialize the peripheral
   cbi i2c_port, i2c_bitSDA
   cbi i2c_port, i2c_bitSCL
   cbi i2c_dir, i2c_bitSDA
   cbi i2c_dir, i2c_bitSCL
   ;initialize interrupt
   ldi i2c_data, 0x40
   out GIMSK, i2c_data
   in i2c_data, MCUCR
   andi i2c_data, 0xFC
   ;falling edge of int0
   ori i2c_data, 0x02
   out MCUCR, i2c_data
   ret

Re: ATtiny13 и I2CSlave

Сб янв 06, 2018 15:08:44

В Codevision AVR в визарде есть програмный IIC. Лет 10 назад сам пользовал.

Re: ATtiny13 и I2CSlave

Сб янв 06, 2018 15:24:44

В Codevision AVR в визарде есть програмный IIC. Лет 10 назад сам пользовал.

Мастер.

Re: ATtiny13 и I2CSlave

Сб янв 06, 2018 21:01:24

Крайне желательно не использовать INT0

Так не бывает. Можно, конечно, циклически опрашивать линию SCL, но тогда контроллер только этим и будет заниматься.
ВСЕ программные реализации slave I2C используют прерывание INT0 (либо INT1) для того, чтобы контроллер мог еще чем-то осмысленным заниматься.

На немецком сайте нашел нужную мне реализацию, но она на асме.

И она также использует внешнее прерывание.
Если Вам надо использовать внешнее прерывание для декодирования манчестера, используйте PCINT0.

Re: ATtiny13 и I2CSlave

Сб янв 06, 2018 22:30:48

Так не бывает. Можно, конечно, циклически опрашивать линию SCL, но тогда контроллер только этим и будет заниматься.
ВСЕ программные реализации slave I2C используют прерывание INT0 (либо INT1) для того, чтобы контроллер мог еще чем-то осмысленным заниматься.

есть же PCINT, тоже внешнее прерывание.
возни конечно будет больше, но работать будет. Если удастся в размер вписаться.

Re: ATtiny13 и I2CSlave

Вс янв 07, 2018 07:31:43

Крайне желательно не использовать INT0

Так не бывает.

Тут и ежу понятно, что нужно прерывание. Я и написал, что "не желательно использовать [именно] прерывание INT0". Желание это относится не к конкретно данному проекту, а исключительно относительно последующей универсальности такой либы для других проектов. В любом случае это лишь пожелание, а не требование.

Добавлено after 13 minutes 54 seconds:
...для того, чтобы контроллер мог еще чем-то осмысленным заниматься.

Это смотря как посмотреть)) Если Манчестер 1мс TE и пакет относительно редкий, то его вполне можно обрабатывать вн. прерыванием вне всяких циклов в майне. Вся его задача состоит в вычислении разницы таймера, сброса таймера, записи бита в переменную байта, битового сдвига и инкремента счетчика бита. Так что вполне можно обойтись без обработки в основном цикле и сделать все в рамках обработки прерывания.
Я такую схему использовал для декодирования кодов CAME (ворот). Все работало аж бегом. Правда там ТЕ был 340 мс.

Re: ATtiny13 и I2CSlave

Вс янв 07, 2018 08:21:16

есть же PCINT, тоже внешнее прерывание.

А я о чем написал?
Если Вам надо использовать внешнее прерывание для декодирования манчестера, используйте PCINT0

Человеку надо внешнее прерывание для декодирования манчестера, он пишет для этого программу. Какой смысл изменять отработанную библиотеку I2C под PCINT? Программу для манчестера все равно писать, но зачем еще и писать изменения в готовой библиотеке, если можно без этого обойтись?
PCINT сейчас есть во всех новых контроллерах семейства AVR. Делать библиотеку манчестера под PCINT, а I2C использовать готовую.
В любом случае это лишь пожелание, а не требование.

Можно использовать аппноут AVR302, это готовая библиотека I2C slave. Правда, тоже на ассемблере. Но в чем препятствие? Если вы пишете на С, сделайте эту библиотеку ассемблерной вставкой. Решите только вопрос передачи принятых байт в С-шные функции, но это несложно.

Re: ATtiny13 и I2CSlave

Вс янв 07, 2018 08:44:58

Alkul, ну, если не брать в расчет арбитраж и более функциональный фарш на INT0 (который не хотелось бы тратить на i2c)... то да, безусловно, разницы нет. Для меня АСМ - темный лес со светом в конце тоннеля. Увы.
ЗЫ AVR302 использует TMR0

Я наверно должен пояснить суть своей просьбы. Я не требую кого-то что-то написать. Я вовсе не халявщик из разряда "сделайте мне быстренько". Я лишь прошу поделиться СИшной заготовкой либы, если у вас вдруг таковая есть. С диаграммами I2C у меня свадьбы не случилось еще лет 10 назад... если (вдруг) не найдется либы слейва i2c на СИ, то придется открывать спецификацию и вновь пытаться побороть этот злосчастный для меня i2c. Ну или покорять АСМ .. скорее это уже в другой жизни.

Re: ATtiny13 и I2CSlave

Вс янв 07, 2018 21:53:37

Я лишь прошу поделиться СИшной заготовкой либы

Увы. На С не пишу.

Re: ATtiny13 и I2CSlave

Вс янв 07, 2018 22:43:42

А если посчитать?
Тактовая тини13 9,6МГц.
Если I2C 100 кГц, у нас есть 96 тактов на обработку каждого бита. Если прерывание- то около 20 тактов на заход- выход. Итого 70 тактов на обработку одного состояния. Это достаточно мало, но можно влезть. А вот 400 кГц не успеет.

Re: ATtiny13 и I2CSlave

Пн янв 08, 2018 14:08:03

А чем обусловлен выбор именно протокола I2C?, несколько слейв устройств на шине?

Re: ATtiny13 и I2CSlave

Пн янв 08, 2018 19:15:33

А чем обусловлен выбор именно протокола I2C?, несколько слейв устройств на шине?

Да 3 слейва еще на линии. Собственно можно было бы и 1W использовать. Но хотелось бы именно i2c.

Re: ATtiny13 и I2CSlave

Пн янв 08, 2018 19:25:39

Вы можете любой свой протокол придумать получается? Сделайте например какой-нибудь полудуплексный SPI, мне кажется будет проще.

Re: ATtiny13 и I2CSlave

Пн янв 08, 2018 21:51:14

Z_h_e писал(а):Вы можете любой свой протокол придумать
У меня почему то такая же идея промелькнула и именно про SPI, потому что прост как три копейки. :))

Re: ATtiny13 и I2CSlave

Пн янв 08, 2018 22:51:04

Есть задача запилить I2c слейв на ATtiny13.

Если ещё актуально могу попробовать написать нужный код на графическом асме (Algorithm Builder).

Re: ATtiny13 и I2CSlave

Вт янв 09, 2018 16:27:04

Меняй контроллер, хотя бы на ATtiny25, у него имеется аппаратный USI.
Вот, поиграйся
Правда скорость всего 100Гц (из-за записи в его EEPROM), но работает, по крайне мере в симуляторе,
хотя если USI-Slave доработать может и можно скорость поднять.
Ответить