It is running in an interrupt, thus loop() is still called all the time.
[s]I'll release the source once i clean it a bit out, I'm having an issue with inline assembly mentioned here: viewtopic.php?f=8&t=3040#p8673 [/s] (come on, no stroke tag?!?!)
Have the source here:
Please keep in mind that it is my first AVR program with asm and my second AVR program altogether, so if you see any obvious optimization mistakes, please tell me! Actually any peephole optimizations are welcome, hehe.
Also I didn't put a lot of effort into the moving square parts. The frontBuf and backBuf are pre-filled with the gradient, if you just use the moving squares you wouldn't have to pre-fill them.
- Code: Select all
// Gamebuino full-screen four-level-grey-scale rendering demo - by Sorunome ( http://www.sorunome.de )
//
// The interrupt wrapper and LCD initilizer are based off of http://gamebuino.com/wiki/index.php?title=Grey-scale_rendering
#include <TimerOne.h>
// how often to update the screen, 42 seems to be the magic number to minimize flicker
#define FRAME_RATE 42
#define PIN_SCE A1
#define PIN_RESET A0
#define PIN_DC A2
#define PIN_SDIN 11
#define PIN_SCLK 13
#define PIN_BACKLIGHT 5
#define PORT_SCLK PORTB
#define SCLK_BIT 5
#define PORT_SDIN PORTB
#define SDIN_BIT 3
#define LCD_C LOW
#define LCD_D HIGH
#define LCD_X 84
#define LCD_Y 48
#define LCD_CMD 0
// the interrupt will need both buffers, frontBuf and backBuf to update the LCD
// the following table will get you the result for each pixel:
//
// frontBuf | backBuf | Result
// ---------+---------+------------
// 0 | 0 | white
// ---------+---------+------------
// 0 | 1 | light grey
// ---------+---------+------------
// 1 | 0 | dark grey
// ---------+---------+------------
// 1 | 1 | black
byte frontBuf[LCD_X*LCD_Y/8] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};
byte backBuf[LCD_X*LCD_Y/8] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};
void LcdClear(void) {
for (int index = 0; index < LCD_X * LCD_Y / 8; index++)
{
LcdWrite(LCD_D, 0x00);
}
}
void LcdInitialise(void) {
pinMode(PIN_SCE, OUTPUT);
pinMode(PIN_RESET, OUTPUT);
pinMode(PIN_DC, OUTPUT);
pinMode(PIN_SDIN, OUTPUT);
pinMode(PIN_SCLK, OUTPUT);
digitalWrite(PIN_RESET, LOW);
digitalWrite(PIN_RESET, HIGH);
LcdWrite( LCD_CMD, 0x21 ); // LCD Extended Commands.
LcdWrite( LCD_CMD, 0xB9 ); // Set LCD Vop (Contrast). //B1
LcdWrite( LCD_CMD, 0x04 ); // Set Temp coefficent. //0x04
LcdWrite( LCD_CMD, 0x14 ); // LCD bias mode 1:48. //0x13
LcdWrite( LCD_CMD, 0x0C ); // LCD in normal mode. 0x0d for inverse
LcdWrite(LCD_C, 0x20);
LcdWrite(LCD_C, 0x0C);
}
void LcdWrite(byte dc, byte data) {
digitalWrite(PIN_DC, dc);
digitalWrite(PIN_SCE, LOW);
shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data);
digitalWrite(PIN_SCE, HIGH);
}
byte rotFlag = -2;
void callback() {
digitalWrite(PIN_DC, LCD_D);
digitalWrite(PIN_SCE, LOW);
// X - front buffer
// Y - back buffer
// Z - cycle pointer
asm volatile(
"ldi R18,219\n\t"
"clc\n\t"
"inc %[rotFlagReg]\n\t"
"breq doneWithInitOffset\n\t"
"rol R18\n\t"
"mov R20,%[rotFlagReg]\n\t"
"inc R20\n\t"
"breq doneWithInitOffset\n\t"
"rol R18\n\t"
"ldi %[rotFlagReg],-2\n"
"doneWithInitOffset:\n\t"
//this is where the jumps go
//R18 together with the carry flag is the "9-bit" register for the mask, it works well as the loop doesn't modify carry
"ldi R21,84\n"
"Loop_Outer:\n\t"
"ldi R22,6\n"
"Loop_Inner:\n\t"
"ld R16,Y+\n\t"
"ld R17,X+\n\t"
"ror R18\n\t"
"eor R16,R17\n\t"
"and R16,R18\n\t"
"eor R16,R17\n\t"
// now ouptut R16 to the screen start
"SBRC R16,7\n\t"
"rjmp BitSkip0_1\n\t"
"cbi 5,3\n\t"
"rjmp BitSkip0_2\n"
"BitSkip0_1:\n\t"
"sbi 5,3\n\t"
"BitSkip0_2:\n\t"
"sbi 5,5\n\t"
"cbi 5,5\n\t"
"SBRC R16,6\n\t"
"rjmp BitSkip1_1\n\t"
"cbi 5,3\n\t"
"rjmp BitSkip1_2\n"
"BitSkip1_1:\n\t"
"sbi 5,3\n\t"
"BitSkip1_2:\n\t"
"sbi 5,5\n\t"
"cbi 5,5\n\t"
"SBRC R16,5\n\t"
"rjmp BitSkip2_1\n\t"
"cbi 5,3\n\t"
"rjmp BitSkip2_2\n"
"BitSkip2_1:\n\t"
"sbi 5,3\n\t"
"BitSkip2_2:\n\t"
"sbi 5,5\n\t"
"cbi 5,5\n\t"
"SBRC R16,4\n\t"
"rjmp BitSkip3_1\n\t"
"cbi 5,3\n\t"
"rjmp BitSkip3_2\n"
"BitSkip3_1:\n\t"
"sbi 5,3\n\t"
"BitSkip3_2:\n\t"
"sbi 5,5\n\t"
"cbi 5,5\n\t"
"SBRC R16,3\n\t"
"rjmp BitSkip4_1\n\t"
"cbi 5,3\n\t"
"rjmp BitSkip4_2\n"
"BitSkip4_1:\n\t"
"sbi 5,3\n\t"
"BitSkip4_2:\n\t"
"sbi 5,5\n\t"
"cbi 5,5\n\t"
"SBRC R16,2\n\t"
"rjmp BitSkip5_1\n\t"
"cbi 5,3\n\t"
"rjmp BitSkip5_2\n"
"BitSkip5_1:\n\t"
"sbi 5,3\n\t"
"BitSkip5_2:\n\t"
"sbi 5,5\n\t"
"cbi 5,5\n\t"
"SBRC R16,1\n\t"
"rjmp BitSkip6_1\n\t"
"cbi 5,3\n\t"
"rjmp BitSkip6_2\n"
"BitSkip6_1:\n\t"
"sbi 5,3\n\t"
"BitSkip6_2:\n\t"
"sbi 5,5\n\t"
"cbi 5,5\n\t"
"SBRC R16,0\n\t"
"rjmp BitSkip7_1\n\t"
"cbi 5,3\n\t"
"rjmp BitSkip7_2\n"
"BitSkip7_1:\n\t"
"sbi 5,3\n\t"
"BitSkip7_2:\n\t"
"sbi 5,5\n\t"
"cbi 5,5\n\t"
// now ouptut R16 to the screen end
"dec R22\n\t"
"breq InnerLoopExit\n\t"
"jmp Loop_Inner\n"
"InnerLoopExit:\n\t"
"dec R21\n\t"
"breq OuterLoopExit\n\t"
"jmp Loop_Outer\n"
"OuterLoopExit:\n\t"
:[rotFlagReg] "+r" (rotFlag):"x" (backBuf),"y" (frontBuf):);
digitalWrite(PIN_SCE, HIGH);
}
class Square {
int x,y,xDir,yDir;
byte c;
public:
Square(int gx,int gy,int gxDir,int gyDir,byte gc){
x = gx;
y = gy;
xDir = gxDir;
yDir = gyDir;
c = gc;
}
void update(){
x += xDir;
if(x < 0){
xDir = 1;
x = 0;
}else if(x > LCD_X-8){
xDir = -1;
x = LCD_X-8;
}
y += yDir;
if(y < 0){
yDir = 1;
y = 0;
}else if(y > LCD_Y-8){
yDir = -1;
y = LCD_Y-8;
}
//now draw it
switch(c){
case 1:
for(byte i = x;i < x+8;i++){
for(byte j = y;j < y+8;j++){ // i;j
frontBuf[i + (j / 8) * 84] |= _BV(j % 8);
}
}
break;
case 2:
for(byte i = x;i < x+8;i++){
for(byte j = y;j < y+8;j++){ // i;j
backBuf[i + (j / 8) * 84] |= _BV(j % 8);
}
}
break;
case 3:
for(byte i = x;i < x+8;i++){
for(byte j = y;j < y+8;j++){ // i;j
frontBuf[i + (j / 8) * 84] |= _BV(j % 8);
backBuf[i + (j / 8) * 84] |= _BV(j % 8);
}
}
break;
}
}
};
Square *squares[6];
#define numSquares 6
void setup(void) {
LcdInitialise();
LcdClear();
pinMode(PIN_BACKLIGHT, OUTPUT);
digitalWrite(PIN_BACKLIGHT, HIGH);
Timer1.initialize(1000000/FRAME_RATE); // initialize timer1, and set a 1/2 second period
Timer1.pwm(9, 512); // setup pwm on pin 9, 50% duty cycle
Timer1.attachInterrupt(callback); // attaches callback() as a timer overflow interrupt
squares[0] = new Square(10,10,-1,-1,1);
squares[1] = new Square(50,5,1,-1,2);
squares[2] = new Square(30,17,1,1,1);
squares[3] = new Square(0,0,1,1,2);
squares[4] = new Square(3,20,-1,1,3);
squares[5] = new Square(42,21,-1,-1,2);
}
uint32_t nextFrameMillis;
#define timePerFrame 50
void loop(void) {
// loop still gets called, and the LCD is being updated via an interrupt
// so put your game code here and update the display buffers at whatever
// frame rate you like
if(((nextFrameMillis - millis()) > timePerFrame) && rotFlag==0) {
nextFrameMillis = millis() + timePerFrame;
memset(frontBuf, 0, LCD_X * LCD_Y / 8);
memset(backBuf, 0, LCD_X * LCD_Y / 8);
for(byte i = 0;i < numSquares;i++){
squares[i]->update();
}
}
}