mcu - Atmega128 управляет двумя устройствами по шине SPI, плюс одно на шине TWI, плюс на усарт повешен преобразователь в интерфейса rs485, через который и идёт связь с внешним миром. По внешним светодиодикам зависон выглядит так, как будто после перезагрузки по какой-то причине (по вотчдогу или пропаданию питания) контроллер начинает по новой инициализироваться и на каком-то этапе входит в вечный цикл, который обычно должен выполнятся очень быстро. После снятия напряжения питания и его восстановления зависон исчезает (на некоторое время).
Привожу часть кода инициализации, с возможно проблемными местами (пояснения ниже):
Код
/******************************************************************************/
/* avr-gcc v 4.1.2 */
#include <avr/io.h>
#define BIT(b) (1 << b)
void spi_transmit (unsigned char c)
{
asm ("wdr");
SPDR = c;
while (!(SPSR & BIT(SPIF)));
}
void spi_str_transmit (const unsigned char * str, unsigned char size, unsigned char no)
{
static const unsigned char cs_bit [] = {BIT(PB4), BIT(PB6)};
PORTB &= ~cs_bit [no];
for (;size--;)
spi_transmit (*str++);
PORTB |= cs_bit [no];
}
/******************************************************************************/
void twi_transmit (unsigned char data, unsigned char flags, const unsigned char stat)
{
asm ("wdr");
TWDR = data;
TWCR = flags;
while (!(TWCR & BIT (TWINT)));
}
void send_twi_addr (const char a)
{
twi_transmit (a, BIT(TWINT) | BIT(TWEN), 0x18);
}
void send_twi_byte (const char b)
{
twi_transmit (b, BIT(TWINT) | BIT(TWEN), 0x28);
}
unsigned char read_twi_byte (void)
{
twi_transmit (0xFF, BIT(TWEA) | BIT(TWINT) | BIT(TWEN), 0x50);
return TWDR;
}
void twi_start (void)
{
twi_transmit (0xFF, BIT(TWSTA) | BIT(TWINT) | BIT(TWEN), TWI_START);
}
void twi_stop (void)
{
TWCR = BIT (TWSTO) | BIT (TWINT) | BIT (TWEN);
TWCR = BIT (TWSTO) | BIT (TWINT);
}
/******************************************************************************/
void init (void)
{
DDRF |= BIT(PF4) | BIT(PF5) | BIT(PF6) | BIT(PF7); /* all leds */
PORTF |= BIT(PF4) | BIT(PF5) | BIT(PF6) | BIT(PF7); /* on */
asm ("wdr");
WDTCR = BIT(WDCE) | BIT(WDE);
WDTCR = BIT(WDP2) | BIT(WDP1) | BIT(WDP0); /* watchdog timeout ~ 2 sec */
WDTCR = BIT(WDE);
DDRE = BIT (0x03); /* w/r rs485 driver ctrl. output enable */
DDRA |= BIT(PA3); /* debug led */
PORTA |= BIT(PA3); /* off */
PORTD |= BIT(PD3); /* internal pullup resistor */
PORTB = BIT(PB7) | BIT(PB6) | BIT(PB5) | BIT(PB0) | BIT(PB4);
DDRB = BIT(PB7) | BIT(PB6) | BIT(PB5) | BIT(PB4) | BIT(PB2) | BIT(PB1) | BIT(PB0);
while (!(PIND & BIT(PD3))); /* wait pwr signal */
SPCR = BIT(SPE) | BIT(MSTR) | (SPR0); /* init SPI (250 kHz) */
/* Two wire interface initialization */
TWSR = 0; /* <- setting prescaler */
TWBR = 0; /* F_CPU / 16; Bit rate = 250 kHz */
EIMSK = BIT(INT7) | BIT(INT6) | BIT(INT3); /* Extrnal intr. 7, 6, 3 */
EICRB = BIT(ISC71) | BIT(ISC61); /* Falling edge sens. */
EICRA = BIT(ISC31); /* Falling edge sens. */
PORTE = ~BIT(0x03); /* w/r rs485 driver read enable */
UCSR0B = BIT(RXCIE0) | BIT(TXCIE0) | BIT(RXEN0) | BIT(TXEN0); /* enable intr/ on recv/send byte usart0 */
UCSR0C = BIT(UCSZ01) | BIT(UCSZ00) | BIT(UPM01) | BIT(UPM00); /* async, even parity, 2 stop bit 8 bit usart0 */
init_r (0); /* spi_str_transmit */
init_r (1); /* spi_str_transmit */
reset_tm (); /* r/w twi */
init_s (); /* r/w twi */
init_m (); /* eeprom_read -> UBRR0H, UBRR0L; */
clear_list (); /* r/w twi */
asm ("sei");
PORTF &= ~(BIT(PF4) | BIT(PF5) | BIT(PF6) | BIT(PF7)); /* leds off */
}
void loop (void)
{
for (;;)
{ /* ... */ }
}
int main (void)
{
init ();
loop ();
return 0;
}
/* avr-gcc v 4.1.2 */
#include <avr/io.h>
#define BIT(b) (1 << b)
void spi_transmit (unsigned char c)
{
asm ("wdr");
SPDR = c;
while (!(SPSR & BIT(SPIF)));
}
void spi_str_transmit (const unsigned char * str, unsigned char size, unsigned char no)
{
static const unsigned char cs_bit [] = {BIT(PB4), BIT(PB6)};
PORTB &= ~cs_bit [no];
for (;size--;)
spi_transmit (*str++);
PORTB |= cs_bit [no];
}
/******************************************************************************/
void twi_transmit (unsigned char data, unsigned char flags, const unsigned char stat)
{
asm ("wdr");
TWDR = data;
TWCR = flags;
while (!(TWCR & BIT (TWINT)));
}
void send_twi_addr (const char a)
{
twi_transmit (a, BIT(TWINT) | BIT(TWEN), 0x18);
}
void send_twi_byte (const char b)
{
twi_transmit (b, BIT(TWINT) | BIT(TWEN), 0x28);
}
unsigned char read_twi_byte (void)
{
twi_transmit (0xFF, BIT(TWEA) | BIT(TWINT) | BIT(TWEN), 0x50);
return TWDR;
}
void twi_start (void)
{
twi_transmit (0xFF, BIT(TWSTA) | BIT(TWINT) | BIT(TWEN), TWI_START);
}
void twi_stop (void)
{
TWCR = BIT (TWSTO) | BIT (TWINT) | BIT (TWEN);
TWCR = BIT (TWSTO) | BIT (TWINT);
}
/******************************************************************************/
void init (void)
{
DDRF |= BIT(PF4) | BIT(PF5) | BIT(PF6) | BIT(PF7); /* all leds */
PORTF |= BIT(PF4) | BIT(PF5) | BIT(PF6) | BIT(PF7); /* on */
asm ("wdr");
WDTCR = BIT(WDCE) | BIT(WDE);
WDTCR = BIT(WDP2) | BIT(WDP1) | BIT(WDP0); /* watchdog timeout ~ 2 sec */
WDTCR = BIT(WDE);
DDRE = BIT (0x03); /* w/r rs485 driver ctrl. output enable */
DDRA |= BIT(PA3); /* debug led */
PORTA |= BIT(PA3); /* off */
PORTD |= BIT(PD3); /* internal pullup resistor */
PORTB = BIT(PB7) | BIT(PB6) | BIT(PB5) | BIT(PB0) | BIT(PB4);
DDRB = BIT(PB7) | BIT(PB6) | BIT(PB5) | BIT(PB4) | BIT(PB2) | BIT(PB1) | BIT(PB0);
while (!(PIND & BIT(PD3))); /* wait pwr signal */
SPCR = BIT(SPE) | BIT(MSTR) | (SPR0); /* init SPI (250 kHz) */
/* Two wire interface initialization */
TWSR = 0; /* <- setting prescaler */
TWBR = 0; /* F_CPU / 16; Bit rate = 250 kHz */
EIMSK = BIT(INT7) | BIT(INT6) | BIT(INT3); /* Extrnal intr. 7, 6, 3 */
EICRB = BIT(ISC71) | BIT(ISC61); /* Falling edge sens. */
EICRA = BIT(ISC31); /* Falling edge sens. */
PORTE = ~BIT(0x03); /* w/r rs485 driver read enable */
UCSR0B = BIT(RXCIE0) | BIT(TXCIE0) | BIT(RXEN0) | BIT(TXEN0); /* enable intr/ on recv/send byte usart0 */
UCSR0C = BIT(UCSZ01) | BIT(UCSZ00) | BIT(UPM01) | BIT(UPM00); /* async, even parity, 2 stop bit 8 bit usart0 */
init_r (0); /* spi_str_transmit */
init_r (1); /* spi_str_transmit */
reset_tm (); /* r/w twi */
init_s (); /* r/w twi */
init_m (); /* eeprom_read -> UBRR0H, UBRR0L; */
clear_list (); /* r/w twi */
asm ("sei");
PORTF &= ~(BIT(PF4) | BIT(PF5) | BIT(PF6) | BIT(PF7)); /* leds off */
}
void loop (void)
{
for (;;)
{ /* ... */ }
}
int main (void)
{
init ();
loop ();
return 0;
}
Самое подозрительное в отношение зависания место - ожидание установки единицы на PD3:
while (!(PIND & BIT(PD3))); /* wait pwr signal */.
В реальном устройстве этот пин соединён через резистор 1к с шиной питания и вроде всегда должна там быть единица.
Другие места, где возможно организуется вечный цикл - в функциях twi_transmit и spi_transmit.
В связи с этим возникли такие вопросы :
1. Может ли линия порта ввода-вывода (и вообще порты ввода - вывода) входит в такое состояние, что уровни с неё не читаются корректно (то есть единица не воспринимается как единица)?
2. Могут ли так зависать внутринние устройства TWI - SPI, что чтение их статусных регистров всегда возвращает одно и тоже ?
3. Самое трудное - воспроизвести эту проблему не в условиях эксплуатации, а на месте, где её можно решить. Одно из устройств сейчас гоняю всяко - на мороз его выставляю, помехи возле него создаю, влажность повышенную - всё нипочём, - всегда бы так работало. Какие ещё дестабилизирующие средства можно применить ?