Lets Make Tech

ATMega 328p ILI9341 TFT Library

Have at it. 

 

#define ClearBit(x,y) x &= ~_BV(y) 
#define SetBit(x,y) x |= _BV(y) 
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

#define SLPOUT 0x11 // sleep out
#define DISPON 0x29 // display on
#define CASET 0x2A // column address set
#define RASET 0x2B // row address set
#define RAMWR 0x2C // RAM write
#define COLMOD 0x3A // color mode
#define MADCTL 0x36 // axis control

// Color constants
#define BLACK 0x0000
#define RED 0x001F
#define BLUE 0xF800
#define GREEN 0x0400


typedef uint8_t byte;  
typedef int8_t sbyte;

const byte FONT_CHARS[96][5] PROGMEM =
{
 { 0x00, 0x00, 0x00, 0x00, 0x00 }, // (space)
 { 0x00, 0x00, 0x5F, 0x00, 0x00 }, // !
 { 0x00, 0x07, 0x00, 0x07, 0x00 }, // "
 { 0x14, 0x7F, 0x14, 0x7F, 0x14 }, // #
 { 0x24, 0x2A, 0x7F, 0x2A, 0x12 }, // $
 { 0x23, 0x13, 0x08, 0x64, 0x62 }, // %
 { 0x36, 0x49, 0x55, 0x22, 0x50 }, // &
 { 0x00, 0x05, 0x03, 0x00, 0x00 }, // '
 { 0x00, 0x1C, 0x22, 0x41, 0x00 }, // (
 { 0x00, 0x41, 0x22, 0x1C, 0x00 }, // )
 { 0x08, 0x2A, 0x1C, 0x2A, 0x08 }, // *
 { 0x08, 0x08, 0x3E, 0x08, 0x08 }, // +
 { 0x00, 0x50, 0x30, 0x00, 0x00 }, // ,
 { 0x08, 0x08, 0x08, 0x08, 0x08 }, // -
 { 0x00, 0x60, 0x60, 0x00, 0x00 }, // .
 { 0x20, 0x10, 0x08, 0x04, 0x02 }, // /
 { 0x3E, 0x51, 0x49, 0x45, 0x3E }, // 0
 { 0x00, 0x42, 0x7F, 0x40, 0x00 }, // 1
 { 0x42, 0x61, 0x51, 0x49, 0x46 }, // 2
 { 0x21, 0x41, 0x45, 0x4B, 0x31 }, // 3
 { 0x18, 0x14, 0x12, 0x7F, 0x10 }, // 4
 { 0x27, 0x45, 0x45, 0x45, 0x39 }, // 5
 { 0x3C, 0x4A, 0x49, 0x49, 0x30 }, // 6
 { 0x01, 0x71, 0x09, 0x05, 0x03 }, // 7
 { 0x36, 0x49, 0x49, 0x49, 0x36 }, // 8
 { 0x06, 0x49, 0x49, 0x29, 0x1E }, // 9
 { 0x00, 0x36, 0x36, 0x00, 0x00 }, // :
 { 0x00, 0x56, 0x36, 0x00, 0x00 }, // ;
 { 0x00, 0x08, 0x14, 0x22, 0x41 }, // <
 { 0x14, 0x14, 0x14, 0x14, 0x14 }, // =
 { 0x41, 0x22, 0x14, 0x08, 0x00 }, // >
 { 0x02, 0x01, 0x51, 0x09, 0x06 }, // ?
 { 0x32, 0x49, 0x79, 0x41, 0x3E }, // @
 { 0x7E, 0x11, 0x11, 0x11, 0x7E }, // A
 { 0x7F, 0x49, 0x49, 0x49, 0x36 }, // B
 { 0x3E, 0x41, 0x41, 0x41, 0x22 }, // C
 { 0x7F, 0x41, 0x41, 0x22, 0x1C }, // D
 { 0x7F, 0x49, 0x49, 0x49, 0x41 }, // E
 { 0x7F, 0x09, 0x09, 0x01, 0x01 }, // F
 { 0x3E, 0x41, 0x41, 0x51, 0x32 }, // G
 { 0x7F, 0x08, 0x08, 0x08, 0x7F }, // H
 { 0x00, 0x41, 0x7F, 0x41, 0x00 }, // I
 { 0x20, 0x40, 0x41, 0x3F, 0x01 }, // J
 { 0x7F, 0x08, 0x14, 0x22, 0x41 }, // K
 { 0x7F, 0x40, 0x40, 0x40, 0x40 }, // L
 { 0x7F, 0x02, 0x04, 0x02, 0x7F }, // M
 { 0x7F, 0x04, 0x08, 0x10, 0x7F }, // N
 { 0x3E, 0x41, 0x41, 0x41, 0x3E }, // O
 { 0x7F, 0x09, 0x09, 0x09, 0x06 }, // P
 { 0x3E, 0x41, 0x51, 0x21, 0x5E }, // Q
 { 0x7F, 0x09, 0x19, 0x29, 0x46 }, // R
 { 0x46, 0x49, 0x49, 0x49, 0x31 }, // S
 { 0x01, 0x01, 0x7F, 0x01, 0x01 }, // T
 { 0x3F, 0x40, 0x40, 0x40, 0x3F }, // U
 { 0x1F, 0x20, 0x40, 0x20, 0x1F }, // V
 { 0x7F, 0x20, 0x18, 0x20, 0x7F }, // W
 { 0x63, 0x14, 0x08, 0x14, 0x63 }, // X
 { 0x03, 0x04, 0x78, 0x04, 0x03 }, // Y
 { 0x61, 0x51, 0x49, 0x45, 0x43 }, // Z
 { 0x00, 0x00, 0x7F, 0x41, 0x41 }, // [
 { 0x02, 0x04, 0x08, 0x10, 0x20 }, // "\"
 { 0x41, 0x41, 0x7F, 0x00, 0x00 }, // ]
 { 0x04, 0x02, 0x01, 0x02, 0x04 }, // ^
 { 0x40, 0x40, 0x40, 0x40, 0x40 }, // _
 { 0x00, 0x01, 0x02, 0x04, 0x00 }, // `
 { 0x20, 0x54, 0x54, 0x54, 0x78 }, // a
 { 0x7F, 0x48, 0x44, 0x44, 0x38 }, // b
 { 0x38, 0x44, 0x44, 0x44, 0x20 }, // c
 { 0x38, 0x44, 0x44, 0x48, 0x7F }, // d
 { 0x38, 0x54, 0x54, 0x54, 0x18 }, // e
 { 0x08, 0x7E, 0x09, 0x01, 0x02 }, // f
 { 0x08, 0x14, 0x54, 0x54, 0x3C }, // g
 { 0x7F, 0x08, 0x04, 0x04, 0x78 }, // h
 { 0x00, 0x44, 0x7D, 0x40, 0x00 }, // i
 { 0x20, 0x40, 0x44, 0x3D, 0x00 }, // j
 { 0x00, 0x7F, 0x10, 0x28, 0x44 }, // k
 { 0x00, 0x41, 0x7F, 0x40, 0x00 }, // l
 { 0x7C, 0x04, 0x18, 0x04, 0x78 }, // m
 { 0x7C, 0x08, 0x04, 0x04, 0x78 }, // n
 { 0x38, 0x44, 0x44, 0x44, 0x38 }, // o
 { 0x7C, 0x14, 0x14, 0x14, 0x08 }, // p
 { 0x08, 0x14, 0x14, 0x18, 0x7C }, // q
 { 0x7C, 0x08, 0x04, 0x04, 0x08 }, // r
 { 0x48, 0x54, 0x54, 0x54, 0x20 }, // s
 { 0x04, 0x3F, 0x44, 0x40, 0x20 }, // t
 { 0x3C, 0x40, 0x40, 0x20, 0x7C }, // u
 { 0x1C, 0x20, 0x40, 0x20, 0x1C }, // v
 { 0x3C, 0x40, 0x30, 0x40, 0x3C }, // w
 { 0x44, 0x28, 0x10, 0x28, 0x44 }, // x
 { 0x0C, 0x50, 0x50, 0x50, 0x3C }, // y
 { 0x44, 0x64, 0x54, 0x4C, 0x44 }, // z
 { 0x00, 0x08, 0x36, 0x41, 0x00 }, // {
 { 0x00, 0x00, 0x7F, 0x00, 0x00 }, // |
 { 0x00, 0x41, 0x36, 0x08, 0x00 }, // }
 { 0x08, 0x08, 0x2A, 0x1C, 0x08 }, // ->
 { 0x08, 0x1C, 0x2A, 0x08, 0x08 }, // <-
};

void SetupPorts()
{
 DDRB = 0x2F; // set B0-B3, B5 as outputs
 DDRC = 0x00; // set PORTC as inputs
DDRD |= (1<<DDD7);
 SetBit(PORTB,0); //Reset low
}

void OpenSPI()
{
 SPCR = 0x50; // SPI enabled at 4 MHz
 SetBit(SPSR,SPI2X); // double SPI rate
}

byte Xfer(byte data)
{
 SPDR = data; // initiate transfer
 while (!(SPSR & 0x80)); // wait for transfer
 return SPDR;
}

void WriteCmd (byte cmd)
{
 ClearBit(PORTD,7); 
 Xfer(cmd);
 SetBit(PORTD,7); 
}
void WriteByte (byte b)
{
 Xfer(b);
}
void WriteWord (int w)
{
 Xfer(w >> 8); // write upper 8 bits
 Xfer(w & 0xFF); // write lower 8 bits
}

void Write565 (int data, unsigned long count)
{
 WriteCmd(RAMWR);
 while (count>0)
 {
 SPDR = (data >> 8); // write hi byte
 while (!(SPSR & 0x80)); // wait for transfer to complete
 SPDR = (data & 0xFF); // write lo byte
 while (!(SPSR & 0x80)); // wait for transfer to complete
 count--;
 }
}
void HReset()
{
 ClearBit(PORTB,0); // pull PB0 (digital 8) low
 SetBit(PORTB,0); // return PB0 high
}
void InitDisplay()
{
 HReset(); // initialize display controller
 WriteCmd(SLPOUT); // take display out of sleep mode
 
 WriteCmd(COLMOD); // select color mode:
 WriteByte(0x05); // mode 5 = 16bit pixels (RGB565)
 WriteCmd(DISPON); // turn display on!
}
void SetAddrWindow(int x0, int y0, int x1, int y1)
{
 WriteCmd(CASET); // set column range (x0,x1)
 WriteWord(x0);
 WriteWord(x1);
 WriteCmd(RASET); // set row range (y0,y1)
 WriteWord(y0);
 WriteWord(y1);
}

void DrawPixel (int x, int y, int color)
{
 SetAddrWindow(x,y,x,y);
 Write565(color,1);
}

void drawVLine(int xo, int yo, int y, int color)
{
  int i=y;
  while (i>yo)
  {
    DrawPixel(i,xo,color);
    i--;
  }
}

void drawHLine(int yo, int xo, int x, int color)
{
  int i=x;
  while (i>xo)
  {
    DrawPixel(yo,i,color);
    i--;
  }
}

void Rectangle(int x, int y, int x_size, int y_size, int color)
{
  int yend= y - y_size;
  int xend= x - x_size;
  int thickness=2;
  int i=0;

  while (i<thickness)
  {
    drawHLine(y+i, xend, x, color);
    drawHLine(yend-i, xend, x, color);
    i++;
  }
  i=0;
  while (i<thickness)
  {
    drawVLine(x+i, yend, y, color);
    drawVLine(xend-i, yend, y, color);
    i++;
  }
  i=0;
}


void drawdatabox()
{
	
	Rectangle(310, 230, 130, 50, GREEN);	
}



void SetOrientation(int degrees)
// Set the display orientation to 0,90,180,or 270 degrees
{
 byte arg;
 switch (degrees)
 {
 case 90: arg = 0x60; break;
 case 180: arg = 0xC0; break;
 case 270: arg = 0xA0; break;
 default: arg = 0x00; break;
 }
 WriteCmd(MADCTL);
 WriteByte(arg);
}

void PutCh (char ch, long x, long y, int color)
{
 int pixel;
 byte row, col, bit, data, mask = 0x40;
 SetAddrWindow(x,y,x+4,y+6);
 WriteCmd(RAMWR);
 for (row=0; row<7;row++)
 {
 for (col=0; col<5;col++)
 {
 data = pgm_read_byte(&(FONT_CHARS[ch-32][col]));
 bit = data & mask;
 if (bit==0) pixel=BLACK;
 else pixel=color;
 WriteWord(pixel);
 }
 mask >>= 1;
 }
}

long curX,curY; // current x & y cursor position
void GotoXY (long x,long y)
// position cursor on character x,y grid, where 0<x<20, 0<y<19.
{
 curX = x;
 curY = y;
}
void GotoLine(long y)
// position character cursor to start of line y, where 0<y<19.
{
 curX = 0;
 curY = y;
}
void AdvanceCursor()
// moves character cursor to next position, assuming portrait orientation
{
 curX=curX+6; // advance x position

}
void WriteChar(char ch, int color)
// writes character to display at current cursor position.
{
 PutCh(ch,curX, curY, color);
 AdvanceCursor();
}

void WriteString(char *text, int color)
// writes string to display at current cursor position.
{
 for (;*text;text++) // for all non-nul chars
 WriteChar(*text,color); // write the char
}

void WriteInt(int i, int color)
// writes integer i at current cursor position
{
 char str[8]; // buffer for string result
 itoa(i,str,10); // convert to string, base 10
 WriteString(str,color);
}

void blankscreen()
{
	SetOrientation(0);
  int i=0;
  int j=0;
  while(i<320)
  {
    while(j<240)
    {
      DrawPixel(j,i,BLACK);
      j++;
    }
    j=0;
    i++;
  }
}

 

 

 

 

EMG Circuit Design

BOM:

1 Instrumentation amp

4 op amps

Electrodes

Alligator clips

Some capacitors 

Some resistors 

A diode 

A zener diode

Coffee

Steps:

0. Drink coffee

1. The signal from two electrodes is amplified by an instrumentation amp.

2. The instrumentation amp uses a right leg drive for setting a reference.

3. The signal from the instrumentation amp is filtered in a band pass filter passing 20-400 Hz.

4. The signal is amplified and filtered more with op amps.

5. The signal is then rectified with a diode.

6. Finally the signal is clipped at 5V for micro-controller safety.  

Step 0.

undefined

Step 1 & 2. 

undefined

 

The cylinder around the electrodes is shielding, we don't have shielded wire, just wrap a wire around your other wires and connect to that. 

undefined

undefined

 

Step 3:

Filter out high and low frequency noise from your signal. Ideally keep 20-400 hz

undefined

undefined

The four resistors and capacitors seen in the bottom left corner pass about 22-330 hz. Good enough for me, no need to go crazy with resistors to get exact cut off points.

Step 4

Now we need to bring the signal up near 5 volts, use two more op amps to amplify it. While amplifying the signal I do additional filtering by adding a capacitor in a feedback look to make an integrator. 

 undefined

 undefined

Step 5 &6 :

Rectify your signal and smooth it out to make it easy for a micro-controller to interpret, and clip it at 5v to make it safe for the micro-controller. Some micro-controllers use 3V, check yours or just do 3V to be safe. 

undefined

 undefined

 

This should give you a working EMG. 

Your circuit may be different than mine. 

You may want to omit the passive bandpass filter and make an active one using an op amp. You may want to increase or decrease the gain or try other versions of the right-leg drive. You may need more or less gain depending on the muscle group that interest you in order to get the best resolution for that application. I did not include the values of components on the schematic because I don't expect you to copy it. This is a reference point for you to design and build your own. While you can figure out the values easily by looking at the photos, that is not my expectation.

As an example here is alternative simpler circuit that would only require 2 op amps. I never tested or made this one, but it should work.

undefined 

 

Here is a previously made EMG circuit that is similar, but still slightly different. 

http://letsmaketech.com/index.php?controller=post&action=view&id_post=8

Try some things out see what works best for you. 

If you need feel free to email me at the email address provided in class.

 

 

 

Home ← Older posts