I2C Display-interface for CW-Decoder

Hvis man i stedet for at WIRE et display op med de 10 ledninger der skal til, for at styre et standard-display, kan man bruge I2C-Interface, som beskrevet andet steds..

I og med, at en anden processor styrer displayfunktionen, som i I2C, har ARDUINO UNO ikke så travlt som tidligere, og det er forbavsende lidt der skal ændres i den tidligere version, for at få I2T til at fungere i decoder-softwaren.

Dette er Interface-kortet set bagfra, med de 4 terminaler, hvoraf 2 skal forbindes til ARDUINO UNO, og 2 til forbindes til + 5 volt og GND (stel).

Her er en DEMO-video af forbindelsen mellem I2C-Interface og Display og forbindelse til ARDUINO UNO.

https://www.youtube.com/watch?v=uGrZ2bckJLI&feature=youtu.be


Sådan WIRES ARDUINO UNO op med I2C-Interface til Displayet.

Jeg kan se, at jeg har glemt at lave spændingsforbindelser mellem TOP og BUND på "fumlebrættet", men det kan du nok selv finde ud af.

 

Af dette billede fremgår, at forbindelserne på I2C-Interface SDA og SLC skal forbuindes til ARDUINO UNO ben 
A4 og A5 ( brune forbindelser) svarende til CPU'ens ben 27 og 28.

Det er forbavsende lidt der skal ændres i selve coden, for at få I2C til at fungere.

Det, der er til føjet, er herunder, i koden farvet RØDT ( 14 linjer ), og det, der er fjernet og ændret til kommentarer, er farvet GRØNT ( 2 linjer ).

Der skal tilføjes nogle nye biblioteker til ARDUINO IDE, se HER hvordan og hvilke.

Det nye program fylder lidt mere i CPU'en:

Sketch uses 12.004 bytes (37%) of program storage space. Maximum is 32.256 bytes.
Global variables use 1.342 bytes (65%) of dynamic memory, leaving 706 bytes for local variables. Maximum is 2.048 bytes.

///////////////////////////////////////////////////////////////////////
// CW Decoder made by Hjalmar Skovholm Hansen OZ1JHM VER 1.02 //
// Feel free to change, copy or what ever you like but respect //
// that license is http://www.gnu.org/copyleft/gpl.html //
// Discuss and give great ideas on //
// https://groups.yahoo.com/neo/groups/oz1jhm/conversations/messages //
///////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////
// Read more here http://en.wikipedia.org/wiki/Goertzel_algorithm //
// if you want to know about FFT the http://www.dspguide.com/pdfbook.htm //
///////////////////////////////////////////////////////////////////////////
// *******************************************************
// ** The DECODETABLE is a part of the decode-routine ** 
// ** writen by OZ6YM, Palle http://www.planker.dk **
// ** February 2015 **
// ** Compile: 7.972 bytes (af en 32.256 byte maksimum **
// ** Some part is deleted as not nessesary in the prog.**
// ** all for speeding up the program. **
// *******************************************************

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#define I2C_ADDR 0x27 // Define I2C Address where the PCF8574A is
#define BACKLIGHT_PIN 3
#define BACKLIGHT_POL POSITIVE
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7
LiquidCrystal_I2C lcd (I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin, BACKLIGHT_PIN, BACKLIGHT_POL);


// #include <LiquidCrystal.h>

/////////////////////////////////////////////////
// select the pins used on the LCD panel       //
// **********************************************
// ** LiquidCrystal lcd(RS, E, D4, D5, D6, D7) **
// **********************************************
// LiquidCrystal lcd(2, 3, 5, 6, 7, 8 );
// **********************************************
// *** END of pin- assignment **
// **********************************************

// ********** Defining the DISPLAY **************
// ** Display is 20 character pr line **
// ** Display have 4 lines **
// **********************************************
const int colums = 20; // have to be 16 or 20, could be other size
const int rows = 4; // have to be 2 or 4
// **********************************************
// *** END of DISPLAY definition **
// **********************************************

// **********************************************
// ** Variabel definitions for Display use **
// **********************************************
int lcdindex = 0;
int line1[colums];
int line2[colums];
// **********************************************
// *** END of Variaberl definition **
// **********************************************

////////////////////////////////
// Define 8 specials letters //
////////////////////////////////
byte U_umlaut[8] = {B01010,B00000,B10001,B10001,B10001,B10001,B01110,B00000}; // ' ' 
byte O_umlaut[8] = {B01010,B00000,B01110,B10001,B10001,B10001,B01110,B00000}; // ' ' 
byte A_umlaut[8] = {B01010,B00000,B01110,B10001,B11111,B10001,B10001,B00000}; // ' ' 
byte AE_capital[8] = {B01111,B10100,B10100,B11110,B10100,B10100,B10111,B00000}; // ' ' 
byte OE_capital[8] = {B00001,B01110,B10011,B10101,B11001,B01110,B10000,B00000}; // ' ' 
byte fullblock[8] = {B11111,B11111,B11111,B11111,B11111,B11111,B11111,B11111}; 
byte AA_capital[8] = {B00100,B00000,B01110,B10001,B11111,B10001,B10001,B00000}; // ' ' 
byte emtyblock[8] = {B00000,B00000,B00000,B00000,B00000,B00000,B00000,B00000}; 
// **********************************************
// ** END of Special Letters **
// **********************************************

// **********************************************
// ** Other port assignments on ARDUINO UNO **
// ** Read the application note if you use a **
// ** ATmega328p at: 
// http://www.atmel.com/images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf
// **********************************************
int audioInPin = A1;  // ARDUINO UNO-port AD1 - This is the port READING signal from radio.

int audioOutPin = 12; // ARDUINO tone on pin 12 - ATmega328P, pin 18 =PB4(PCINT4/MISO) - 
// A 8 Ohm lautspeaker in serie with a 100 Ohm Resistor make received CW-sound 

int ledPin = 13; // Lightdiode-port 13 - ATmega328P, pin 19=PB5 (PCINT5/SCK ) - 
// A light-diode on this port will blink in same tact af received morse-sign
// **********************************************
// *** END of Other port assignments **
// **********************************************

// **********************************************
// ** Program Variable **
// **********************************************
float magnitude ;
int magnitudelimit = 100;
int magnitudelimit_low = 100;
int realstate = LOW;
int realstatebefore = LOW;
int filteredstate = LOW;
int filteredstatebefore = LOW;
///////////////////////////////////////////////////////////////////////////////////////////////////
// The sampling frq will be 8928 on a 16 mhz without any prescaler etc because we need the tone //
// in the center of the bins you can set the tone to 496, 558, 744 or 992 then n the number of //
// samples which give the bandwidth can be (8928 / tone) * 1 or 2 or 3 or 4 etc init is //
// 8928/558 = 16 *4 = 64 samples try to take n = 96 or 128 ;o) //
// 48 will give you a bandwidth around 186 hz //
// 64 will give you a bandwidth around 140 hz //
// 96 will give you a bandwidth around 94 hz //
// 128 will give you a bandwidth around 70 hz //
// BUT remember that high n take a lot of time so you have to find the compromice - i use 48 //
///////////////////////////////////////////////////////////////////////////////////////////////////
float coeff;
float Q1 = 0;
float Q2 = 0;
float sine;
float cosine; 
float sampling_freq=8928.0;
float target_freq=700.0; /// adjust for your needs see above
float n=48.0; //// if you change her please change next line also 
int testData[48];
// ************************************************************************************************
///////////////////////////////////////////////////////////////////
// Noise Blanker time which shall be computed so this is initial //
///////////////////////////////////////////////////////////////////
int nbtime = 6; /// ms noise blanker 
long starttimehigh;
long highduration;
long lasthighduration;
long hightimesavg;
long lowtimesavg;
long startttimelow;
long lowduration;
long laststarttime = 0;
char code[20];
int stop = LOW;
int wpm;
unsigned long count;
int PalStop = 0;
int countMag = 0;

// *************** Revrited february 2015 *****************
// ** by OZ6YM, Palle A. Andersen **
// ** http://www.planker.dk **
// ********************************************************
// ** DECODE-TABLE **
// ********************************************************
char const VARcw0[47][9] = {"--..--","-....-",".-.-.-","-..-.","-----",".----","..---","...--","....-",".....","-....","--...","---..","----.","---...","-.-.-.",
".-.-.","-...-","-.-.--","..--..",".--.-.",".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...",
"-","..-","...-",".--","-..-","-.--","--.."};
// ***************************************************************************************************************************************************
// ** ASCII-karakterer linear from no: 44 T.O.M. 90 **
// ** 44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90 **
// ***************************************************************************************************************************************************
char const VARcw1[8][9] = {"..--.", "...-..-", "-.--.-", "-.--.", "e", ".-.-", "---.", ".--.-"};
// *******************************************************
// ** ASCII-Charakter outside the lineare numbers **
// *******************************************************
int VARchr[8] = {33,36,40,41,0,3,4,6}; 
// *******************************************************
// ** END of re-writed code de OZ6YM **
// *******************************************************

////////////////
// init setup //
////////////////
void setup() 
{
// FreqCount.begin(1000);  //NOT ACTIVATED
////////////////////////////////////
// The basic goertzel calculation //
////////////////////////////////////
int k;
float omega;
k = (int) (0.5 + ((n * target_freq) / sampling_freq));
omega = (2.0 * PI * k) / n;
sine = sin(omega);
cosine = cos(omega);
coeff = 2.0 * cosine;

///////////////////////////////
// define special characters //
///////////////////////////////
lcd.createChar(0, U_umlaut); // German
lcd.createChar(1, O_umlaut); // German, Swedish
lcd.createChar(2, A_umlaut); // German, Swedish 
lcd.createChar(3, AE_capital); // Danish, Norwegian
lcd.createChar(4, OE_capital); // Danish, Norwegian
lcd.createChar(5, fullblock); 
lcd.createChar(6, AA_capital); // Danish, Norwegian, Swedish
lcd.createChar(7, emtyblock); 
lcd.clear(); 

// *******************************************************************************
Serial.begin(115200); // All serial communication is only used in DEBUG mode...
// *******************************************************************************
pinMode(ledPin, OUTPUT);
lcd.begin(colums, rows); 
for (int index = 0; index < colums; index++){
line1[index] = 32;
line2[index] = 32;

lcd.backlight(); // Backlight on 
}
// ************************************************
// ** END of SETUP **
// ************************************************

///////////////
// main loop //
///////////////
void loop() 
{
/* if (FreqCount.available()) 
{
count = FreqCount.read();

target_freq = (count );
Serial.println(target_freq);
}*/ 
///////////////////////////////////// 
// The basic where we get the tone //
/////////////////////////////////////
for (char index = 0; index < n; index++)
{
testData[index] = analogRead(audioInPin);
}
for (char index = 0; index < n; index++)
{
float Q0;
Q0 = coeff * Q1 - Q2 + (float) testData[index];
Q2 = Q1;
Q1 = Q0; 
}
float magnitudeSquared = (Q1*Q1)+(Q2*Q2)-Q1*Q2*coeff; // we do only need the real part //
magnitude = sqrt(magnitudeSquared);
Q2 = 0;
Q1 = 0;
// *********************************************************************************************

/////////////////////////////////////////////////////////// 
// here we will try to set the magnitude limit automatic //
///////////////////////////////////////////////////////////
if (magnitude > magnitudelimit_low)
{
magnitudelimit = (magnitudelimit +((magnitude - magnitudelimit)/6)); /// moving average filter
}
if (magnitudelimit < magnitudelimit_low)
{
magnitudelimit = magnitudelimit_low;
}
////////////////////////////////////
// now we check for the magnitude //
////////////////////////////////////

if(magnitude > magnitudelimit*0.6)
{ // just to have some space up 
realstate = HIGH;

else
{
realstate = LOW; 
}
///////////////////////////////////////////////////// 
// here we clean up the state with a noise blanker //
/////////////////////////////////////////////////////
if (realstate != realstatebefore)
{
laststarttime = millis();
}
if ((millis()-laststarttime)> nbtime)
{
if (realstate != filteredstate)

filteredstate = realstate; 
}
}
////////////////////////////////////////////////////////////
// Then we do want to have some durations on high and low //
////////////////////////////////////////////////////////////
if (filteredstate != filteredstatebefore)
{
if (filteredstate == HIGH)
{
starttimehigh = millis(); lowduration = (millis() - startttimelow); 
}
if (filteredstate == LOW) 

startttimelow = millis(); highduration = (millis() - starttimehigh);
if (highduration < (2*hightimesavg) || hightimesavg == 0)
{
hightimesavg = (highduration+hightimesavg+hightimesavg)/3;
} // now we know avg dit time ( rolling 3 avg)
if (highduration > (5*hightimesavg) )
{
hightimesavg = highduration+hightimesavg; 
} // if speed decrease fast ..
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// now we will check which kind of baud we have - dit or dah and what kind of pause we do have 1 - 3 or 7 pause //
// we think that hightimeavg = 1 bit //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (filteredstate != filteredstatebefore)
{
stop = LOW;
if (filteredstate == LOW)
{ // we did end a HIGH
if (highduration < (hightimesavg*2) && highduration > (hightimesavg*0.6))
{ // 0.6 filter out false dits
strcat(code,".");
// *****************************************************************************************************************************// 
//Serial.print(".");
}
if (highduration > (hightimesavg*2) && highduration < (hightimesavg*6))

strcat(code,"-");
// *****************************************************************************************************************************// 
//Serial.print("-");
wpm = ( wpm + (1200/((highduration)/3)))/2; // the most precise we can do ;o)
}
}
if (filteredstate == HIGH)
{ //// we did end a LOW
float lacktime = 1;
if(wpm > 25)lacktime=1.0; /// when high speeds we have to have a little more pause before new letter or new word 
if(wpm > 30)lacktime=1.2;
if(wpm > 35)lacktime=1.5;

if (lowduration > (hightimesavg*(2*lacktime)) && lowduration < hightimesavg*(5*lacktime))
{ // letter space
docode();
code[0] = '\0';
// Serial.print("/");
}
if (lowduration >= hightimesavg*(5*lacktime))
{ // word space
docode();
code[0] = '\0';
printascii(32); //32 = Space
// Serial.println();
}
}
}
//////////////////////////////
// write if no more letters //
//////////////////////////////
if ((millis() - startttimelow) > (highduration * 6) && stop == LOW)
{
docode(); code[0] = '\0'; stop = HIGH; 
}
/////////////////////////////////////
// we will turn on and off the LED //
// and the speaker //
/////////////////////////////////////
if(filteredstate == HIGH)
{
digitalWrite(ledPin, HIGH);
tone(audioOutPin,700);
}
else
{
digitalWrite(ledPin, LOW);
noTone(audioOutPin);
}

//////////////////////////////////
// the end of main loop clean up//
/////////////////////////////////
updateinfolinelcd(); realstatebefore = realstate; lasthighduration = highduration; filteredstatebefore = filteredstate;
}
////////////////////////////////
// translate cw code to ascii //
////////////////////////////////
// **************************************************************************
// *** This routine is re-writed by OZ6YM, Palle - http://www.planker.dk ***
// **************************************************************************
void docode()
{
for (int i=0; i <= 46; i++)

if (strcmp(code,&VARcw0[i][0]) == 0)
{
printascii(i+44); 
break;
}
} // Char kendes
for (int i=0; i <= 7; i++)

if (strcmp(code,&VARcw1[i][0]) == 0)
{
printascii(VARchr[i]); 
break;
}
}
} // Char kendes

// **************************************************************************
// ** END of re-writed code de OZ6YM **
// **************************************************************************
///////////////////////////////////////////////////////////////////////////////////
// print the ascii code to the lcd one a time so we can generate special letters //
///////////////////////////////////////////////////////////////////////////////////
void printascii(int asciinumber)
{
int fail = 0;
if (rows == 4 and colums == 16)fail = -4; // to fix the library problem with 4*16 display http://forum.arduino.cc/index.php/topic,14604.0.html
if (lcdindex > colums-1)
{
lcdindex = 0;
if (rows==4)
{
for (int i = 0; i <= colums-1 ; i++)
{
lcd.setCursor(i,rows-3);
lcd.write(line2[i]);
line2[i]=line1[i];
}
}
for (int i = 0; i <= colums-1 ; i++)
{
lcd.setCursor(i+fail,rows-2);
lcd.write(line1[i]);
lcd.setCursor(i+fail,rows-1);
lcd.write(32);
}
}
line1[lcdindex]=asciinumber;
lcd.setCursor(lcdindex+fail,rows-1);
lcd.write(asciinumber);
lcdindex += 1;
}

void updateinfolinelcd()
{
///////////////////////////////////////////////////
// here we update the upper line with the speed. //
///////////////////////////////////////////////////
int place;
if (rows == 4)
{
place = colums/2;
}
else
{
place = 2;
}
// ***************************************************
// ** here is some modyfied code de OZ6YM **
// ** and you can put your own call here........ **
// ***************************************************
if (wpm<10)
{

// lcd.setCursor((place)-10,0);
// lcd.print(" CW BY OZ6YM-");
lcd.setCursor((place)+4,0);
lcd.print("0");
lcd.setCursor((place)+5,0);
lcd.print(wpm);
lcd.setCursor((place)+6,0);
lcd.print(" WPM");
// lcd.setCursor(0,0);lcd.print(count);
}
else
{
lcd.setCursor((place)+4,0);
lcd.print(wpm);
lcd.setCursor((place)+6,0);
lcd.print(" WPM");
// lcd.setCursor(0,0);lcd.print(int(target_freq));
}
}