Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: USI типа I2C на Tiny45
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
alux
Необходимо организовать связь между Tiny45 (Slave) и Mega324P(Master). Мега должна считывать данные с Тини и передать на ЖКИ (i2c TIC154). Попробовал использовать аппнот avr312 (USI -> I2C Slave) для Тини45, но не удается получить ответ от Тини. ЖКИ при этом тоже не работает. Анализ исходника avr312 показал, что у Тини вывод SCL настроен на выход и удерживается в низком состоянии. Настройка вывода SCL (Tiny45) на вход не решила проблему.
Есть ли у кого положительный опыт работы с данным примером?
AHTOXA
Помнится, мы нашли какой-то глюк в AVR312. Но глюк был мелкий, не приводящий к полной неработоспособности. Если что, то исправленный вариант лежит тут.
alux
Вывод SCL у Тини должен быть выходом? Тогда остальные I2C устройства (ЖКИ) не работают...
AHTOXA
Да, выходом. Но не нулём:
Код
void i2c_init(u08 address)
{
    i2c_Address = address;

    PORT_USI |= (1<<PORT_USI_SCL);                    // Set SCL high
    PORT_USI |= (1<<PORT_USI_SDA);                    // Set SDA high
    DDR_USI |= (1<<PORT_USI_SCL);                    // Set SCL as output
    DDR_USI &= ~(1<<PORT_USI_SDA);                    // Set SDA as input
    USICR =    (1<<USISIE)|(0<<USIOIE)|                // Enable Start Condition Interrupt. Disable Overflow Interrupt.
            (1<<USIWM1)|(0<<USIWM0)|                // Set USI in Two-wire mode. No USI Counter overflow prior
                                                    // to first Start Condition (potentail failure)
            (1<<USICS1)|(0<<USICS0)|(0<<USICLK)|    // Shift Register Clock Source = External, positive edge
            (0<<USITC);
    USISR    = 0xF0;                                // Clear all flags and reset overflow counter
}
SysRq
Цитата(alux @ Feb 22 2010, 19:31) *
Вывод SCL у Тини должен быть выходом?
Если у вас SCL и SDA подтянуты к VCC резисторами как положено, то с МК вы должны имитировать выход типа открытый коллектор\сток (т.е. давать высокий уровень на выход МК не должен).
alux
Притянуты внешними 10кОм к +5В. Должен по спецификации I2C быть открытый коллектор. Получается, что этот USI нифига не совместимый с I2C?
АНТОХА, попробовал твой исходник в паре с отлаженным драйвером I2C (Мастер) на Меге. Не получается прочитать байт от Тини, хоть тресни sad.gif
AHTOXA
Цитата
Не получается прочитать байт от Тини, хоть тресни sad.gif

А передать байт в тиню? Там же только запись, в моём примере. И это точно работало у меняsmile.gif А другие устройства на i2c - перестали зависать?
Возможно я не всё исправил, тогда надо ждать MTh, он это (неответ) вроде поборол.
alux
Ни привета, ни ответа. В слейве даже по условию
Код
        if( USI_TWI_statusReg.dataInRxBuf )
        {
              PORTB |= (1 << LED);
                 ........

ничего не горит.
Со стороны Меги вызываю
Код
void ReadSensor()    
{
    I2c_StartWait(SENSOR_ADDR | W);         // slave address, write to sensor
    I2c_Write(TWI_CMD_MASTER_READ);  
  
    I2c_RepStart(SENSOR_ADDR | R);   // slave address + read bit, read sensor
          
    Value = (unsigned long)I2c_Read(I2C_ACK) << 24;
    Value |= (unsigned long)I2c_Read(I2C_ACK) << 16;
    Value |= (unsigned long)I2c_Read(I2C_ACK) << 8;
    Value |= (unsigned long)I2c_Read(I2C_NACK) << 0;
  
    I2c_Stop();
}
AHTOXA
Ну тогда странно... Я сейчас и проверить-то не могуsad.gif
Вот софтовый i2c мастер для меги16, которым я проверял:
Нажмите для просмотра прикрепленного файла
alux
Нашел одну ошибку:
адрес слейв устройства в тини нужно инициализировать сдвинутым на 1 вправо. Т.е. из Меги передаю адрес 0х10, а в Тини TWI_SlaveAdress = 0x08. После этого начал зажигаться светодиод по условию, "буфер не пуст" и ЖКИ индикатор вроде как работает. "Вроде как" значит, что по включению питания на ЖКИ мусор и зависание, а после ресета по крайней мере проходит процедура инициализации ЖКИ. Но все-равно нет ответа от Тини sad.gif.

Короче, в топку этот недоинтерфейс USI в режиме I2C. Буду пробовать трехпроводный SPI. Надеюсь, что с ним не будет таких проблем...
SysRq
Цитата(alux @ Feb 23 2010, 11:41) *
Нашел одну ошибку...
Разберитесь с адресацией (и с R\W заодно).

Обычно предлагается два подхода.

1. Адрес отделяется от признака R\W; адрес - 7 бит. При приёме вы вручную выделяете сташие 7 бит принятого байта (сдвиг на 1 вправо, отбрасываем R\W) - это и будет адрес. При передаче сдвигаете адрес на 1 влево, добавляете R\W, и отправляете байт. Микросхема "пишется" и "читается" по одному и тому же адресу.
В AVR312 именно так. Код проверки адреса:
Код
(( USIDR>>1 ) == TWI_slaveAddress

2. Адрес связан с R\W, микросхема "пишется" по одному адресу, а "читается" по другому; адрес - 8 бит. Обычно из двух этих адресов легко видно, что старшие 7 бит у них одинаковы, а младший бит адреса для "чтения" равен 1, - т.е. это просто R\W, и легко свести обмен к предыдущему подходу.
Похоже, именно так у вас master реализован.

Согласуйте подход в адресации, и должно завестись...
alux
Цитата(SysRq @ Feb 23 2010, 19:22) *
Разберитесь с адресацией (и с R\W заодно).

Повторю еще раз. Со стороны Меги :
Код
#define SENSOR_ADDR  0x10

    I2c_StartWait(SENSOR_ADDR | W);         // slave address, write to sensor
    I2c_Write(TWI_CMD_MASTER_READ);  
  
    I2c_RepStart(SENSOR_ADDR | R);   // slave address + read bit, read sensor


Со стороны Тини:
Код
TWI_slaveAddress = 0x08;
............
    case USI_SLAVE_CHECK_ADDRESS:
      if ( ( USIDR == 0 ) || ( ( USIDR >> 1 ) == slaveAddress) )
      {
          if ( USIDR & 0x01 )
        {
          overflowState = USI_SLAVE_SEND_DATA;
        }
        else
        {
          overflowState = USI_SLAVE_REQUEST_DATA;
        }
        SET_USI_TO_SEND_ACK( );
      }
.......

И где я не прав?
alux
Попробовал использовать USI (Tiny45) в режиме SPI (Slave). Все нормально запустилось. Отправляю от Тини число, на Меге это число принимаю и вывожу на ЖКИ. Но теперь необходимо поменять устройства ролями: Тини должен быть мастером, а Мега - подчиненным. Соответственно на Тини USCK настроил на выход, а у Меги, соответственно SCK, на вход. Кроме этого на Меге вывод SS настроен на вход и притянут перемычкой на землю.

Функция передачи байта от Тини:
Код
unsigned char send_byte(unsigned char val)
{
   USIDR = val;
   USISR = (1<<USIOIF);
   do
   {
      USICR = (1<<USIWM0) | (1<<USICS1) | (1<<USICLK) | (1<<USITC);
   }
   while ((USISR & (1<<USIOIF)) == 0);
  
   return USIDR;
}

void main()
{
    SETBIT(DDRB, USCK);
    SETBIT(PORTB, USCK);
    
    SETBIT(DDRB, DO);
    CLRBIT(PORTB, DO);
    
    CLRBIT(DDRB, DI);
    SETBIT(PORTB, DI);
        
    for(;;)
    {    
        SendByte(0x11);
        
        delay_ms(100);
    }
}


На Меге функция инициализации аппаратного SPI:
Код
    // select clock phase negative-going in middle of data
    SPCR |= (1 << CPOL0);
        
        // data is sampled on the trailing (last) edge of SCK. SPI Mode - 3
        SPCR |= (1 << CPHA0);
        
    // Data order MSB first
    SPCR &= ~(1 << DORD0);
        
    // enable SPI
    SPCR |= (1 << SPE0);
    
    // clear status
    SPSR = SPSR;
    SpiTransferComplete = true;

    // enable SPI interrupt
    #ifdef SPI_USEINT
    SPCR |= (1 << SPIE0);
    #endif
}

В результате получаю число 255!!!
Пробовал для Тини использовать программный SPI:
Код
void SendByte(unsigned char Data)
{
    unsigned char BitCount = 8;

    do
    {
        // Send bit to Slave, MSB first
        if(Data & 0x80)
        {
            SETBIT(PORTB, DO);
        }
        else
        {
            CLRBIT(PORTB, DO);
        }
        
        // Toggle SCK pin to send current bit
        CLRBIT(PORTB, USCK);
        SETBIT(PORTB, USCK);        
                
        // Get next bit to send
        Data <<= 1;      
    }
    while(--BitCount);

}

Результат - тот же. В чем может быть проблема? Уже все перепробовал cranky.gif
SysRq
Цитата(alux @ Feb 24 2010, 12:28) *
И где я не прав?
Да не красиво как-то biggrin.gif

--

Цитата(alux @ Feb 25 2010, 16:53) *
Код
    // clear status
    SPSR = SPSR;
Так SPIF не сбросится, он read only.
Сброс:
Цитата
SPIF is cleared by hardware when executing the corresponding interrupt handling vector. Alternatively, the SPIF bit is cleared by first reading the SPI Status Register with SPIF set, then accessing the SPI Data Register (SPDR).
alux
SPIF = SPIF;
в данном случае ни при чем.
Проблема в том, что со стороны Меги MISO и MOSI перепутаны местами...
Сбило с толку то, что когда Мега был Мастером, а Тини Слейвом, то все работало. Теперь, ничего не меняя в проводах, изменив только статусы мастера и слейва местами, и, соответственно, бит MSTR => 0 у Меги, MOSI у Меги должен быть входом, а MISO, соответственно, выходом!

PS. А вообще, логично было бы назвать вывод MISO - DI , MOSI - DO у Тини. Чем руководствовались Атмел, не понятно...
Александр Куличок
Цитата
А вообще, логично было бы назвать вывод MISO - DI , MOSI - DO у Тини. Чем руководствовались Атмел, не понятно...

Возможно, Вы будете удивлены, но они почти так у Атмела и названы (с точностью до наоборот). У тини названия выводов MISO и MOSI никак не связаны с USI. Эти названия касаются только назначения ног в режиме программирования.
alux
Вот именно, с точностью до наоборот. Мне, например, удобно связать по интерфейсу SPI Тини с Мегой через разъем для программирования. В случае, если Тини будет Мастером, а Мега - Слейвом, использование аппаратного USI (SPI) через этот же разъем не получится.
alux
С USI(SPI) работает, но нужно организовывать протокол передачи байт, либо синхронизировать начало передачи выводом SS. А у Тини у меня уже нет лишних выводов. Вернулся я все-таки к интерфейсу USI->TWI(Slave). Обнаружил у себя одну ошибку настройки портов. Но все-равно драйвер USI->TWI(Slave) avr312 не работает, а вот этот исходник - работает.
alux
Чтобы отправить от Slave(Tiny45) длинный байт
Код
    for(;;)
    {    
        if(usiTwiSlave.available())
        {
            //usiTwiSlave.receive();

            usiTwiSlave.write((unsigned char)(Value >> 24));
            usiTwiSlave.write((unsigned char)(Value >> 16));
            usiTwiSlave.write((unsigned char)(Value >> 8));
            usiTwiSlave.write((unsigned char)(Value >> 0));
        }
    }

в Master(Мега) нужно добавить задержки
Код
void ReadSensor()    
{
    I2c_StartWait(SENSOR_ADDR | W);  // slave address, write to capsensor  SENSOR_ADDR
    I2c_Write(TWI_CMD_MASTER_READ);  
  
    I2c_RepStart(SENSOR_ADDR | R);   // slave address + read bit, read capsensor SENSOR_ADDR
          
    ValueOfCharge = (unsigned long)I2c_Read(I2C_ACK) << 24;
    delay_ms(100);
    ValueOfCharge |= (unsigned long)I2c_Read(I2C_ACK) << 16;
    delay_ms(100);
    ValueOfCharge |= (unsigned long)I2c_Read(I2C_ACK) << 8;
    delay_ms(100);
    ValueOfCharge |= (unsigned long)I2c_Read(I2C_NACK) << 0;
  
    I2c_Stop();
}

Тини работает от 8МГц, Мега - на 20 МГц. При меньших задержках - не работает.
Nuts_
Если надо то есть реальный проверенный код на ASM
45я и mega8535 на одной правда тактовой частоте 16 мгц

суть что используються констуркции вида

cbi DDRB,0
cbi PortB,2
cbi DDRB,2

для ACK

=-----
тоже откуда то сыскал боле менее готовые исходники в свое время
stells
Цитата(Nuts_ @ Mar 30 2010, 15:07) *
Если надо то есть реальный проверенный код на ASM

надо, давайте smile.gif
Nuts_
код для принимающего утройтва
принимает 6 байт
ну еще широковещательный пакет обрабатываеться

рабоатет по прерываниям

CODE
.include "tn45def.inc"

.def temp =r16
.def USIcnt =r19
.def USIstate =r20



.org 0
RJMP reset;RESET External Pin, Power-on Reset, Brown-out
RJMP reset;INT0 External Interrupt Request 0
RJMP reset;PCINT0 External Interrupt Request 1
RJMP reset;TIMER1 COMPA Timer/Counter1 Compare Match A
RJMP reset;TIMER1 OVF Timer/Counter1 Overflow
RJMP reset;TIMER0 OVF Timer/Counter0 Overflow
RJMP reset;EE_RDY EEPROM Ready
RJMP reset;ANA_COMP Analog Comparator
RJMP reset;ADC ADC Conversion Complete
RJMP reset;TIMER1 COMPB Timer/Counter1 Compare Match B
RJMP reset;TIMER0 COMPA Timer/Counter0 Compare Match A
RJMP reset;TIMER0 COMPB Timer/Counter0 Compare Match B
RJMP reset;WDT
RJMP USIstart;USI - start
RJMP USIofl;USI - Overflow
;-----------------------
USIstart:
push temp

ldi temp,1
mov USIstate,temp


in temp,USICR;(Enable USI Counter overflow interrupt)
sbr temp,0b01000000
out USICR,temp
rcall ClearACK
pop temp
reti

USIofl:
push temp


mov temp,USIstate
cpi temp,1
breq I2Cadr
cpi temp,2
breq I2Cack
cpi temp,3
breq I2Cbyte

rjmp USIend

I2Cadr:
in temp,USIDR
cpi temp,0b11111110
breq I2Calladr
cpi temp,0b00000100
brne I2Cbadadr

; cbi portb,1

ldi xl,low(0x80);Set pointer on the first received byte
ldi xh,high(0x80);адрес буфера дл япримеа

rcall SetACK

ldi temp,2
mov USIstate,temp
clr USIcnt
rjmp USIend

I2Cack:
rcall ClearACK

inc USIcnt
mov temp,USIcnt
cpi temp,6; сколько приянть байтов
breq I2Call

ldi temp,3
mov USIstate,temp
rjmp USIend


I2Cbyte:

in temp,USIDR
st X+,temp; прияняли очередной байт
rcall SetACK

ldi temp,2
mov USIstate,temp
rjmp USIend

I2Calladr:
; пришел шыроковещательный пакет

rjmp I2Cbadadr
I2Call:
; sbi portb,1
; прияны все данные!

I2Cbadadr:
rcall StopDetected

USIend:
pop temp
reti
;--------
SetACK:
sbi DDRB,0
sbi PortB,2
sbi DDRB,2

in temp,USISR
cbr temp,0b00000001;(Set counter to 0xE)
sbr temp,0b11101110;(Enable new interrupts. Releasing SCL)
out USISR,temp
ret


StopDetected:

in temp,USICR;(Disable USI Counter overflow interrupt)
cbr temp,0b01000000
out USICR,temp

ClearACK:

cbi DDRB,0
cbi PortB,2
cbi DDRB,2

in temp,USISR
cbr temp,0b00001111;(Clear counter)
sbr temp,0b11100000;(Enable new interrupts. Releasing SCL)
out USISR,temp
ret
;------------------
reset:

ldi temp,high(RAMEND);High byte only required if
out SPH,temp ;RAM is bigger than 256 Bytes
ldi temp,low(RAMEND)
out SPL,temp

ldi temp,0b00000010
out PORTB,temp
ldi temp,0b00000010
out DDRB,temp

ldi temp,(1<<USISIE)+(1<<USIWM1)+(1<<USIWM0)+(1<<USICS1)
out USICR,temp
ldi temp,0xf0
out USISR,temp

ldi Zh,HIGH(reset)
ldi Zl,LOW(reset)

ldi temp, 0b00100000
out MCUCR,temp

sei

clr USIstate


loop:
sleep
rjmp loop
stells
Цитата(Nuts_ @ Apr 1 2010, 15:15) *
код для принимающего уcтройcтва

спасибо! попробовать правда в ближайшее время не получится, но как только результат будет, отпишусь. может свой вариант предложу
Cursedsmite
Скажите, а примера реализации похожей задачи на ассемблере кто-нибудь видел?

Цитата(Nuts_ @ Apr 1 2010, 15:15) *
код для принимающего утройтва

А кода для передающего устройства, того же Atiny45 ни у кого нету?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.