реклама на сайте
подробности

 
 
> i2c между двумя ATmega8- II, - вторая серия.
ReAl
сообщение Feb 26 2009, 22:39
Сообщение #1


Нечётный пользователь.
******

Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417



Добрался и я до twi slave на AVR-ке, решил проверить, что же там и почему не работает "между двумя ATmega8"
Автор первой серии решил, что он разобрался и закрыл тему. Ну его никто и не заставляет это читать, а другим начинающим может быть полезно.
Правда, я не понял, почему это вдруг
Цитата(rushack @ Feb 18 2009, 16:12) *
в прерывании всё обрабатывать нужно
там просто в нужном месте слово volatile поставить надо и всё работает (с оговорками, ради которых вторая серия и выпущена) с указателем на обработчик, проверкой его в прерывании и вызовом по указателю. Т.е. как изначально задумано.

Поскольку та тема закрыта, для более подробного разбора замечаний пришлось открыть новую.
Итак, к чему приводит в том исходнике применение |= вместо =. Приведены фрагменты switch, комментарии //!//
отмечают добавленный для демонстрации происходящих событий код. Исходная программа рассчитана на 8-байтовый буфер, для проврок обработки переполнения послыалось 9-12 байт.

Цитата(rushack @ Feb 16 2009, 21:34) *
Код
    switch (TWSR & 0xF8)
    {
        case TW_SR_SLA_ACK:
        case TW_SR_ARB_LOST_SLA_ACK:
        case TW_SR_GCALL_ACK:
        case TW_SR_ARB_LOST_GCALL_ACK:
            i2crxindex = 0;
            TWCR |= (1 << TWINT) | (1 << TWEA);
            break;
Тут в бит TWEA записана единичка. Дальше идут прерывания приёма данных

Цитата(rushack @ Feb 16 2009, 21:34) *
Код
        case TW_SR_DATA_ACK:
        case TW_SR_GCALL_DATA_ACK:
            i2crxbuffer[i2crxindex++] = TWDR;
            if(i2crxindex < TWI_RX_BUFFER_SIZE)
                TWCR |= (1 << TWINT) | (1 << TWEA); // receive data byte and return ACK
            else
                TWCR |= (1 << TWINT); // receive data byte and return NACK
            //!// светодиод загорается при 9-байтовом пакете, происходит переполнение приёмного буфера i2crxbuffer
            //!// if( i2crxindex > TWI_RX_BUFFER_SIZE)
            //!//     ON(LED_R);
            break;
Тут при достижении конца буфера якобы будет "and return NAK", но бит TWEA останется, как было сказано в прошлый раз, в единичке. В результате при приёме от мастера следующего байта прерывание идёт не на следующую ветку, которая ниже, а на эту же, буфер переполняется и в зависимости от компиляции не при девяти, так при двенадцатибайтовом пакете программа глючит/зависает.

Цитата(rushack @ Feb 16 2009, 21:34) *
Код
        case TW_SR_DATA_NACK:
        case TW_SR_GCALL_DATA_NACK:
            TWCR |= (1 << TWINT);
            //!// светодиод НЕ загорается при 9-байтовом пакете, т.е. сюда программа никогда не попадает.
            //!//   ON(LED_R);
            break;

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

Приблизительно так должна была бы выглядеть та программа для того, чтобы и корректно обрабаотывать слишком длинные пакеты и сигнализировать об этом мастеру NAK-ом и чтобы функционировала в определённых случаях полезная возможность вызывать обработчик пакета по изменяемому указателю. При этом полученный в результате компиляции код на три десятка байт короче исходного.
CODE
#include <avr/io.h>
#include <util/twi.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <util/atomic.h>

#include "pin_macros.h" // это традиционные макросы для ON/OFF/... выводов контроллера
#include "cam_tez.h" // а это от той платы, на которой проверялось, отсюда светодиоды взяты

#define TWI_RX_BUFFER_SIZE 8

// receive buffer (incoming data)
static uint8_t i2crxbuffer[TWI_RX_BUFFER_SIZE];
static uint8_t i2crxindex;
uint8_t i2c_rxbuffer[TWI_RX_BUFFER_SIZE];

typedef void (*i2c_handler)(uint8_t,uint8_t*); // лучше сразу объявить тип указателя на обработчик

static volatile i2c_handler i2cSlaveReceive;

// Можно было бы и так, без своего типа, но слово volatile обязательно - в основнйо программе указатель только пишется
// и никода не читается, откуда компилятору знать, что он читается в прерывании?
// Вот он и оптимизирует эти "ненужные" записи путём удаления из кода
//static void (* volatile i2cSlaveReceive) (uint8_t receiveDataLength, uint8_t * recieveData);

void twi_init(uint8_t adress);

#define TWCR_DEFAULT ((1<<TWINT) | (1<<TWEN) | (1 << TWIE) | (1<<TWEA))

// I2C (TWI) interrupt service routine
SIGNAL(SIG_2WIRE_SERIAL)
{
uint8_t twi_sr = TW_STATUS;

switch (twi_sr) {
case TW_SR_SLA_ACK:
case TW_SR_ARB_LOST_SLA_ACK:
case TW_SR_GCALL_ACK:
case TW_SR_ARB_LOST_GCALL_ACK:
i2crxindex = 0;
TWCR = TWCR_DEFAULT;
break;

case TW_SR_DATA_ACK:
case TW_SR_GCALL_DATA_ACK:
i2crxbuffer[i2crxindex++] = TWDR;
if (i2crxindex < TWI_RX_BUFFER_SIZE)
TWCR = TWCR_DEFAULT;
else
TWCR = TWCR_DEFAULT & ~(1 << TWEA); // тут обнуляем TWEA
//!// //НЕ загорается при 9-байтовом пакете, работаем нормально, без переполнения буфера
//!// if( i2crxindex > TWI_RX_BUFFER_SIZE)
//!// ON(LED_R);
break;

case TW_SR_DATA_NACK:
case TW_SR_GCALL_DATA_NACK:
TWCR = TWCR_DEFAULT & ~(1 << TWEA);
//!// //загорается при 9-байтовом пакете, раз сюда попали, значит сообщили мастеру, что он неправ
//!// ON(LED_R);
break;

case TW_SR_STOP:
TWCR = TWCR_DEFAULT;
{ // подавление volatile в обработчике - чтобы не зачитывало указатель два раза
i2c_handler hnd = i2cSlaveReceive;
if( hnd ) hnd(i2crxindex, i2crxbuffer); // i2c receive is complete, call i2cSlaveReceive
}
break;

case TW_BUS_ERROR:
TWCR = TWCR_DEFAULT | (1 << TWSTO);
break;
}
}


void twi_slave_setrxhandler( i2c_handler i2cSlaveRx_func)
{
// Атомаризация желательна, так как обработчик может меняться на ходу
// Если нет полной гарантии невозможности чтения указателя в прерывании в процессе его смены,
// то прерывания надо заблокировать. Хватило бы блокировки прерываний только от TWI, но лень думать,
// какие конфликты могут возникнуть при параллельном обращении к TWСR отсюда и из обработчика,
// проще на несколько тактов запретить все прерывания.
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
i2cSlaveReceive = i2cSlaveRx_func;
}
}


void twi_init(uint8_t adress)
{
i2cSlaveReceive = 0;
TWAR = adress;
TWCR = TWCR_DEFAULT;
sei();
}


void i2c_rxservice(uint8_t length, uint8_t * data)
{
uint8_t counter;
for (counter = 0; counter <= length; counter++)
i2c_rxbuffer[counter] = *data++;

switch (i2c_rxbuffer[0]) {
case 'a':
ON(LED_G);
break;
case 'b':
OFF(LED_G);
break;
}
}


int main(void)
{
DRIVER(LED_G,OUT);
DRIVER(LED_R,OUT);

twi_init(0x58);
// не было никакого смысла вызывать twi_slave_setrxhandler в цикле
twi_slave_setrxhandler(i2c_rxservice);

for(;;) ;

}


--------------------
Ну, я пошёл… Если что – звоните…
Go to the top of the page
 
+Quote Post

Сообщений в этой теме


Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 22nd July 2025 - 10:33
Рейтинг@Mail.ru


Страница сгенерированна за 0.01372 секунд с 7
ELECTRONIX ©2004-2016