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++;
  }
}

 

 

 

 

Signal Generator

I threw this together real quick to test out a signal generator and it is a bit sloppy and written around someone else's code (their code isn't sloppy mine is). This code should just be used as a reference or for testing and credit given to David Mills for the part of the code that I borrowed from him.

This tutorial shows how to make a signal generator using the AD9850 DDS signal generator, an Arduino, and a potentiometer. I used code written by David Mills http://webshed.org/wiki/AD9850_Arduino to interface with the signal generator and added to it to allow you to set the frequency using a potentiometer and change the mode between Hz, kHz, and MHz by pressing a button. As David Mills requested, don't claim the code as your own or try to prevent anyone else using it other than that do with it what you will. 

If you are interested in making music with this check out this previous post.

BOM

-1 Arduino

-1 LCD display (optional)

-1 AD9850 DDS signal generator

-1 Touch button

Connections

Note the connections between the arduino and the AD9850 DDS signal generator. Feel free to change them if you need.

#define CLOCK 2 //pin connections for DDS
#define LOAD 3
#define DATA 7
#define RESET 8

For the LCD I used the following connections LiquidCrystal lcd(5, 6, 9, 10, 11, 12); For additional help connecting the LCD see this tutorial

Note that in this tutorial they use different pins and it uses a potentiometer being used on pin 3, I just used a resistor to ground. The value will vary between screens and it is just used to set contrast. If your screen is all black or white you may need to use a different resistor or adjust the pot. 

 

For the pot and button I used the following pins

#define POTPIN A0
#define MODE 13

 

Use the button to change the mode, then depending on what mode it is, change how you interpret the pot.

Interpreting the pot, convert the reading from the pot to a value between 0 and 1,000 for mode 1, 1,000-1,000,000 for mode 2, and 1,000,000-30,000,000 for mode 3. You will want to limit it to 30,000,000 because past that the hardware doesn't work. You may need to edit the code I used for the potentiometer that you use.

Send this to the AD9850 DDS signal generator using the code that David Mills wrote.

Pro tip: Display the frequency and mode on the LCD so you know what you are doing. 

Alternative inputs: The potentiometer I used wasn't very accurate and the value changed a lot when set all the way up or down. Use buttons or a keypad to set the frequency to get an exact amount and easily set it how you want.   

 

The complete code:

#include <LiquidCrystal.h>

LiquidCrystal lcd(5, 6, 9, 10, 11, 12);


#define DDS_CLOCK 125000000

#define  CLOCK  2  //pin connections for DDS
#define  LOAD 3 
#define  DATA  7
#define  RESET 8
#define  POTPIN A0
#define  MODE 13
float hrz;
float pot;
float temp;
int mode=0;
int moderead;
void setup()
{
  pinMode(DATA, OUTPUT); 
  pinMode(CLOCK, OUTPUT); 
  pinMode(LOAD, OUTPUT); 
  pinMode(RESET, OUTPUT); 
  pinMode(POTPIN, INPUT);
  pinMode(MODE, INPUT);
  AD9850_init();
  AD9850_reset();
  lcd.begin(16, 2);
  Serial.begin(9600);
  
  //SetFrequency(hrz);

}

void loop()
{ 
 moderead=digitalRead(MODE); 
 pot=analogRead(POTPIN);  //14-1000
 hrz=((pot-6)*10000000); 
 if (moderead==LOW)
 {
   mode++;
   delay(500);
   if (mode>2)
   {
     mode=0;
   }
 }
 
 if (mode==0)
 {
   hrz=(hrz/10000000);
    lcd.setCursor(0, 1);
    lcd.print("MODE: Hz");

 }
 
 if (mode==1)
 {
   hrz=(hrz/10000);
    lcd.setCursor(0, 1);
    lcd.print("MODE: KHz");
 }
 
 if (mode==2)
 {
    hrz=(hrz/100);
    lcd.setCursor(0, 1);
    lcd.print("MODE: MHz");
 }
if (mode!=3)
{
 if (hrz<0)
 {
   hrz=0;
   
   if (temp>1000)
   {
     lcd.clear();
   }
   lcd.setCursor(0, 0);
   lcd.print(hrz,1);
   lcd.print("HZ   ");
 }
 
 if (hrz>0 && hrz<1000)
 {
   if (temp>1000)
   {
     lcd.clear();
   }
   lcd.setCursor(0, 0);
   lcd.print(hrz,1);
   lcd.print("HZ   ");
 }
 if (hrz>1000 && hrz<1000000)
 {
   if (temp<1000|| temp>1000000)
   {
     lcd.clear();
   }
   lcd.setCursor(0, 0);
   lcd.print(hrz/1000,1);
   lcd.print("KHZ   ");
 }
  if (hrz>1000000 && hrz<30000000)
 {
   if (temp<1000000)
   {
     lcd.clear();
   }
   lcd.setCursor(0, 0);
   lcd.print(hrz/1000000,1);
   lcd.print("MHZ   ");
 }
 if (hrz>30000000)
 {
   hrz=30000000;
   if (temp<1000000)
   {
     lcd.clear();
   }
   lcd.setCursor(0, 0);
   lcd.print(hrz/1000000,1);
   lcd.print("MHZ   ");
 }
 
 
 SetFrequency(hrz);
 temp=hrz;
} 

}
void SetFrequency(unsigned long frequency)
{
  unsigned long tuning_word = (frequency * pow(2, 32)) / DDS_CLOCK;
  digitalWrite (LOAD, LOW); 

  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word);
  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word >> 8);
  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word >> 16);
  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word >> 24);
  shiftOut(DATA, CLOCK, LSBFIRST, 0x0);
  digitalWrite (LOAD, HIGH); 
}

void AD9850_init()
{

  digitalWrite(RESET, LOW);
  digitalWrite(CLOCK, LOW);
  digitalWrite(LOAD, LOW);
  digitalWrite(DATA, LOW);
}

void AD9850_reset()
{
  //reset sequence is:
  // CLOCK & LOAD = LOW
  //  Pulse RESET high for a few uS (use 5 uS here)
  //  Pulse CLOCK high for a few uS (use 5 uS here)
  //  Set DATA to ZERO and pulse LOAD for a few uS (use 5 uS here)

  // data sheet diagrams show only RESET and CLOCK being used to reset the device, but I see no output unless I also
  // toggle the LOAD line here.

  digitalWrite(CLOCK, LOW);
  digitalWrite(LOAD, LOW);

  digitalWrite(RESET, LOW);
  delay(5);
  digitalWrite(RESET, HIGH);  //pulse RESET
  delay(5);
  digitalWrite(RESET, LOW);
  delay(5);

  digitalWrite(CLOCK, LOW);
  delay(5);
  digitalWrite(CLOCK, HIGH);  //pulse CLOCK
  delay(5);
  digitalWrite(CLOCK, LOW);
  delay(5);
  digitalWrite(DATA, LOW);    //make sure DATA pin is LOW

    digitalWrite(LOAD, LOW);
  delay(5);
  digitalWrite(LOAD, HIGH);  //pulse LOAD
  delay(5);
  digitalWrite(LOAD, LOW);
  // Chip is RESET now
}






 

 

 

 

Home ← Older posts