/** * * Bubble Display PIC Firmware * (for PIC18F4455) * * This code reads in row data from the uart and turns on / off * microcontroller output pins with 50 microsecond resolution. * * Copyright (C) 2010, Jon Bennett * http://www.jbprojects.net/projects/bubbledisplay/ * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. **/ #include #include #include #include #include #include #define CLK_20MHZ 0 #define CLK_48MHZ 1 #define CLK_64MHZ 0 //Nothing in place for 20MHZ and 50 micro res. All other combos supported. #define FIFTY_MICRO_RES 1 #define HUNDRED_MICRO_RES 0 __CONFIG(1,USBOSC & CPUDIV1 & PLLDIV12 & HS & FCMDIS & IESODIS); __CONFIG(2,VREGDIS & PWRTDIS & BORDIS & WDTDIS); __CONFIG(3,PBDIGITAL & LPT1DIS & MCLRDIS & ICPORTDIS & DEBUGDIS); __CONFIG(4,STVREN & LVPDIS & XINSTDIS & DEBUGDIS); __CONFIG(5,UNPROTECT); __CONFIG(6,UNPROTECT); __CONFIG(7,UNPROTECT); // Data Structures struct rowData { unsigned char frameType; //0x01 = rowData unsigned int offTime; //time off in ms unsigned int onTime[24]; }; struct performanceData { unsigned long num_loops; unsigned long micros; }; //keeps track of timer2 with 50 or 100us resolution unsigned int fifty_micros = 0; //keeps track of timer0 with approx 3ms resolution unsigned long micros = 0; // Global variables struct performanceData pData; struct rowData rData; unsigned char * p; //pointer for serial communication unsigned char count; unsigned char numSet; unsigned char toWriteA; unsigned char toWriteB; unsigned char toWriteC; unsigned char toWriteD; int i; // Interrupt service routine that is called every time a timer fires void interrupt isr(void) { if(TMR2IF){ //This is run every 50 or 100uS #if HUNDRED_MICRO_RES fifty_micros+=2; #endif #if FIFTY_MICRO_RES fifty_micros++; #endif TMR2IF=0; // clear event flag } else { if (TMR0IF) { //Run every 3ms or so //20MHz #if CLK_20MHZ micros += 3277; #endif #if CLK_48MHZ micros += 2731; #endif #if CLK_64MHZ micros += 2048; #endif TMR0IF=0; } } } void init(void){ ADCON1 =0x06 ; // Changes PORTA to digital CMCON = 0x07 ; // Disable analog comparators //Configure ports, all output except for RC7 TRISA = 0x00 ; TRISB = 0X00 ; TRISC = 0b10000000 ; //Set RC7 input for USART, all others output 71 TRISD = 0X00 ; TRISE = 0X00 ; PEIE = 1; //Peripheral interrupt enabled /***** Timer 2 Code **** * Prescale ratio set at 1:16 * Timer2 module activated * Postscale ratio set at 1:10 */ #if CLK_20MHZ T2CON = 0b00000101; //Set timer period to 97.6 = 122, works out to be ˜100uS //after ISR is run. OLD: 123, 125, 130 - micros too low, //120, 122, 123 to high. 124=dead on for 6.510215s //PR2 = 124; PR2 = 124; #endif #if CLK_48MHZ #if HUNDRED_MICRO_RES T2CON = 0b00000111; //1:16 prescaler PR2 = 74; #endif #if FIFTY_MICRO_RES T2CON = 0b00000101; //1:4 prescaler PR2 = 149; #endif #endif #if CLK_64MHZ T2CON = 0b00000111; #if HUNDRED_MICRO_RES PR2 = 99; #endif #if FIFTY_MICRO_RES PR2 = 49; #endif #endif //Timer0 Setup #if CLK_20MHZ T0CON = 0b11000101; //1:64 prescaler #endif #if CLK_48MHZ T0CON = 0b11000110; //1:128 prescaler #endif #if CLK_64MHZ 72 T0CON = 0b11000110; //1:128 prescaler #endif ei(); // Global interrupts enabled //Setup USART (Serial Port) for 19200bps #if CLK_20MHZ SPBRG = 64; #endif #if CLK_48MHZ SPBRG = 155; #endif #if CLK_64MHZ SPBRG = 207; #endif BRGH = 1; //Set to high speed mode TX9 = 0; SYNC = 0; SPEN = 1; TXEN = 1; //enable transmitter CREN = 1; } void turnOnPorts() { //Dummy Data for Testing for (i = 0; i < 24; i++) { if (i % 2 == 0) { rData.onTime[i] = 60000; } else { rData.onTime[i] = 30000; } } //initialize variables count = 0; toWriteA = 0x0f; toWriteB = 0xff; toWriteC = 0x0f; toWriteD = 0xff; count = 0; //Loop through onTime data. If any is = 0, //indicate that pin should be turned off. while (count < 24) { if(rData.onTime[count] == 0) { if (count < 8) { toWriteB -= (1<= 8 && count < 16) { toWriteD -= (1<<(count-8)); } if (count >= 16 && count < 20) { //PORTA0-PORTA3 toWriteA -= (1<<(count-16)); } if (count >= 20) { //PORTC0-PORTC3 toWriteC -= (1<<(count-20)); } } count++; } //Keep track of how many are turned off already for (i = 0; i < 24; i++) { if (rData.onTime[i] == 0x0000) { numSet++; } } //Write the calculated values to the ports PORTA = toWriteA; PORTB = toWriteB; PORTC = toWriteC; PORTD = toWriteD; } void turnOffPorts() { //Keep track of performance for debugging and optimizations pData.num_loops = 0; numSet = 0; //Initialize toWriteA = 0x0f; toWriteB = 0xff; toWriteC = 0x0f; toWriteD = 0xff; //About 1uS has passed since the ports were initialized. //Set to 0 for performance testing with the stopwatch function. TMR2 = 0; fifty_micros = 0; 74 //Performance sensitive loop while(numSet < 24) { count = 0; while (count < 24) { if((rData.onTime[count] != 0x0000) && (rData.onTime[count] <= fifty_micros)) { numSet++; rData.onTime[count] = 0x0000; if (count < 8) { toWriteB -= (1<= 8 && count < 16) { toWriteD -= (1<<(count-8)); } if (count >= 16 && count < 20) { //PORTA0-PORTA3 toWriteA -= (1<<(count-16)); } if (count >= 20) { //PORTC0-PORTC3 toWriteC -= (1<<(count-20)); } } count++; } PORTA = PORTA & toWriteA; PORTB = PORTB & toWriteB; PORTC = PORTC & toWriteC; PORTD = PORTD & toWriteD; pData.num_loops++; //performance measure. } //Comments regarding different optimizations tried //START w/ nested for loops: //t = 15.3014mS, micros 15400, shifter=74 // //ONE LOOP: //t = 15.328, micros 15400, shifter = 79 //WITH 2 SUBPORTS and nested ifs //t = 15.185, micros 15400, shifter = 78 //WITH 2 subport and non-nested ifs //t=15.249, micros 15300, shifter = 78 //WITH 2 subport and non-nested ifs and count shifter instead of j //t=15.324, micros 15400, shifter = 79 pData.micros = micros; //48MHZ = 263 loops, 15.2466s = 0.05797ms 75 //64MHZ = 351 loops, 15.1642s = 0.0432ms //64MHZ = 347 loops, 15.1359s = 0.0436ms @ 50uS resolution //64MHZ = 413 loops, 15.1479s = 0.0367ms @ 50uS and changing // fifty_micros variable to int rather than long //48MHZ = 308 loops, 15.1769s = 0.0492ms @ 50uS and // the int rather than long } //Serial USART functions -- grabbed from Hi-Tech C examples folder void putch(unsigned char byte) { /* output one byte */ while(!TXIF) /* set when register is empty */ continue; TXREG = byte; } unsigned char getch() { /* retrieve one byte */ while(!RCIF) /* set when register is not empty */ continue; return RCREG; } //Writes contents of rData to the serial port void writeRowDataInfo() { p = &rData; i = 0; while(i < sizeof(rData)) { putch(*p); i++; p++; } } void writePerformanceData() { p = &pData; i = 0; while(i < sizeof(pData)) { putch(*p); i++; p++; } } //Reads serial port to fill in rData void readRowDataInfo() { putch(0x06); //Send ACK character so that PC knows to start sending data. p = &rData; i = 0; while(i < sizeof(rData)) { *p = getch(); p++; i++; } } main() { //Initializes ports, timers, usart init(); //Turn off timer2, turn on timer0, clear micros counter TMR0ON = 1; TMR0IE = 1; TMR2ON = 0; TMR2IE = 0; TMR0 = 0; //Reset timer 0 while(1) { micros = 0; //Read next row from serial port readRowDataInfo(); //Turn on all pins required turnOnPorts(); //Turn off pins when the timing is correct (performance sensitive area) turnOffPorts(); //Wait for offTime period while (micros < rData.offTime) { //busy loop } } }