Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * RS232 to TDA5940 Synced data transfer.
- * Reads channel data from RS232 and writes it to SPI connected TDA5940 devices
- * It keeps the TDA5940 running by periodically creating a pulse on the BLANK line.
- * When all new data has been written it is latched in during blank period.
- *
- * (c) 2012 Philipp Claves licensed under GPLv2 or later
- */
- #include <avr/io.h>
- #include <avr/interrupt.h>
- #define F_CPU 20000000
- #define BAUD 115200
- /**
- * Initialize the UART, Frame format 8-N-1
- */
- void uart_init(void){
- #include <util/setbaud.h>
- UBRR0H = UBRRH_VALUE;
- UBRR0L = UBRRL_VALUE;
- #if USE_2X
- UCSR0A |= (1 << U2X0);
- #else
- UCSR0A &= ~(1 << U2X0);
- #endif
- //Set frame format
- UCSR0C = (1<<UCSZ00) | (1<<UCSZ01); // | (1<<URSEL); //8 data bits, 1 stop bit no parity (URSEL is an ATMega specific fuckup)
- //Enable receiver and transmitter.
- UCSR0B = (1<<RXEN0) | (1<<TXEN0);
- }
- /**
- * Initialize the SPI in Master mode, freq = f_cpu/2
- */
- void spi_init(void){
- // Set SS, MOSI and SCK output
- DDRB |= (1<<PB5) | (1<<PB7) | (1<<PB4);
- // Enable SPI, Master, MSB first, set clock rate fck/2
- SPCR = (1<<SPE) | (1<<MSTR);
- SPSR = (1<<SPI2X);
- }
- #define TLC5940_N 1 //Number of chained TLC5940 chips
- #define DCPROG_PORT PORTD
- #define DCPROG_DDR DDRD
- #define DCPROG_LINE (1 << PD6) //DC Program pin: PD6
- #define VPROG_PORT PORTD
- #define VPROG_DDR DDRD
- #define VPROG_LINE (1 << PD7) //DC Program pin: PD7
- #define DC_DATA_SIZE (12 * TLC5940_N)
- typedef uint8_t tlc5940_dc_data_t[DC_DATA_SIZE];
- #define GS_DATA_SIZE (24 * TLC5940_N)
- typedef uint8_t tlc5940_gs_data_t[GS_DATA_SIZE];
- void tlc5940_init(void){
- DCPROG_DDR |= DCPROG_LINE;
- VPROG_DDR |= VPROG_LINE;
- }
- #define BLANK_DDR DDRD
- #define BLANK_LINE (1 << PD4) //Blank pulse pin: PD4
- #define BLANK_OCR OCR1B
- #define BLANK_INT_ENABLE (1 << OCIE1B)
- #define LATCH_INT_FLAG (1 << OCF1A)
- #define BLANK_INT_VECT TIMER1_COMPB_vect
- #define LATCH_DDR DDRD
- #define LATCH_LINE (1 << PD5) //Latch pulse pin: PD5
- #define LATCH_OCR OCR1A
- #define LATCH_INT_ENABLE (1 << OCIE1A)
- #define LATCH_INT_FLAG (1 << OCF1A)
- #define LATCH_INT_VECT TIMER1_COMPA_vect
- #define BLANK_INT_ENABLE (1 << OCIE1B)
- #define BLANK_INT_FLAG (1 << OCF1B)
- #define BLANK_INT_VECT TIMER1_COMPB_vect
- /**
- * Initialize the timer to gernate blanking and latching pulses
- * The timer is set to 10 bit PWM with no prescaler to generate a blanking pulse of
- * 2 clock cyles length every 1024 clock cyles. The blanking is enabled.
- * The latching pulse is configured to be 1 clock cyle in length and is diables by default.
- * To activate the latching pulse, set the LATCH_LINE pin on LATCH_DDR to output (1)
- * Timer interrupts are NOT enabled.
- */
- void timer_init(void){
- TCCR1A = (1 << WGM10) | (1 << WGM11) //Fast PWM 10bit
- | (1 << COM1A1) //Clear A on compare match;
- | (1 << COM1B1); //Clear B on compare match;
- TCCR1B = (1 << WGM12) //Fast PWM 10bit
- | (1 << CS10); //Clock source cpu (no prescaler)
- LATCH_OCR = 0; //Latch pulse, shorter than blaking
- BLANK_OCR = 1; //Blanking pulse
- BLANK_DDR |= BLANK_LINE; //Enable Blanking pulse
- }
- /**
- * Set to 1 once the latching occurs.
- * Due to the interrupt context switch delay this happens about
- * 2µS (at 12Mhz) AFTER the latching pulse has ended.
- */
- volatile uint8_t latch_done;
- volatile uint8_t send_done;
- volatile uint8_t rx_buffer[GS_DATA_SIZE];
- /**
- * Latching interrupt handler.
- * If enabled it will set the latch_done flag and disable the Lachting pulse as well as itself.
- * NOTE: Any timer1 iterrupt could be used here, handling them takes longer than
- * both latching and blanking pulse do.
- */
- ISR(LATCH_INT_VECT){
- PORTB |= (1 << 0); //Debug output
- latch_done = 1; //Set latch done flag
- LATCH_DDR &= ~LATCH_LINE; //Disable latching pulse output
- TIMSK1 = 0; //Disable timerinterrupt
- PORTB &= ~(1 << 0); //Debug output
- }
- /**
- * Enables the latching pulse to fire once and load the data into the TDA5940.
- * It waits for the latching pulse to occur, so that new data
- * can be safely written to SPI.
- * NOTE: Waiting might not be needed here because the UART is slow
- * but better safe than sorry.
- */
- void do_latch_wait(void){
- latch_done = 0; //Clear latch flag. To be set by timer interrupt
- LATCH_DDR |= LATCH_LINE; //Enable latching pulse output
- TIFR1 = LATCH_INT_FLAG; //Clear interrupt flag before enabling timer interrupt
- TIMSK1 = LATCH_INT_ENABLE; //Enable the timer overflow interrupt
- while (!latch_done) {;} //Wait for latch to be done
- }
- ISR(BLANK_INT_VECT){
- for(uint8_t tx_pos = 0; tx_pos < 24; tx_pos++){
- SPDR = rx_buffer[tx_pos];
- while (!(SPSR & (1 << SPIF)));
- }
- send_done = 1;
- }
- void tlc5940_write_dc_data(tlc5940_dc_data_t data){
- VPROG_PORT |= VPROG_LINE;
- DCPROG_PORT |= DCPROG_LINE;
- for (uint8_t i = 0; i < DC_DATA_SIZE; i++){
- while (!(SPSR & (1 << SPIF)));
- SPDR = data[i];
- }
- while (!(SPSR & (1 << SPIF)));
- DCPROG_PORT &= ~DCPROG_LINE;
- VPROG_PORT &= ~VPROG_LINE;
- }
- int main(void){
- uint8_t rx_pos;
- uart_init();
- spi_init();
- tlc5940_init();
- timer_init();
- sei();
- DDRB |= (1 << 0); //Debug output
- while(1) {
- PORTB |= (1 << 0);
- while ( !( UCSR0A & (1 << UDRE0)) ) {;} //Wait for empty UART transmit buffer
- UDR0 = 'R'; //Send ready status
- PORTB &= ~(1 << 0);
- //Receive data from UART and Write it to SPI
- for (rx_pos = 0; rx_pos < GS_DATA_SIZE; rx_pos++){ //Transfer 10 bytes
- while ((UCSR0A & (1 << RXC0)) == 0) {;} //Wait for UART Data
- // SPDR = UDR0; //Write data to SPI
- rx_buffer[rx_pos] = UDR0;
- }
- UDR0 = 'A'; //Ack data receive
- send_done = 0;
- TIFR1 = BLANK_INT_FLAG; //Clear interrupt flag before enabling timer interrupt
- TIMSK1 = BLANK_INT_ENABLE;
- while(send_done == 0){;}
- do_latch_wait(); //Latch data into TDA5940
- }
- }
Add Comment
Please, Sign In to add comment