Добрался и я до 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(;;) ;
}