New lighting control system - i2c question rpi and avr micro - Page 1
Forum section: electronics / projects

author: jmibk
author_rank: Regular Contributor
timestamp: November 12, 2016, 06:49:29 pm
content: Hello,I'am designing a new lighting control software that runs on a rasperry pi and also on windows or linux based platforms. It communicates mainly via Art-Net and on a raspberry pi via I2C to an external micro (atmel atmega328p).If you interested in that software, visithttps://www.facebook.com/GearBoxLC1. There is also a alpha preview version of it for download. It is freeware.Now to the problem: This i2c connection is very slow, 3 - 4 updates of the dmx universe (512x 8Bit) per second. It is used to output dmx data on the rpi hardware - so it is neccessary to outsource this task to an external micro, that can generate the neccessary pulses for the break in the dmx protocol (the rpi can''t do that itself).The data are transmit by the raspberry pi (python script) in blocks of 32 bytes per i2c connection.Here is the software of the pi, that sends the data (dmx data stored in __dmx_data):Code:try:from smbus import SMBusexcept ImportError:#raise ImportError('This platform has no SMBUS')passimport timeimport threadingclass I2C(threading.Thread):__address = 0x10__dmx_data =  * 512__send_state = False #True, wenn I2C gesentet wird. Reset ueber useriodef __init__(self,dmxio):threading.Thread.__init__(self)self.__dmxio = dmxiotry:self.__i2c = SMBus(1)except:print " I2C.init: no I2C interface found on this system"for channel in range(0,512):self.__dmx_data = channel #fill with datadef run(self):print " I2C thread start"while self.__dmxio.is_running():for block in range(0,16):data = self.get_i2c_dmx_block_data(block)#print datatry:self.__i2c.write_i2c_block_data(self.__address, block, data)self.__send_state = Trueexcept:pass#print datatime.sleep(0.01)#print self.dmx_dataprint " I2C thread exit"'''CONTROL FUNCTIONS'''#send statedef get_send_state(self):return self.__send_statedef set_send_state(self, state):self.__send_state = statedef get_i2c_dmx_block_data(self, block_number):data = []start = block_number*32end = start + 32for channel in range(start, end):data.append(self.__dmx_data)return datadef set_dmx_data(self,data):self.__dmx_data = dataand the Atmel code (it is clocked 16MHz) and operates at 3,3V :Code:#include <avr/interrupt.h>#include <stdint.h>#include <util/delay.h>#include <util/twi.h>#include <avr/io.h>//common varsvolatile uint8_t dmx_data;//twi varsvolatile uint16_t dmx_channel;//Listenindex//dmx varsuint16_tgCurDmxCh;//current DMX channeluint8_tgDmxState;enum {BREAK, STARTB, DATA};#define TWCR_ACKTWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC);//ACK nach empfangenen Daten senden/ ACK nach gesendeten Daten erwarten#define TWCR_NACKTWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC);//NACK nach empfangenen Daten senden/ NACK nach gesendeten Daten erwarten#define TWCR_RESETTWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA)|(0<<TWSTA)|(1<<TWSTO)|(0<<TWWC);//switch to the non adressed slave mode...#define TWI_ADDRESS0x20//twi Die Slave-Adresse#define F_OSC16000//oscillator freq. in kHz (typical 8MHz or 16MHz)#define IBG10//interbyte gap ISR (TWI_vect){uint8_t data=0;switch (TW_STATUS){//TWI-Statusregister prüfen und nötige Aktion bestimmencase TW_SR_SLA_ACK://0x60 Slave Receiver, Slave wurde adressiertTWCR_ACK;//nächstes Datenbyte empfangen, ACK danach sendendmx_channel=0xFF;//Bufferposition ist undefiniertbreak;case TW_SR_DATA_ACK://0x80 Slave Receiver, ein Datenbyte wurde empfangendata=TWDR;//Empfangene Daten auslesenif (dmx_channel == 0xFF){//erster Zugriff, Bufferposition setzendmx_channel = data * 32;//Bufferposition wie adressiert setzenTCNT0 = 0;PORTB |= (1<<PB2);TWCR_ACK;//nächstes Datenbyte empfangen, ACK danach, um nächstes Byte anzufordern}else{//weiterer Zugriff, nachdem die Position im Buffer gesetzt wurde. NUn die Daten empfangen und speichernif(dmx_channel<512){dmx_data[dmx_channel++]=data;//Daten in Buffer schreibe}TWCR_ACK;}break;case TW_ST_SLA_ACK://0xA8 Slave wurde im Lesemodus adressiert und hat ein ACK zurückgegeben.case TW_ST_DATA_ACK://0xB8 Slave Transmitter, Daten wurden angefordertif (dmx_channel == 0xFF){//zuvor keine Leseadresse angegeben!dmx_channel=0;}if(dmx_channel<256){TWDR = dmx_data[dmx_channel++];//Datenbyte senden}else{TWDR=0;//Kein Daten mehr im Buffer}TWCR_ACK;break;case TW_SR_STOP:TWCR_ACK;break;case TW_ST_DATA_NACK://0xC0 Keine Daten mehr gefordertcase TW_SR_DATA_NACK://0x88case TW_ST_LAST_DATA://0xC8 Last data byte in TWDR has been transmitted (TWEA = “0”); ACK has been receiveddefault:TWCR_RESET;break;}}ISR (USART_TX_vect){uint8_t DmxState= gDmxState;if (DmxState == BREAK){UBRR0H = 0;UBRR0L = (F_OSC/1600);//90.9kbaudUDR0 = 0;//send breakgDmxState= STARTB;}else if (DmxState == STARTB){UBRR0H = 0;UBRR0L = ((F_OSC/4000)-1);//250kbaudUDR0 = 0;//send start bytegDmxState= DATA;gCurDmxCh= 0;}else{PORTB | (1<<PB1);_delay_us(IBG);uint16_t CurDmxCh= gCurDmxCh;UDR0 = dmx_data[CurDmxCh++];//send dataif (CurDmxCh == sizeof(dmx_data)) gDmxState= BREAK; //new break if all ch sentelse gCurDmxCh= CurDmxCh;}}ISR(TIMER0_OVF_vect){PORTB &= ~( (1<<PB2) | (1<<PB1) );}//MAINint main (void){cli();//TWIPORTC |= (3<<PC4);TWAR = TWI_ADDRESS;TWCR &= ~(1<<TWSTA)|(1<<TWSTO);TWCR|= (1<<TWEA) | (1<<TWEN)|(1<<TWIE);//DMXDDRD |= (1<<2)|(1<<1);PORTD |= (1<<2)|(1<<1);//enable transmissionUBRR0H = 0;UBRR0L = ((F_OSC/4000)-1);//250kbaud, 8N2UCSR0C |= (3<<UCSZ00)|(1<<USBS0);UCSR0B |= (1<<TXEN0)|(1<<TXCIE0);UDR0 = 0;//start USARTgDmxState= BREAK;//start with break//TIMER0TCCR0B = (1<<CS02) | (1<<CS00);TIMSK0 |= (1<<TOIE0);//TIMER1TCCR1C = (1<<CS11);TIMSK1 |= (1<<TOIE1);DDRB |= (1<<PB1) | (1<<PB0);//TCNT1H , TCNT1L//sei();while(1){}}I know - the datasheet of that micro says that 16MHz operation not possible with 3,3V but it works absolutely the same as with lower clock rates.Anyone an idea?