Skip to content
Permalink
Browse files

Clean start

Program Memory Usage  : 3048 bytes   74.4 % Full
Data Memory Usage   : 79 bytes   30.9 % Full
Time in Refresh (min/max): 153us/174us

Starting with very clean and readable, but non-optimized code.
  • Loading branch information
bigjosh committed Nov 30, 2013
1 parent 799e737 commit e504f0ca18015cded76e5900ab0f04d0d8292831
Showing with 63 additions and 74 deletions.
  1. +63 −74 Atmel Studio/Candle0005.c
@@ -180,9 +180,7 @@ static inline void nextFrame(void) {
// Time to display the next frame in the animation...
// copy the next frame from program memory (candel_bitstream[]) to the RAM frame buffer (fda[])





static byte const *candleBitstremPtr; // next byte to read from the bitstream in program memory

static byte workingByte; // current working byte
@@ -267,54 +265,55 @@ static inline void nextFrame(void) {

static byte const rowDirectionBits = 0b01010101; // 0=row goes low, 1=Row goes high

static byte const portBRowBits[ROWS] = {_BV(0),_BV(0),_BV(2),_BV(2),_BV(4),_BV(4),_BV(6),_BV(6) };
PROGMEM static byte const portBRowBits[ROWS] = {_BV(0),_BV(0),_BV(2),_BV(2),_BV(4),_BV(4),_BV(6),_BV(6) };
PROGMEM static byte const portDRowBits[ROWS] = { 0, 0, 0, 0, 0, 0, 0, 0 };

#ifndef ALL_PORTD_ROWS_ZERO
static byte const portDRowBits[ROWS] = { 0, 0, 0, 0, 0, 0, 0, 0 };
#endif

// Note that col is always opposite of row so we don't need colDirectionBits

static byte const portBColBits[COLS] = {_BV(7),_BV(5),_BV(3), _BV(1), 0};
static byte const portDColBits[COLS] = { 0, 0, 0, 0,_BV(6)};
PROGMEM static byte const portBColBits[COLS] = {_BV(7),_BV(5),_BV(3), _BV(1), 0};
PROGMEM static byte const portDColBits[COLS] = { 0, 0, 0, 0,_BV(6)};


#define REFRESH_PER_FRAME ( REFRESH_RATE / FRAME_RATE ) // How many refreshes before we trigger the next frame to be drawn?

byte refreshCount =REFRESH_PER_FRAME+1;




// Do a single full screen refresh
// call nextframe() to decode next frame into buffer afterwards if it is time
// This version will work with any combination of row/col bits

static inline void refreshScreen(void)


static inline void refreshScreenClean(void)
{

#ifdef TIMECHECK
#ifdef TIMECHECK
DDRA = _BV(0)|_BV(1); // Set PORTA0 for output. Use OR because it compiles to single SBI instruction
PORTA |=_BV(0); // twiddle A0 bit for oscilloscope timing
#endif
byte *fdaptr = fda; // Where are we in scanning through the FDA?

byte rowDirectionBitsRotating = rowDirectionBits; // Working space for rowDirections bits that we shift down once for each row
// Bit 0 will be bit for the current row.
// TODO: in ASM, would could shift though the carry flag and jmp based on that and save a bit test
// Bit 0 will be bit for the current row.
// TODO: in ASM, would could shift though the carry flag and jmp based on that and save a bit test



for( byte int_y = 0 ; int_y < FDA_Y_MAX ; int_y++ ) {

byte portBRowBitsCache = portBRowBits[int_y];
byte portBRowBitsCache = pgm_read_byte_near(portBRowBits+int_y);

byte portDRowBitsCache = pgm_read_byte_near(portDRowBits+int_y);

#ifndef ALL_PORTD_ROWS_ZERO
byte portDRowBitsCache = portDRowBits[int_y];
#endif

for( byte int_x = 0 ; int_x < FDA_X_MAX ; int_x++) {
// get the brightness of the current LED

register byte b = *( fdaptr++ ); // Want this in a register because later we will loop on it and want the loop entrance to be quick
// If the LED is off, then don't need to do anything since all LEDs are already off all the time except for a split second inside this routine....

if (b>0) {
@@ -328,92 +327,82 @@ static inline void refreshScreen(void)

PORTB = portBRowBitsCache;

#ifndef ALL_PORTD_ROWS_ZERO
PORTD = portDRowBitsCache;
#else
PORTD = 0;
#endif
PORTD = portDRowBitsCache;

// Only need to set the correct bits in PORTB and PORTD to drive the row high (col bit will get set to 0)

ddrbt = portBRowBitsCache | portBColBits[int_x] ; // enable output for the Row pins to drive high, also enable output for col pins which are zero so will go low

#ifndef ALL_PORTD_ROWS_ZERO
ddrdt = portDRowBitsCache | portDColBits[int_x] ;
#else
ddrdt = portDColBits[int_x];
#endif
ddrbt = portBRowBitsCache | pgm_read_byte_near(portBColBits+int_x) ; // enable output for the Row pins to drive high, also enable output for col pins which are zero so will go low

ddrdt = portDRowBitsCache | pgm_read_byte_near(portDColBits+int_x) ;

} else { // row goes low, cols go high....

PORTB = portBColBits[int_x];
PORTD = portDColBits[int_x];
PORTB = pgm_read_byte_near(portBColBits+int_x);
PORTD = pgm_read_byte_near(portDColBits+int_x);

ddrbt = PORTB | portBRowBitsCache; // enable output for the col pins to drive high, also enable output for row pins which are zero so will go low

#ifndef ALL_PORTD_ROWS_ZERO
ddrdt = PORTD | portDRowBitsCache;
#else
ddrdt = PORTD;
#endif

ddrdt = PORTD | portDRowBitsCache;

}

// Now comes the business of Actually turning on the LED.
// This is the tightest loop possible - I can't figure out how to do it in C
// Now comes the business of Actually turning on the LED.

// This is the tightest loop possible - I can't figure out how to do it in C

//while (b--) asm volatile (""); //does not work because it generates a strange RJMP +0 loop preamble

//_delay_loop_1( b ); // DOes not work becuase it pathalogically loads b into a register



asm volatile (

// TODO: Do actual visual test to make sure that actual brightness is linear and smooth with this algorthim
// Might not be because this does not take into account delay of loop branching. It would take at least 5 lines of
// code to get this timing exactly right by special casing out b=1 and b=2, and then dividing higher numbers to account for the branch cost
// would be better to have this already accounted into the precomputed brightness->dutycycle map.
// TODO: Do actual visual test to make sure that actual brightness is linear and smooth with this algorthim
// Might not be because this does not take into account delay of loop branching. It would take at least 5 lines of
// code to get this timing exactly right by special casing out b=1 and b=2, and then dividing higher numbers to account for the branch cost
// would be better to have this already accounted into the precomputed brightness->dutycycle map.

"OUT %0,%1 \n\t" // DO DDRD first because in current config it will never actually have both pins so LED can't turn on (not turn of DDRB)
"OUT %2,%3 \n\t" // Ok, LED is on now!
"L_%=:dec %4 \n\t"
// "BRNE L_%= \n\t"
"OUT %2,__zero_reg__ \n\t" // Do DDRB first since this will definitely turn off the LED
"OUT %0,__zero_reg__ \n\t"

: : "I" (_SFR_IO_ADDR(DDRD)) , "r" (ddrdt) , "I" (_SFR_IO_ADDR(DDRB)) , "r" (ddrbt) , "r" (b)

"OUT %0,%1 \n\t" // DO DDRD first because in current config it will never actually have both pins so LED can't turn on (not turn of DDRB)
"OUT %2,%3 \n\t" // Ok, LED is on now!
"L_%=:dec %4 \n\t"
"BRNE L_%= \n\t"
"OUT %2,__zero_reg__ \n\t" // Do DDRB first since this will definitely turn off the LED
"OUT %0,__zero_reg__ \n\t"

: : "I" (_SFR_IO_ADDR(DDRD)) , "r" (ddrdt) , "I" (_SFR_IO_ADDR(DDRB)) , "r" (ddrbt) , "r" (b)

);

}
}

rowDirectionBitsRotating >>= 1; // Shift bits down so bit 0 has the value for the next row

}

#ifdef TIMECHECK
PORTA &= ~_BV(0);
#endif


refreshCount--;

if (refreshCount == 0 ) { // step to next frame in the animation sequence?

refreshCount=REFRESH_PER_FRAME+1;
// TODO: this diagnostic screen generator costs 42 bytes. Can we make it smaller or just get rid of it?
nextFrame();
refreshCount=REFRESH_PER_FRAME+1;
// TODO: this diagnostic screen generator costs 42 bytes. Can we make it smaller or just get rid of it?
nextFrame();
}

}



void init0 (void) __attribute__ ((naked)) __attribute__ ((section (".init0")));

// This code will be run immedeately on reset, before any initilization or main()
@@ -473,7 +462,7 @@ int main(void)

static inline void userWakeRoutine(void) {

refreshScreen();
refreshScreenClean();

}

0 comments on commit e504f0c

Please sign in to comment.
You can’t perform that action at this time.