#include <util/crc16.h>
#include <util/delay.h>
#include <avr/wdt.h>
#define DATA_LENGTH 6
#define ADDRESS 0x10
#define RECEIVER 0x11
#define RE_DE_ON PORTD|=4
#define RE_DE_OFF PORTD&=~(4)
#define RE_DE_REG DDRD |= 4
#define BAUD 115200
#define _WAIT(_pres) _delay_us(_pres)
#define SERIAL_BUFFER_SIZE 16
struct ring_buffer
{
uint8_t buffer[SERIAL_BUFFER_SIZE];
volatile uint8_t head;
volatile uint8_t tail;
};
volatile uint8_t listener = 0;
volatile uint32_t delayWD = 0;
ring_buffer rx_buffer = { { 0 }, 0, 0 };
uint8_t bufferBusTx[DATA_LENGTH] = {0};
uint32_t delayS = 0;
uint32_t _micros = 0;
volatile uint8_t ack = 0;
uint8_t err = 0;
const uint32_t pres = 69120000/BAUD; //temps de maintien RE_DE
const uint32_t maxRec = 14000000/BAUD; //temps max en reception
const uint32_t maxTrans = (32640000/(BAUD/100))+((ADDRESS*(pres*2))); //temps max de transaction+ temps different entre module selon l'adresse
volatile uint8_t* registreR[3] = {&PINB, &PINC, &PIND}; //tableau pour la gestion semi dynamique des registres (&DDR = &PIN+1, &P0RT= &PIN+2)
uint8_t stateB = 0;
uint32_t delayB = 0;
inline void store_char(uint8_t c, struct ring_buffer *buffer) //gestion des donnees entrantent dans le buffer (circulaire)
{
uint8_t i = (uint8_t)(buffer->head + 1) % SERIAL_BUFFER_SIZE;
if (i != buffer->tail) {
buffer->buffer[buffer->head] = c;
buffer->head = i;
}
}
ISR(USART_RX_vect)
{
if((UCSR0B & (1<<RXB80))) // si bit9 à 1 = adresse
{
listener = UDR0;
if(listener == ADDRESS || listener == 0x80 || (ack && listener == 0x81)) //s'il sagit de son adresse ou adresse controleur = 0x80 ou si une transaction en cours&& adresse de ack = 0x81 on active l'ecoute
{
UCSR0A &= ~(1<<MPCM0);
}
else //sinon toute donnees transitant sur le bus seront ignore
{
UCSR0A |= (1<<MPCM0);
}
delayWD = micros()+maxTrans; //on active un delay max d'occupation du bus quelque soit l'emeteur ou ecouteur
}
else
{
store_char(UDR0, &rx_buffer); //si mode écoute entrante, on enregistre les donnees
}
}
////////////////////////////////////////////////////////////////////////////
void setup()
{
RE_DE_REG; //pin RE_DE en sortie
DDRB |= 56; //debug
DDRD &= ~(64); // button de test en entree
DDRD |= 56; //debug
begin(BAUD); //init
PORTB |= 56;
_delay_ms(100);
PORTB &= ~(56); //debug reset
wdt_enable(WDTO_2S); //activation watch dog, temps max 2 sec
}
void loop()
{
_micros = micros();
wdt_reset();// watch remise a 0 a chaque passage
if(available()) //si donnees entrente
{
readData();
}
else //sinon on verifie s'il y a des donnee a transmettre dans le buffer
{
sendData();
}
if(PIND&64 && _micros >= delayB) // code de teste en temps que contrôleur distant, remplissage du buffer
{
if(!stateB)
{
bufferBusTx[0] = 0x05; //taille des donnees
bufferBusTx[1] = RECEIVER; //destinataire
bufferBusTx[2] = ADDRESS; //emeteur
bufferBusTx[3] = 0x01; //cmd
bufferBusTx[4] = 0x02; //param: registre
bufferBusTx[5] = 0x20; // param: bit
}
else
{
bufferBusTx[0] = 0x05;
bufferBusTx[1] = RECEIVER;
bufferBusTx[2] = ADDRESS;
bufferBusTx[3] = 0x00;
bufferBusTx[4] = 0x02;
bufferBusTx[5] = 0x20;
}
delayB = _micros+250000;
}
if(listener != 0) //debug
{
PORTD |= 8;
}
else
{
PORTD &= ~(8);
}
PORTB &= ~(56);// debug
PORTD &= ~(31);//debug
}
uint8_t sendData()
{
if(bufferBusTx[2] && !listener) // si données à transmettre et que le bus est libre (=0)
{
PORTB |= 8; //debug
_WAIT(pres); //petite pause de sécurité car même en cas de liberation '0', RE_DE peux encore être maintenu de l'autre côté
writeData(bufferBusTx); //on envois les données
ack = 1; //
}
if(listener && _micros >= delayWD) // si delay de transaction max est dépasse donc erreur, collision, etc
{
listener = 0x00;
ack = 0;
}
}
void readData() // reception
{
if(read() == 0x02) //debut de trame
{
delayS = _micros+(maxRec*2); //definition d'un delay max pour la lecture
while(!available()) //temps qu'il n'y a pas de suite ...
{
if(micros() >= delayS) // si delay depasse on arrête et on retourne une erreur
{
PORTD |= 16;
ackError(0x01);
break;
}
}
uint8_t sizeD = read(); // recuperation de la taille des donnees
uint8_t data[sizeD+4];
data[0] = sizeD;
delayS = _micros+(maxRec*(sizeD+4)); //definition d'un delay max pour la lecture
uint8_t i = 1;
while(i < data[0]+4) //temps qu'on a pas reçu toute la trame
{
if(micros() >= delayS) // si delay depasse on arrête et on retourne une erreur
{
PORTD |= 16;
ackError(0x01);
break;
}
if(available()) // si donnée(s)
{
data[i] = read();
++i;
}
}
if(data[data[0]+3] == 0x03) //fin de trame
{
if(checkData(data)) //crc ok, on envoie la commande
{
action(data, 0);
}
else //sinon on retourne directement un message d'erreur crc à l'envoyeur
{
ackError(0x02);
}
}
else //sinon on retourne directement un message d'erreur general de reception
{
(0x01);
}
}
RXflush();
}
void action(uint8_t* data, uint8_t local)
{
if(data[3] < 0xFE) //s'il sagit d'une cmd
{
if(data[3]==0x00) // test off
{
*(registreR[data[4]]+2) &= ~(data[5]); //PORT &= ~(bit)
}
else if(data[3]==0x01) //test on
{
*(registreR[data[4]]+2) |= data[5];
}
data[5] = ((*registreR[data[4]])&(data[5])); // pour ack : PIN&BIT
if(local) //s'il sagit d'une commande local on rempli le buffer de transmition
{
bufferBusTx[0] = 0x05;
bufferBusTx[1] = 0x80;
bufferBusTx[2] = ADDRESS;
bufferBusTx[3] = 0xFD;
bufferBusTx[4] = data[4];
bufferBusTx[5] = data[5];
}
else //sinon on retourne directement un ack de confirmation
{
data[1] = 0x80;
data[2] = ADDRESS;
data[3] = 0xFF;
_WAIT(pres);
writeData(data);
}
}
if(ack && data[3] >= 0xFE && (bufferBusTx[1] == data[2])) //si transaction en cours et cmd >= FE
{
if(data[3] == 0xFF || err == 2) // ack positif ou plus de 3 échec, on libère le bus
{
bufferBusTx[2] = 0;
_WAIT(pres);
RE_DE_ON;
write(0x00, 1);
_WAIT(pres);
RE_DE_OFF;
ack = 0;
err = 0;
if(data[3] == 0xFF) //s'il sagit d'un ack positif on enregistre le resultat
{
stateB = data[5];
}
}
else //si un ack d'erreur on retante directement
{
++err;
_WAIT(pres);
writeData(bufferBusTx);
}
}
}
void writeData(uint8_t* data)
{
//calcul du crc en premier
uint16_t crc = 0xFFFF;
for(uint8_t i=0; i <= data[0]; i++)
{
crc = _crc16_update(crc, data[i]);
}
//active le mode transmition
RE_DE_ON;
write(data[1], 1); //adressage
write(0x02, 0); //debut de trame
for(uint8_t i=0; i <= data[0]; i++) //donnees (suivant la taille specifiée)
{
write(data[i], 0);
}
write(((crc & 0xFF00) >> 8), 0); //crc MSB
write((crc & 0x00FF), 0); //crc LSB
write(0x03, 0); //fin de trame
_WAIT(pres); //delay de maintien
RE_DE_OFF; // on desacive le mode de transmition
}
uint8_t checkData(uint8_t* data) //controle crc
{
uint16_t crc = 0xFFFF;
uint16_t _crc = (data[data[0]+1]<<8);
_crc += data[data[0]+2];
for(uint8_t i=0; i <= data[0]; i++)
{
crc = _crc16_update(crc, data[i]);
}
if(crc == _crc)
{
return 1;
}
return 0;
}
void ackError(uint8_t ErrorNo)
{
uint8_t data[5] = {0};
data[0] = 0x04;
data[1] = 0x81;
data[2] = ADDRESS;
data[3] = 0xFE;
data[4] = ErrorNo;
_WAIT(pres);
writeData(data);
}
//////////////////////////////////////////////////////////////////////////////////////////////////
void write(uint8_t data, uint8_t txb8)
{
while (!((UCSR0A) & (1 << UDRE0)))
;
if(txb8)
{
UCSR0B |= (1<<TXB80);
}
else
{
UCSR0B &= ~(1<<TXB80);
}
UDR0 = data;
}
uint8_t available(void)
{
return (uint8_t)(SERIAL_BUFFER_SIZE + rx_buffer.head - rx_buffer.tail) % SERIAL_BUFFER_SIZE;
}
uint8_t read(void)
{
if (rx_buffer.head == rx_buffer.tail)
{
return -1;
}
else
{
uint8_t c = rx_buffer.buffer[rx_buffer.tail];
rx_buffer.tail = (uint8_t)(rx_buffer.tail + 1) % SERIAL_BUFFER_SIZE;
return c;
}
}
void RXflush()
{
rx_buffer.head = rx_buffer.tail;
}
void begin(unsigned long baud)
{
uint16_t baud_setting;
UCSR0A = 0;
baud_setting = (F_CPU / 8 / baud - 1) / 2;
UBRR0H = baud_setting >> 8;
UBRR0L = baud_setting;
UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0)|(1<<UCSZ02);
UCSR0C = (3<<UCSZ00);
UCSR0A |= (1<<MPCM0);
}
Com poste suivant: