Saturday, November 10, 2012

Simple LiPo / Li-Ion Charger



Disclaimer: Charging LiPo / Li-Ion batteries is dangerous! Better use a comercial professional charger. If you choose to build the charger described here, use it at your own risk.

To charge a  LiPo / Li-Ion cell, it is supplied with constant current until it reaches 4.2V (depends on cell, usually 4.2V). This is what the circuit does: the current is limited to 0.7/Rc, where 0.7 is the Vbe of BC337, and the voltage limit is adjustable from the potentiometer (with a voltmeter connected instead of battery). LED1 indicates if the battery polarity is OK. LED2 indicates if the battery is charged. The transistor D882 dissipates the unused power, so it needs a radiator. It is a good idea to place the transistors close to each other, because at high temperature the Vbe of BC337 will decrease, so the current will decrease. The diode is used for protecting the circuit from reverse polarity of the cell. Almost any high-current diode can be used.

Friday, November 2, 2012

PIC18F4550 Snake Game on 128x64 GLCD v2



much better graphics :) enjoy!



source code: (PIC18F4550, CCS C)

file kbd.h:

#define keyUp PIN_B2
#define keyDown PIN_B3
#define keyLeft PIN_B4
#define keyRight PIN_B5
#define keyA PIN_B6
#define keyB PIN_B7

#define keyUpPressed !input(keyUp)
#define keyDownPressed !input(keyDown)
#define keyLeftPressed !input(keyLeft)
#define keyRightPressed !input(keyRight)
#define keyAPressed !input(keyA)
#define keyBPressed !input(keyB)











file glcd.c:

#ifndef GLCD_C
#define GLCD_C

#define ON  1
#define OFF 0

#define YES 1
#define NO  0


// GLCD PIN CONFIGURATION:

#bit GLCD_CS1 = porte.1
#bit GLCD_CS2 = porte.2
#bit GLCD_DI = portc.0
#bit GLCD_RW = porte.0
#bit GLCD_E  = portc.1

#byte GLCD_DATA = portd 

#bit tris_GLCD_CS1 = trise.1
#bit tris_GLCD_CS2 = trise.2
#bit tris_GLCD_DI = trisc.0
#bit tris_GLCD_RW = trise.0
#bit tris_GLCD_E  = trisc.1

#byte tris_GLCD_DATA = trisd 

////////////////////////////



// IO functions:

void glcdPulseE() {
   GLCD_E = 1;      
   delay_us(10);     // for more speed improve the data bus to glcd
   GLCD_E = 0;
}

void glcdWrite1(unsigned int8 data) {
   GLCD_CS1 = 1;
   GLCD_RW = 0;       // R/W = Write
   GLCD_DATA = data;
   tris_GLCD_DATA = 0;
   GLCD_DATA = data;
   glcdPulseE();
   GLCD_CS1 = 0;
}
void glcdWrite2(unsigned int8 data) {
   GLCD_CS2 = 1;
   GLCD_RW = 0;       // R/W = Write
   GLCD_DATA = data;
   tris_GLCD_DATA = 0;
   GLCD_DATA = data;
   glcdPulseE();
   GLCD_CS2 = 0;
}

unsigned int8 glcdRead1() {
   unsigned int8  data;    
   GLCD_CS1 = 1;
   tris_GLCD_DATA = 0xFF; // input
   GLCD_RW = 1;       // R/W = Read   
   GLCD_E = 1;
   delay_us(10);
   GLCD_E = 0;
   delay_us(10);
   GLCD_E = 1;      
   delay_us(10); 
   data = GLCD_DATA;       
   GLCD_E = 0;
   GLCD_CS1 = 0;
   return data;   
}
unsigned int8 glcdRead2() {
   unsigned int8  data;    
   GLCD_CS2 = 1;
   tris_GLCD_DATA = 0xFF; // input
   GLCD_RW = 1;       // R/W = Read  
   GLCD_E = 1;
   delay_us(10);
   GLCD_E = 0;
   delay_us(10);
   GLCD_E = 1;      
   delay_us(10); 
   data = GLCD_DATA;       
   GLCD_E = 0;
   GLCD_CS2 = 0;
   return data;   
}
/////////////////////////////////////////


void glcdFillScreen(unsigned int8 color) {
   int x, y;
  
   if(color!=0) color = 0xFF;

   for(x=0; x<8; x++) {  // for all 8 rows of the glcd
  
      GLCD_DI = 0;                      // D/I = Instruction
      glcdWrite1(0b01000000);     // start of the row
      glcdWrite2(0b01000000);
      glcdWrite1(x | 0b10111000); // Set row
      glcdWrite2(x | 0b10111000);
      GLCD_DI = 1;                     // D/I = Data

      for(y=0; y<64; y++) {   // fill columns of the row with pixels
         glcdWrite1(color);
         glcdWrite2(color);
      }
   }
}


void glcdInit(int1 active) {  // 1 = on, 0 = off

   GLCD_E = 0;
   tris_GLCD_E = 0;
   GLCD_E = 0;
  
   GLCD_CS1 = 0;
   tris_GLCD_CS1 = 0;
   GLCD_CS1 = 0;
  
   GLCD_CS2 = 0;
   tris_GLCD_CS2 = 0;
   GLCD_CS2 = 0;
  
   GLCD_DI = 0;
   tris_GLCD_DI = 0;
   GLCD_DI = 0;       // D/I = Instruction
  
   GLCD_RW = 0;
   tris_GLCD_RW = 0; 
   GLCD_RW = 0;       // R/W = Write
  
              
   glcdWrite1(0xC0);
   glcdWrite2(0xC0);
   glcdWrite1(0x40);
   glcdWrite2(0x40);
   glcdWrite1(0xB8);
   glcdWrite2(0xB8);
  
  
   glcdWrite1(0x3E + active);  // turn on or off
   glcdWrite2(0x3E + active);
  
   glcdFillScreen(OFF);
}

void glcdClear() {
   glcdFillScreen(OFF);
}

#endif











file main.c:

#include "18F4550.h"
#DEVICE PASS_STRINGS = IN_RAM
#fuses HSPLL, PLL5, CPUDIV1, NOWDT, NOPROTECT, PUT, BORV43, NOLVP, NOMCLR, USBDIV, VREGEN, WRT=14, WRTB

#use delay(clock=48000000)

#zero_ram

#byte porta = 0xF80
#byte portb = 0xF81
#byte portc = 0xF82
#byte portd = 0xF83
#byte porte = 0xF84

#byte trisa = 0xF92
#byte trisb = 0xF93
#byte trisc = 0xF94
#byte trisd = 0xF95
#byte trise = 0xF96


#byte ucon  = 0xF6D
#bit usben = ucon.3

#byte ucfg  = 0xF6F
#bit utrdis = ucfg.3


#include "kbd.h"
#include "glcd.c"




void cpu_init() {

   usben = 0;
   utrdis = 1;
  
   setup_comparator(NC_NC_NC_NC);
   SETUP_ADC_PORTS(NO_ANALOGS);
   SETUP_ADC(ADC_OFF);
  
   port_b_pullups(true);
  
}



unsigned int32 _Randseed;

unsigned int16 rand(void) {
   _Randseed = _Randseed * 1103515245 + 12345;
   return ((unsigned int16)(_Randseed >> 16) );
}

void srand(unsigned int32 seed) {
   _Randseed = seed;
}


#define CODE_EMPTY '.'
#define CODE_FOOD 3
#define CODE_SNAKE 219



#define OFFSET_FOOD 0

#define OFFSET_SNVE 8
#define OFFSET_SNHO 16

#define OFFSET_SNDL 24
#define OFFSET_SNUL 32
#define OFFSET_SNUR 40
#define OFFSET_SNDR 48

#define OFFSET_SNHU 56
#define OFFSET_SNHR 64
#define OFFSET_SNHD 72
#define OFFSET_SNHL 80

#define OFFSET_SNTU 88
#define OFFSET_SNTR 96
#define OFFSET_SNTD 104
#define OFFSET_SNTL 112



const unsigned int8 BITMAP[] = {0b00011110,  // food
                                0b00100001,
                                0b01000001,
                                0b10000010,
                                0b10000010,
                                0b01000001,
                                0b00100001,
                                0b00011110,
                                    
                                0b00000000,  // snake_vertical
                                0b00000000,
                                0b11101110,
                                0b11101110,
                                0b01110111,
                                0b01110111,
                                0b00000000,
                                0b00000000,
                                              
                                0b00001100,  // snake_horizontal
                                0b00111100,
                                0b00111100,
                                0b00110000,
                                0b00001100,
                                0b00111100,
                                0b00111100,
                                0b00110000,    
                                                
                                0b00001100,  // snake_down_left
                                0b00111100,
                                0b11111100,
                                0b11101000,
                                0b01111000,
                                0b01100000,
                                0b00000000,
                                0b00000000,
                                               
                                0b00001100,  // snake_up_left
                                0b00111100,
                                0b00111110,
                                0b00010110,
                                0b00011111,
                                0b00000111,
                                0b00000000,
                                0b00000000,
                                               
                                0b00000000,  // snake_up_right
                                0b00000000,
                                0b00000110,
                                0b00011110,
                                0b00010111,
                                0b00111111,
                                0b00111100,
                                0b00110000,
                                                 
                                0b00000000,  // snake_down_right
                                0b00000000,
                                0b11100000,
                                0b11111000,
                                0b01101000,
                                0b01111100,
                                0b00111100,
                                0b00110000,
                               
                                0b00000000,  // snake_head_up   // link up, head looks down
                                0b01110000,
                                0b10011110,
                                0b11111110,
                                0b11111111,
                                0b10011111,
                                0b01110000,
                                0b00000000,
                               
                                0b00111100,  // snake_head_right  // link right
                                0b01011010,
                                0b01011010,
                                0b01111110,
                                0b00111100,
                                0b00111100,
                                0b00111100,
                                0b00110000,
                               
                                0b00000000,  // snake_head_down  // link down
                                0b00001110,
                                0b11111001,
                                0b11111111,
                                0b01111111,
                                0b01111001,
                                0b00001110,
                                0b00000000,
                               
                                0b00001100,  // snake_head_left  // link left
                                0b00111100,
                                0b00111100,
                                0b00111100,
                                0b01111110,
                                0b01011010,
                                0b01011010,
                                0b00111100,
                               
                               
                               
                               
                                0b00000000,  // snake_tail_up   // link up
                                0b00000000,
                                0b00101110,
                                0b11101110,
                                0b11110111,
                                0b00110111,
                                0b00000000,
                                0b00000000,
                               
                                0b00011000,  // snake_tail_right  // link right
                                0b00011000,
                                0b00111100,
                                0b00110000,
                                0b00001100,
                                0b00111100,
                                0b00111100,
                                0b00110000,
                               
                                0b00000000,  // snake_tail_down  // link down
                                0b00000000,
                                0b11101100,
                                0b11101111,
                                0b01110111,
                                0b01110100,
                                0b00000000,
                                0b00000000,
                               
                                0b00001100,  // snake_tail_left  // link left
                                0b00111100,
                                0b00111100,
                                0b00110000,
                                0b00001100,
                                0b00111100,
                                0b00011000,
                                0b00011000};


unsigned int8 arena[16][8];     // x, y


int snakeDir = 1;


unsigned int8 snakeX[100];
unsigned int8 snakeY[100];
unsigned int8 snakeLen = 3;


unsigned int8 snakeBitmapOffset(unsigned int8 x, unsigned int8 y) {
   unsigned int8 i;
  
   i = 0;
   while(true) {
      if(snakeX[i]==x && snakeY[i]==y) break;
      i++;
   }
  
   if(i==0) {
      if(snakeX[i+1]==x+1) return OFFSET_SNHR;
      if(snakeX[i+1]==x-1) return OFFSET_SNHL;
      if(snakeY[i+1]==y+1) return OFFSET_SNHD;
      if(snakeY[i+1]==y-1) return OFFSET_SNHU;
   }
  
   if(i==snakeLen-1) {
      if(snakeX[i-1]==x+1) return OFFSET_SNTR;
      if(snakeX[i-1]==x-1) return OFFSET_SNTL;
      if(snakeY[i-1]==y+1) return OFFSET_SNTD;
      if(snakeY[i-1]==y-1) return OFFSET_SNTU;
   }
  
   if(snakeX[i+1]==x && snakeX[i-1]==x) return OFFSET_SNVE;
   if(snakeY[i+1]==y && snakeY[i-1]==y) return OFFSET_SNHO;
  
  
   if(snakeX[i+1]==x+1 && snakeY[i-1]==y+1) return OFFSET_SNDR;
   if(snakeX[i-1]==x+1 && snakeY[i+1]==y+1) return OFFSET_SNDR;
  
   if(snakeX[i+1]==x-1 && snakeY[i-1]==y+1) return OFFSET_SNDL;
   if(snakeX[i-1]==x-1 && snakeY[i+1]==y+1) return OFFSET_SNDL;
  
   if(snakeX[i+1]==x+1 && snakeY[i-1]==y-1) return OFFSET_SNUR;
   if(snakeX[i-1]==x+1 && snakeY[i+1]==y-1) return OFFSET_SNUR;
  
   if(snakeX[i+1]==x-1 && snakeY[i-1]==y-1) return OFFSET_SNUL;
   if(snakeX[i-1]==x-1 && snakeY[i+1]==y-1) return OFFSET_SNUL;
  
   return OFFSET_SNVE;
}



void glcdUpdate() {
   unsigned int8 x, y, code, i;
   unsigned int8 bitmapOffset;
  
   for(y = 0; y < 8; y++) {
  
      GLCD_DI = 0;                 
      glcdWrite1(0b01000000);   
      glcdWrite2(0b01000000);
      glcdWrite1(y | 0b10111000);
      glcdWrite2(y | 0b10111000);
      GLCD_DI = 1;              
     
      for(x=0; x<16; x++) {
         code = arena[x][y];
        
         if(code==CODE_SNAKE) {
            bitmapOffset = snakeBitmapOffset(x, y);
            if(x<8)
               for(i=0; i<8; i++)
                  glcdWrite1(BITMAP[bitmapOffset+i]);             
            else
               for(i=0; i<8; i++)
                  glcdWrite2(BITMAP[bitmapOffset+i]);
         }/////////////////////////
         if(code==CODE_EMPTY) {
            if(x<8)
               for(i=0; i<8; i++)
                  glcdWrite1(0x00);             
            else
               for(i=0; i<8; i++)
                  glcdWrite2(0x00);
         }/////////////////////////
         if(code==CODE_FOOD) {
            if(x<8)
               for(i=0; i<8; i++)
                  glcdWrite1(BITMAP[OFFSET_FOOD+i]);
            else
               for(i=0; i<8; i++)
                  glcdWrite2(BITMAP[OFFSET_FOOD+i]);
         }/////////////////////////
        
      }
    
   }
  
  
  
}
  
     

unsigned int8 score = 0;


void arena_clear() {
    int x, y;
    for(x=0; x<16; x++)
        for(y=0; y<8; y++)
            arena[x][y] = CODE_EMPTY;
}


void put_food() {
    unsigned int8 x, y;
    while(true) {
        x = rand() % 16;
        y = rand() % 8;
        if(arena[x][y]!=CODE_SNAKE) {
            arena[x][y] = CODE_FOOD;
            break;
        }
    }
}


unsigned int8 move_snake() {
    signed int8 tail_x, tail_y, head_x, head_y;
    unsigned int8 i;
   
    tail_x = snakeX[snakeLen-1];
    tail_y = snakeY[snakeLen-1];
   
   
    for(i=snakeLen-1; i>0; i--) {
        snakeX[i] = snakeX[i-1];
        snakeY[i] = snakeY[i-1];
    }
   
    head_x = snakeX[0];
    head_y = snakeY[0];
   
    switch(snakeDir) {
        case 0: head_y--; break;
        case 1: head_x++; break;
        case 2: head_y++; break;
        case 3: head_x--; break;
    }
   
    snakeX[0] = head_x;
    snakeY[0] = head_y;
   
    if(head_x < 0)
        return -1;
   
    if(head_y < 0)
        return -1;
       
    if(head_x >= 16)
        return -1;
       
    if(head_y >= 8)
        return -1;
       
    if(arena[head_x][head_y] != CODE_FOOD) {  // nothing eaten
        arena[tail_x][tail_y] = CODE_EMPTY;  // cut tail
    }
       
    if(arena[head_x][head_y] == CODE_FOOD) {
        snakeX[snakeLen] = tail_x;
        snakeY[snakeLen] = tail_y;
        snakeLen++;
        arena[head_x][head_y] = CODE_SNAKE;  // add head
        return 2;
    }
   
   
    arena[head_x][head_y] = CODE_SNAKE;  // add head 
    return 0;
   
}
 


int1 refreshSnakeDir() {
   unsigned int8 oldDir;
   oldDir = snakeDir;
   if(keyUpPressed) snakeDir = 0;
   if(keyRightPressed) snakeDir = 1;
   if(keyDownPressed) snakeDir = 2;
   if(keyLeftPressed) snakeDir = 3;
   if(oldDir!=snakeDir)
      return 1;
   else
      return 0;
}
    


void main() {

   unsigned int8 i;
   unsigned int8 res;
   unsigned int1 dirChanged;
  
   cpu_init();
  
   delay_ms(100);
  
  
   glcdInit(ON);
  

   arena_clear();
   
   snakeY[0] = 0;
   snakeY[1] = 0;
   snakeY[2] = 0;
  
   snakeX[0] = 2;
   snakeX[1] = 1;
   snakeX[2] = 0;
  
  
   arena[0][0] = CODE_SNAKE;
   arena[1][0] = CODE_SNAKE;
   arena[2][0] = CODE_SNAKE;
  
   put_food();
  
   glcdUpdate();
   
   while(true) {
       
        for(i=0; i<4; i++) {
            dirChanged = refreshSnakeDir();
            delay_ms(20);
            dirChanged |= refreshSnakeDir();
            delay_ms(20);
            dirChanged |= refreshSnakeDir();
            delay_ms(20);
            dirChanged |= refreshSnakeDir();
            delay_ms(20);
            dirChanged |= refreshSnakeDir();
           
            if( dirChanged || keyBPressed || keyAPressed )
               break;       
        }
     
       
        if(keyBPressed) {
            delay_ms(20);
           
            while(keyBPressed)
               delay_ms(10);
           
            while(!keyBPressed)
               delay_ms(10);
              
            while(keyBPressed)
               delay_ms(10);
              
        }
                  
        refreshSnakeDir();
           
        res = move_snake();
          
        if(res==2) {
            score++;
            put_food();
        }
       
        if(res==-1) {
            glcdClear();
            //glcdPutc("Game Over\r\nscore = ");
            //glcdPutn(score);
            delay_ms(1000);
            reset_cpu();
        }
       
       
        glcdUpdate();
    
    }
  
}