├── .gitattributes ├── .gitignore ├── Libraries ├── Lcd7920 │ ├── Lcd7920.ino │ ├── glcd10x10.cpp │ ├── glcd16x16.cpp │ ├── lcd7920.cpp │ ├── lcd7920.h │ ├── lcd_retest │ │ └── lcd_retest.ino │ └── library.properties ├── PushButton │ ├── PushButton.cpp │ ├── PushButton.h │ └── library.properties ├── RotaryEncoder │ ├── RotaryEncoder.cpp │ ├── RotaryEncoder.h │ └── library.properties └── Scheduler │ ├── Scheduler.cpp │ ├── Scheduler.h │ └── Scheduler.ino ├── MetalDetector ├── KiCad │ ├── MetalDetector.kicad_pcb │ ├── MetalDetector.kicad_prl │ ├── MetalDetector.kicad_pro │ ├── MetalDetector.kicad_sch │ └── MetalDetectorSchematic.pdf ├── MetalDetector.ino ├── Photos │ ├── DetectorHead.jpg │ ├── Metal Detector Board Top View Without Arduino.jpg │ ├── Metal Detector Board Top View.jpg │ └── Metal Detector Board underside.jpg ├── PrintableParts │ ├── MetalDetectorArmRest.3mf │ ├── MetalDetectorArmRest.scad │ ├── MetalDetectorBracket.3mf │ ├── MetalDetectorBracket.scad │ ├── MetalDetectorEnclosure.3mf │ └── MetalDetectorEnclosure.scad └── Readme.md └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | [Dd]ebug/ 46 | [Rr]elease/ 47 | *_i.c 48 | *_p.c 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.vspscc 63 | .builds 64 | *.dotCover 65 | 66 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 67 | #packages/ 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | 76 | # Visual Studio profiler 77 | *.psess 78 | *.vsp 79 | 80 | # ReSharper is a .NET coding add-in 81 | _ReSharper* 82 | 83 | # Installshield output folder 84 | [Ee]xpress 85 | 86 | # DocProject is a documentation generator add-in 87 | DocProject/buildhelp/ 88 | DocProject/Help/*.HxT 89 | DocProject/Help/*.HxC 90 | DocProject/Help/*.hhc 91 | DocProject/Help/*.hhk 92 | DocProject/Help/*.hhp 93 | DocProject/Help/Html2 94 | DocProject/Help/html 95 | 96 | # Click-Once directory 97 | publish 98 | 99 | # Others 100 | [Bb]in 101 | [Oo]bj 102 | sql 103 | TestResults 104 | *.Cache 105 | ClientBin 106 | stylecop.* 107 | ~$* 108 | *.dbmdl 109 | Generated_Code #added for RIA/Silverlight projects 110 | 111 | # Backup & report files from converting an old project file to a newer 112 | # Visual Studio version. Backup files are not needed, because we have git ;-) 113 | _UpgradeReport_Files/ 114 | Backup*/ 115 | UpgradeLog*.XML 116 | 117 | 118 | 119 | ############ 120 | ## Windows 121 | ############ 122 | 123 | # Windows image file caches 124 | Thumbs.db 125 | 126 | # Folder config file 127 | Desktop.ini 128 | 129 | 130 | ############# 131 | ## Python 132 | ############# 133 | 134 | *.py[co] 135 | 136 | # Packages 137 | *.egg 138 | *.egg-info 139 | dist 140 | build 141 | eggs 142 | parts 143 | bin 144 | var 145 | sdist 146 | develop-eggs 147 | .installed.cfg 148 | 149 | # Installer logs 150 | pip-log.txt 151 | 152 | # Unit test / coverage reports 153 | .coverage 154 | .tox 155 | 156 | #Translations 157 | *.mo 158 | 159 | #Mr Developer 160 | .mr.developer.cfg 161 | 162 | # Mac crap 163 | .DS_Store 164 | -------------------------------------------------------------------------------- /Libraries/Lcd7920/Lcd7920.ino: -------------------------------------------------------------------------------- 1 | // ST7920-based 128x64 pixel graphic LCD demo program 2 | // Written by D. Crocker, Escher Technologies Ltd. 3 | 4 | #include 5 | #include "lcd7920.h" 6 | 7 | extern PROGMEM LcdFont font10x10; // in glcd10x10.cpp 8 | extern PROGMEM LcdFont font16x16; // in glcs16x16.cpp 9 | 10 | // Define the following as true to use SPI to drive the LCD, false to drive it slowly instead 11 | #define LCD_USE_SPI (true) 12 | 13 | // Definition of Arduino type 14 | #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) 15 | #define IS_MEGA (1) 16 | #define IS_UNO (0) 17 | #else 18 | #define IS_MEGA (0) 19 | #define IS_UNO (1) 20 | #endif 21 | 22 | typedef unsigned char uint8_t; 23 | typedef unsigned int uint16_t; 24 | 25 | // Pins for serial LCD interface. For speed, we use the SPI interface, which means we need the MOSI and SCLK pins. 26 | // Connection of the 7920-based GLCD: 27 | // Vss to Gnd 28 | // Vdd to +5v 29 | // Vo through 10k variable resistor to +5v (not needed if your board has a contrast adjust pot on the back already, like mine does) 30 | // RS to +5v 31 | // PSB to gnd 32 | // RW to Arduino MOSI (see below for pin mapping) 33 | // E to Arduino SCLK (see below for pin mapping) 34 | // RST to +5v 35 | // D0-D7 unconnected 36 | // BLK to ground (or collector of NPN transistor if controlling backlight by PWM) 37 | // BLA via series resistor (if not included in LCD module) to +5v or other supply 38 | 39 | // The following pin numbers must not be changed because they must correspond to the processor MOSI and SCLK pins 40 | // We don't actually use the MISO pin, but it gets forced as an input when we enable SPI 41 | // The SS pin MUST be configured as an output (if it is used as an input and goes low, it will kill the communication with the LCD). 42 | #if IS_UNO 43 | const int MOSIpin = 11; 44 | const int MISOpin = 12; 45 | const int SCLKpin = 13; 46 | const int SSpin = 10; 47 | #else 48 | const int MOSIpin = 51; 49 | const int MISOpin = 50; 50 | const int SCLKpin = 52; 51 | const int SSpin = 53; 52 | #endif 53 | 54 | // End of configuration section 55 | 56 | // LCD 57 | static Lcd7920 lcd(SCLKpin, MOSIpin, LCD_USE_SPI); 58 | 59 | // Initialization 60 | void setup() 61 | { 62 | pinMode(SSpin, OUTPUT); // must do this before we use the lcd in SPI mode 63 | 64 | lcd.begin(true); // init lcd in graphics mode 65 | } 66 | 67 | // Find out how much free memory we have 68 | unsigned int getFreeMemory() 69 | { 70 | uint8_t* temp = (uint8_t*)malloc(16); // assumes there are no free holes so this is allocated at the end 71 | unsigned int rslt = (uint8_t*)SP - temp; 72 | free(temp); 73 | return rslt; 74 | } 75 | 76 | void loop() 77 | { 78 | lcd.setFont(&font10x10); 79 | lcd.setCursor(0, 0); 80 | lcd.print("Hello "); 81 | lcd.setFont(&font16x16); 82 | lcd.print("world!"); 83 | 84 | lcd.circle(110, 31, 12, PixelSet); 85 | lcd.circle(110, 31, 16, PixelSet); 86 | lcd.line(3, 60, 127, 33, PixelFlip); 87 | 88 | lcd.setCursor(44, 14); 89 | lcd.setFont(&font10x10); 90 | lcd.print("Free RAM "); 91 | lcd.print(getFreeMemory()); 92 | lcd.print(" bytes "); 93 | lcd.flush(); 94 | 95 | delay(200); 96 | } 97 | 98 | // End 99 | 100 | 101 | -------------------------------------------------------------------------------- /Libraries/Lcd7920/glcd10x10.cpp: -------------------------------------------------------------------------------- 1 | #include "lcd7920.h" 2 | 3 | //Font Generated by MikroElektronika GLCD Font Creator 1.2.0.0 4 | //MikroElektronika 2011 5 | //http://www.mikroe.com 6 | 7 | //GLCD FontName : glcd10x10 8 | //GLCD FontSize : 10 x 10 9 | 10 | static const PROGMEM uint8_t glcd10x10[] PROGMEM = 11 | { 12 | 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 13 | 0x01, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ! 14 | 0x03, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char " 15 | 0x05, 0x24, 0x00, 0xFF, 0x00, 0x24, 0x00, 0xFF, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char # 16 | 0x05, 0x46, 0x00, 0x89, 0x00, 0xFF, 0x01, 0x89, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char $ 17 | 0x09, 0x06, 0x00, 0x09, 0x00, 0x89, 0x00, 0x66, 0x00, 0x18, 0x00, 0x66, 0x00, 0x91, 0x00, 0x90, 0x00, 0x60, 0x00, 0x00, 0x00, // Code for char % 18 | 0x06, 0x60, 0x00, 0x96, 0x00, 0x89, 0x00, 0x99, 0x00, 0x66, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char & 19 | 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ' 20 | 0x03, 0xFC, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ( 21 | 0x03, 0x01, 0x02, 0x02, 0x01, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ) 22 | 0x05, 0x15, 0x00, 0x0E, 0x00, 0x1F, 0x00, 0x0E, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char * 23 | 0x05, 0x10, 0x00, 0x10, 0x00, 0x7C, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char + 24 | 0x02, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char , 25 | 0x03, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char - 26 | 0x02, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char . 27 | 0x03, 0xC0, 0x00, 0x3C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char / 28 | 0x05, 0x7E, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0 29 | 0x03, 0x04, 0x00, 0x02, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 1 30 | 0x05, 0x82, 0x00, 0xC1, 0x00, 0xA1, 0x00, 0x91, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 2 31 | 0x05, 0x42, 0x00, 0x81, 0x00, 0x89, 0x00, 0x89, 0x00, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 3 32 | 0x06, 0x30, 0x00, 0x28, 0x00, 0x24, 0x00, 0x22, 0x00, 0xFF, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 4 33 | 0x05, 0x4F, 0x00, 0x89, 0x00, 0x89, 0x00, 0x89, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 5 34 | 0x05, 0x7E, 0x00, 0x89, 0x00, 0x89, 0x00, 0x89, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 6 35 | 0x05, 0x01, 0x00, 0xE1, 0x00, 0x19, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 7 36 | 0x05, 0x76, 0x00, 0x89, 0x00, 0x89, 0x00, 0x89, 0x00, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 8 37 | 0x05, 0x4E, 0x00, 0x91, 0x00, 0x91, 0x00, 0x91, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 9 38 | 0x01, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char : 39 | 0x01, 0x88, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ; 40 | 0x05, 0x10, 0x00, 0x10, 0x00, 0x28, 0x00, 0x28, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char < 41 | 0x05, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char = 42 | 0x05, 0x44, 0x00, 0x28, 0x00, 0x28, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char > 43 | 0x05, 0x02, 0x00, 0x01, 0x00, 0xB1, 0x00, 0x09, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ? 44 | 0x0A, 0xF8, 0x00, 0x04, 0x01, 0x72, 0x02, 0x89, 0x02, 0x85, 0x02, 0x45, 0x02, 0xF9, 0x02, 0x8D, 0x02, 0x42, 0x02, 0x3C, 0x01, // Code for char @ 45 | 0x07, 0xC0, 0x00, 0x30, 0x00, 0x2C, 0x00, 0x23, 0x00, 0x2C, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char A 46 | 0x06, 0xFF, 0x00, 0x89, 0x00, 0x89, 0x00, 0x89, 0x00, 0x89, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char B 47 | 0x06, 0x3C, 0x00, 0x42, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char C 48 | 0x06, 0xFF, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x42, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char D 49 | 0x05, 0xFF, 0x00, 0x89, 0x00, 0x89, 0x00, 0x89, 0x00, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char E 50 | 0x05, 0xFF, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char F 51 | 0x07, 0x3C, 0x00, 0x42, 0x00, 0x81, 0x00, 0x81, 0x00, 0x91, 0x00, 0x52, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char G 52 | 0x06, 0xFF, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char H 53 | 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char I 54 | 0x04, 0x60, 0x00, 0x80, 0x00, 0x80, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char J 55 | 0x06, 0xFF, 0x00, 0x10, 0x00, 0x08, 0x00, 0x1C, 0x00, 0x62, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char K 56 | 0x05, 0xFF, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char L 57 | 0x07, 0xFF, 0x00, 0x06, 0x00, 0x18, 0x00, 0x60, 0x00, 0x18, 0x00, 0x06, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char M 58 | 0x06, 0xFF, 0x00, 0x02, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x40, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char N 59 | 0x07, 0x3C, 0x00, 0x42, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x42, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char O 60 | 0x05, 0xFF, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char P 61 | 0x07, 0x3C, 0x00, 0x42, 0x00, 0x81, 0x00, 0xA1, 0x00, 0xA1, 0x00, 0x42, 0x00, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Q 62 | 0x06, 0xFF, 0x00, 0x09, 0x00, 0x09, 0x00, 0x19, 0x00, 0x69, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char R 63 | 0x06, 0x46, 0x00, 0x89, 0x00, 0x89, 0x00, 0x91, 0x00, 0x91, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char S 64 | 0x05, 0x01, 0x00, 0x01, 0x00, 0xFF, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char T 65 | 0x06, 0x7F, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char U 66 | 0x07, 0x03, 0x00, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char V 67 | 0x0A, 0x03, 0x00, 0x3C, 0x00, 0xC0, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x3C, 0x00, 0x03, 0x00, // Code for char W 68 | 0x06, 0x81, 0x00, 0x66, 0x00, 0x18, 0x00, 0x18, 0x00, 0x66, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char X 69 | 0x07, 0x01, 0x00, 0x06, 0x00, 0x08, 0x00, 0xF0, 0x00, 0x08, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Y 70 | 0x06, 0x81, 0x00, 0xC1, 0x00, 0xB1, 0x00, 0x8D, 0x00, 0x83, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Z 71 | 0x02, 0xFF, 0x03, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char [ 72 | 0x03, 0x03, 0x00, 0x3C, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char BackSlash 73 | 0x02, 0x01, 0x02, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ] 74 | 0x05, 0x08, 0x00, 0x06, 0x00, 0x01, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ^ 75 | 0x06, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char _ 76 | 0x02, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ` 77 | 0x05, 0x68, 0x00, 0x94, 0x00, 0x94, 0x00, 0x54, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char a 78 | 0x05, 0xFF, 0x00, 0x48, 0x00, 0x84, 0x00, 0x84, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char b 79 | 0x05, 0x78, 0x00, 0x84, 0x00, 0x84, 0x00, 0x84, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char c 80 | 0x05, 0x78, 0x00, 0x84, 0x00, 0x84, 0x00, 0x48, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char d 81 | 0x05, 0x78, 0x00, 0x94, 0x00, 0x94, 0x00, 0x94, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char e 82 | 0x03, 0x04, 0x00, 0xFE, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char f 83 | 0x05, 0x78, 0x02, 0x84, 0x02, 0x84, 0x02, 0x48, 0x02, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char g 84 | 0x05, 0xFF, 0x00, 0x08, 0x00, 0x04, 0x00, 0x04, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char h 85 | 0x01, 0xFD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char i 86 | 0x02, 0x00, 0x02, 0xFD, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char j 87 | 0x04, 0xFF, 0x00, 0x10, 0x00, 0x68, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char k 88 | 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char l 89 | 0x07, 0xFC, 0x00, 0x08, 0x00, 0x04, 0x00, 0xFC, 0x00, 0x04, 0x00, 0x04, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char m 90 | 0x05, 0xFC, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char n 91 | 0x05, 0x78, 0x00, 0x84, 0x00, 0x84, 0x00, 0x84, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char o 92 | 0x05, 0xFC, 0x03, 0x48, 0x00, 0x84, 0x00, 0x84, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char p 93 | 0x05, 0x78, 0x00, 0x84, 0x00, 0x84, 0x00, 0x48, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char q 94 | 0x03, 0xFC, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char r 95 | 0x05, 0x48, 0x00, 0x94, 0x00, 0x94, 0x00, 0xA4, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char s 96 | 0x02, 0xFF, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char t 97 | 0x05, 0x7C, 0x00, 0x80, 0x00, 0x80, 0x00, 0x40, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char u 98 | 0x05, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char v 99 | 0x09, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x30, 0x00, 0x08, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x00, 0x00, // Code for char w 100 | 0x05, 0x84, 0x00, 0x48, 0x00, 0x30, 0x00, 0x48, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char x 101 | 0x05, 0x0C, 0x00, 0x30, 0x02, 0xC0, 0x01, 0x30, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char y 102 | 0x05, 0x84, 0x00, 0xC4, 0x00, 0xB4, 0x00, 0x8C, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char z 103 | 0x03, 0x10, 0x00, 0xEE, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char { 104 | 0x01, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char | 105 | 0x04, 0x00, 0x00, 0x01, 0x02, 0xEE, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char } 106 | 0x05, 0x10, 0x00, 0x08, 0x00, 0x18, 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ~ 107 | 0x03, 0xFF, 0x00, 0x81, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char  108 | }; 109 | 110 | extern const PROGMEM LcdFont font10x10 = 111 | { 112 | glcd10x10, // font data 113 | 0x0020, // first character code 114 | 0x007F, // last character code 115 | 10, // row height in pixels 116 | 10, // character width in pixels 117 | 1 // number of space pixels between characters 118 | }; 119 | 120 | 121 | // End 122 | 123 | -------------------------------------------------------------------------------- /Libraries/Lcd7920/glcd16x16.cpp: -------------------------------------------------------------------------------- 1 | #include "lcd7920.h" 2 | 3 | //Font Generated by MikroElektronika GLCD Font Creator 1.2.0.0 4 | //MikroElektronika 2011 5 | //http://www.mikroe.com 6 | 7 | //GLCD FontName : glcd16x16 8 | //GLCD FontSize : 16 x 16 9 | 10 | static const uint8_t glcd16x16[] PROGMEM = { 11 | 0x07, 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, // Code for char 12 | 0x03, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x17, 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, // Code for char ! 13 | 0x05, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char " 14 | 0x09, 0x20, 0x02, 0x20, 0x1E, 0xF0, 0x03, 0x2E, 0x02, 0x20, 0x02, 0x20, 0x1E, 0xF0, 0x03, 0x2E, 0x02, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char # 15 | 0x08, 0x00, 0x00, 0x38, 0x04, 0x44, 0x08, 0x42, 0x10, 0xFF, 0x3F, 0x82, 0x10, 0x82, 0x08, 0x0C, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char $ 16 | 0x0D, 0x00, 0x00, 0x3C, 0x00, 0x42, 0x00, 0x42, 0x00, 0x42, 0x18, 0x3C, 0x06, 0x80, 0x01, 0x70, 0x00, 0x0C, 0x0F, 0x82, 0x10, 0x80, 0x10, 0x80, 0x10, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char % 17 | 0x0A, 0x00, 0x00, 0x00, 0x07, 0x9C, 0x08, 0x62, 0x10, 0xC2, 0x10, 0x22, 0x11, 0x1C, 0x0A, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char & 18 | 0x02, 0x00, 0x00, 0x1E, 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, // Code for char ' 19 | 0x04, 0x00, 0x00, 0xE0, 0x0F, 0x1C, 0x70, 0x02, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ( 20 | 0x04, 0x00, 0x00, 0x02, 0x80, 0x1C, 0x70, 0xE0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ) 21 | 0x05, 0x04, 0x00, 0x34, 0x00, 0x0E, 0x00, 0x34, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char * 22 | 0x08, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xF0, 0x07, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char + 23 | 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 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, // Code for char , 24 | 0x04, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char - 25 | 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 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, // Code for char . 26 | 0x04, 0x00, 0x18, 0x80, 0x07, 0x78, 0x00, 0x06, 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, // Code for char / 27 | 0x08, 0x00, 0x00, 0xF8, 0x07, 0x04, 0x08, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x04, 0x08, 0xF8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 0 28 | 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, 0xFE, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 1 29 | 0x08, 0x00, 0x00, 0x08, 0x10, 0x04, 0x18, 0x02, 0x14, 0x02, 0x12, 0x02, 0x11, 0xC6, 0x10, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 2 30 | 0x08, 0x00, 0x00, 0x08, 0x0C, 0x04, 0x08, 0x42, 0x10, 0x42, 0x10, 0x62, 0x10, 0xBC, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 3 31 | 0x09, 0x00, 0x00, 0x00, 0x03, 0x80, 0x02, 0x40, 0x02, 0x30, 0x02, 0x08, 0x02, 0x04, 0x02, 0xFE, 0x1F, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 4 32 | 0x08, 0x00, 0x00, 0x70, 0x04, 0x2E, 0x08, 0x22, 0x10, 0x22, 0x10, 0x22, 0x10, 0x42, 0x08, 0x82, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 5 33 | 0x08, 0x00, 0x00, 0xF8, 0x07, 0x44, 0x08, 0x22, 0x10, 0x22, 0x10, 0x22, 0x10, 0x44, 0x08, 0x88, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 6 34 | 0x08, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x1C, 0xC2, 0x03, 0x32, 0x00, 0x0E, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 7 35 | 0x08, 0x00, 0x00, 0x18, 0x07, 0xA4, 0x08, 0x42, 0x10, 0x42, 0x10, 0x42, 0x10, 0xA4, 0x08, 0x18, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 8 36 | 0x08, 0x00, 0x00, 0x78, 0x04, 0x84, 0x08, 0x02, 0x11, 0x02, 0x11, 0x02, 0x11, 0x84, 0x08, 0xF8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char 9 37 | 0x02, 0x00, 0x00, 0x10, 0x10, 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, // Code for char : 38 | 0x02, 0x00, 0x00, 0x10, 0x70, 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, // Code for char ; 39 | 0x08, 0x00, 0x00, 0x80, 0x00, 0x40, 0x01, 0x40, 0x01, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0x10, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char < 40 | 0x08, 0x00, 0x00, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char = 41 | 0x08, 0x00, 0x00, 0x10, 0x04, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0x40, 0x01, 0x40, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char > 42 | 0x08, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02, 0x17, 0x82, 0x00, 0x44, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ? 43 | 0x10, 0x00, 0x00, 0xC0, 0x0F, 0x30, 0x10, 0x08, 0x20, 0x84, 0x47, 0x44, 0x48, 0x22, 0x90, 0x12, 0x90, 0x12, 0x90, 0x12, 0x88, 0x22, 0x9F, 0xF2, 0x90, 0x04, 0x90, 0x04, 0x48, 0x18, 0x44, 0xE0, 0x23, // Code for char @ 44 | 0x0A, 0x00, 0x00, 0x00, 0x18, 0x00, 0x07, 0xE0, 0x01, 0x1C, 0x01, 0x02, 0x01, 0x1C, 0x01, 0xE0, 0x01, 0x00, 0x07, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char A 45 | 0x0A, 0x00, 0x00, 0xFE, 0x1F, 0x42, 0x10, 0x42, 0x10, 0x42, 0x10, 0x42, 0x10, 0x42, 0x10, 0x42, 0x10, 0xA6, 0x08, 0x18, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char B 46 | 0x0B, 0x00, 0x00, 0xF0, 0x03, 0x08, 0x04, 0x04, 0x08, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x04, 0x08, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char C 47 | 0x0B, 0x00, 0x00, 0xFE, 0x1F, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x04, 0x08, 0x08, 0x04, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char D 48 | 0x0A, 0x00, 0x00, 0xFE, 0x1F, 0x42, 0x10, 0x42, 0x10, 0x42, 0x10, 0x42, 0x10, 0x42, 0x10, 0x42, 0x10, 0x42, 0x10, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char E 49 | 0x09, 0x00, 0x00, 0xFE, 0x1F, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char F 50 | 0x0B, 0x00, 0x00, 0xF0, 0x03, 0x08, 0x04, 0x04, 0x08, 0x02, 0x10, 0x02, 0x10, 0x82, 0x10, 0x82, 0x10, 0x84, 0x08, 0x88, 0x04, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char G 51 | 0x0A, 0x00, 0x00, 0xFE, 0x1F, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xFE, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char H 52 | 0x02, 0x00, 0x00, 0xFE, 0x1F, 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, // Code for char I 53 | 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0xFE, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char J 54 | 0x0A, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x01, 0x80, 0x00, 0x40, 0x00, 0xE0, 0x00, 0x10, 0x01, 0x08, 0x06, 0x04, 0x08, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char K 55 | 0x08, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char L 56 | 0x0C, 0x00, 0x00, 0xFE, 0x1F, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x00, 0x07, 0x00, 0x18, 0x00, 0x07, 0xC0, 0x00, 0x30, 0x00, 0x0C, 0x00, 0xFE, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char M 57 | 0x0A, 0x00, 0x00, 0xFE, 0x1F, 0x04, 0x00, 0x18, 0x00, 0x20, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x08, 0xFE, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char N 58 | 0x0B, 0x00, 0x00, 0xF0, 0x03, 0x08, 0x04, 0x04, 0x08, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x04, 0x08, 0x08, 0x04, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char O 59 | 0x0A, 0x00, 0x00, 0xFE, 0x1F, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x44, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char P 60 | 0x0B, 0x00, 0x00, 0xF0, 0x03, 0x08, 0x04, 0x04, 0x08, 0x02, 0x10, 0x02, 0x10, 0x02, 0x14, 0x02, 0x14, 0x04, 0x08, 0x08, 0x1C, 0xF0, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Q 61 | 0x0A, 0x00, 0x00, 0xFE, 0x1F, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x01, 0x82, 0x06, 0x44, 0x08, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char R 62 | 0x0A, 0x00, 0x00, 0x18, 0x04, 0x24, 0x08, 0x42, 0x10, 0x42, 0x10, 0x42, 0x10, 0x82, 0x10, 0x82, 0x10, 0x84, 0x08, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char S 63 | 0x09, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0xFE, 0x1F, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char T 64 | 0x0A, 0x00, 0x00, 0xFE, 0x07, 0x00, 0x08, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x08, 0xFE, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char U 65 | 0x0A, 0x00, 0x00, 0x06, 0x00, 0x38, 0x00, 0xC0, 0x01, 0x00, 0x06, 0x00, 0x18, 0x00, 0x06, 0xC0, 0x01, 0x38, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char V 66 | 0x0F, 0x06, 0x00, 0x78, 0x00, 0x80, 0x07, 0x00, 0x18, 0x00, 0x07, 0xE0, 0x00, 0x1C, 0x00, 0x02, 0x00, 0x1C, 0x00, 0xE0, 0x00, 0x00, 0x07, 0x00, 0x18, 0x80, 0x07, 0x78, 0x00, 0x06, 0x00, 0x00, 0x00, // Code for char W 67 | 0x0B, 0x00, 0x10, 0x02, 0x08, 0x04, 0x04, 0x18, 0x03, 0xA0, 0x00, 0x40, 0x00, 0xA0, 0x00, 0x18, 0x03, 0x04, 0x04, 0x02, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char X 68 | 0x09, 0x02, 0x00, 0x0C, 0x00, 0x10, 0x00, 0x60, 0x00, 0x80, 0x1F, 0x60, 0x00, 0x10, 0x00, 0x0C, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Y 69 | 0x09, 0x00, 0x10, 0x02, 0x18, 0x02, 0x16, 0x02, 0x11, 0xC2, 0x10, 0x22, 0x10, 0x1A, 0x10, 0x06, 0x10, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char Z 70 | 0x04, 0x00, 0x00, 0xFE, 0xFF, 0x02, 0x80, 0x02, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char [ 71 | 0x04, 0x06, 0x00, 0x78, 0x00, 0x80, 0x07, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char BackSlash 72 | 0x03, 0x02, 0x80, 0x02, 0x80, 0xFE, 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, // Code for char ] 73 | 0x07, 0x40, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x02, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ^ 74 | 0x09, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char _ 75 | 0x03, 0x00, 0x00, 0x02, 0x00, 0x04, 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, // Code for char ` 76 | 0x08, 0x00, 0x00, 0x40, 0x0E, 0x20, 0x11, 0x10, 0x11, 0x10, 0x11, 0x90, 0x10, 0x90, 0x08, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char a 77 | 0x08, 0x00, 0x00, 0xFE, 0x1F, 0x20, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x08, 0xC0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char b 78 | 0x07, 0x00, 0x00, 0xC0, 0x07, 0x20, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char c 79 | 0x08, 0x00, 0x00, 0xC0, 0x07, 0x20, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x08, 0xFE, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char d 80 | 0x08, 0x00, 0x00, 0xC0, 0x07, 0x20, 0x09, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x20, 0x09, 0xC0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char e 81 | 0x04, 0x10, 0x00, 0xFC, 0x1F, 0x12, 0x00, 0x12, 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, // Code for char f 82 | 0x08, 0x00, 0x00, 0xC0, 0x47, 0x20, 0x88, 0x10, 0x90, 0x10, 0x90, 0x10, 0x90, 0x20, 0x48, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char g 83 | 0x07, 0x00, 0x00, 0xFE, 0x1F, 0x20, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char h 84 | 0x02, 0x00, 0x00, 0xF2, 0x1F, 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, // Code for char i 85 | 0x02, 0x00, 0x80, 0xF2, 0x7F, 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, // Code for char j 86 | 0x08, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x02, 0x00, 0x01, 0x80, 0x01, 0x40, 0x06, 0x20, 0x08, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char k 87 | 0x02, 0x00, 0x00, 0xFE, 0x1F, 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, // Code for char l 88 | 0x0C, 0x00, 0x00, 0xF0, 0x1F, 0x20, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0xE0, 0x1F, 0x20, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char m 89 | 0x07, 0x00, 0x00, 0xF0, 0x1F, 0x20, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char n 90 | 0x08, 0x00, 0x00, 0xC0, 0x07, 0x20, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x08, 0xC0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char o 91 | 0x08, 0x00, 0x00, 0xF0, 0xFF, 0x20, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x08, 0xC0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char p 92 | 0x08, 0x00, 0x00, 0xC0, 0x07, 0x20, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x08, 0xF0, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char q 93 | 0x05, 0x00, 0x00, 0xF0, 0x1F, 0x20, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char r 94 | 0x07, 0x00, 0x00, 0xE0, 0x08, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x20, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char s 95 | 0x04, 0x10, 0x00, 0xFC, 0x1F, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char t 96 | 0x07, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x08, 0xF0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char u 97 | 0x07, 0x30, 0x00, 0xC0, 0x00, 0x00, 0x07, 0x00, 0x18, 0x00, 0x07, 0xC0, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char v 98 | 0x0B, 0x30, 0x00, 0xC0, 0x07, 0x00, 0x18, 0x00, 0x07, 0xC0, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x00, 0x07, 0x00, 0x18, 0xC0, 0x07, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char w 99 | 0x07, 0x10, 0x10, 0x20, 0x08, 0xC0, 0x06, 0x00, 0x01, 0xC0, 0x06, 0x20, 0x08, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char x 100 | 0x07, 0x70, 0x00, 0x80, 0x81, 0x00, 0x8E, 0x00, 0x70, 0x00, 0x0E, 0xC0, 0x01, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char y 101 | 0x07, 0x10, 0x10, 0x10, 0x18, 0x10, 0x16, 0x10, 0x11, 0xD0, 0x10, 0x30, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char z 102 | 0x05, 0x00, 0x01, 0x00, 0x01, 0xFC, 0x7E, 0x02, 0x80, 0x02, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char { 103 | 0x02, 0x00, 0x00, 0xFE, 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, // Code for char | 104 | 0x05, 0x02, 0x80, 0x02, 0x80, 0xFC, 0x7E, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char } 105 | 0x08, 0x80, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Code for char ~ 106 | 0x04, 0xFE, 0x0F, 0x02, 0x08, 0x02, 0x08, 0xFE, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char  107 | }; 108 | 109 | extern const PROGMEM LcdFont font16x16 = 110 | { 111 | glcd16x16, // font data 112 | 0x0020, // first character code 113 | 0x007F, // last character code 114 | 16, // row height in pixels 115 | 16, // character width in pixels 116 | 1 // number of space pixels between characters before kerning 117 | }; 118 | 119 | 120 | // End 121 | -------------------------------------------------------------------------------- /Libraries/Lcd7920/lcd7920.cpp: -------------------------------------------------------------------------------- 1 | // Driver for 128x634 graphical LCD with ST7920 controller 2 | // D Crocker, Escher Technologies Ltd. 3 | 4 | #include "lcd7920.h" 5 | #include 6 | #include 7 | 8 | // LCD basic instructions. These all take 72us to execute except LcdDisplayClear, which takes 1.6ms 9 | const uint8_t LcdDisplayClear = 0x01; 10 | const uint8_t LcdHome = 0x02; 11 | const uint8_t LcdEntryModeSet = 0x06; // move cursor right and increment address when writing data 12 | const uint8_t LcdDisplayOff = 0x08; 13 | const uint8_t LcdDisplayOn = 0x0C; // add 0x02 for cursor on and/or 0x01 for cursor blink on 14 | const uint8_t LcdFunctionSetBasicAlpha = 0x20; 15 | const uint8_t LcdFunctionSetBasicGraphic = 0x22; 16 | const uint8_t LcdFunctionSetExtendedAlpha = 0x24; 17 | const uint8_t LcdFunctionSetExtendedGraphic = 0x26; 18 | const uint8_t LcdSetDdramAddress = 0x80; // add the address we want to set 19 | 20 | // LCD extended instructions 21 | const uint8_t LcdSetGdramAddress = 0x80; 22 | 23 | const unsigned int LcdCommandDelayMicros = 72; // 72us required 24 | const unsigned int LcdDataDelayMicros = 6; // Delay between sending data bytes 25 | const unsigned int LcdDisplayClearDelayMillis = 3; // 1.6ms should be enough 26 | 27 | const unsigned int numRows = 64; 28 | const unsigned int numCols = 128; 29 | 30 | Lcd7920::Lcd7920(uint8_t p_clockPin, uint8_t p_dataPin, uint8_t p_csPin, bool spi) 31 | : useSpi(spi), textInverted(false), justSetCursor(false), clockPin(p_clockPin), dataPin(p_dataPin), csPin(p_csPin), currentFont(nullptr) 32 | { 33 | } 34 | 35 | // NB - if using SPI then the SS pin must be set to be an output before calling this, or else csPin must be the SS pin! 36 | void Lcd7920::begin() 37 | { 38 | // Set up the SPI interface for talking to the LCD. We have to set MOSI, SCLK and SS to outputs, then enable SPI. 39 | pinMode(csPin, OUTPUT); 40 | digitalWrite(csPin, LOW); // CS is active high on the ST7920 41 | pinMode(clockPin, OUTPUT); 42 | digitalWrite(clockPin, LOW); 43 | pinMode(dataPin, OUTPUT); 44 | digitalWrite(dataPin, LOW); 45 | 46 | if (useSpi) 47 | { 48 | PRR &= ~(1u << PRSPI); 49 | asm volatile("nop"); 50 | asm volatile("nop"); 51 | SPCR = (1u << SPE) | (1u << MSTR) | (1u << SPR0); // enable SPI, master mode, clock low when idle, data sampled on rising edge, clock = f/16 (= 1MHz), send MSB first 52 | // SPSR = (1 << SPI2X); // double the speed to 2MHz (optional) 53 | } 54 | 55 | AssertCS(); 56 | sendLcdCommand(LcdFunctionSetBasicAlpha); 57 | delay(2); 58 | sendLcdCommand(LcdFunctionSetBasicAlpha); 59 | commandDelay(); 60 | sendLcdCommand(LcdEntryModeSet); 61 | commandDelay(); 62 | sendLcdCommand(LcdDisplayClear); // need this on some displays to ensure that the alpha RAM is clear (M3D Kanji problem) 63 | delay(LcdDisplayClearDelayMillis); 64 | sendLcdCommand(LcdFunctionSetExtendedGraphic); 65 | commandDelay(); 66 | DeassertCS(); 67 | 68 | clear(); // clear graphics ram 69 | flush(); 70 | 71 | AssertCS(); 72 | sendLcdCommand(LcdDisplayOn); 73 | commandDelay(); 74 | DeassertCS(); 75 | 76 | currentFont = nullptr; 77 | } 78 | 79 | size_t Lcd7920::write(uint8_t ch) 80 | { 81 | if (ch == '\n') 82 | { 83 | setCursor(row + currentFont->height + 1, 0); 84 | } 85 | else 86 | { 87 | if (column < rightMargin && currentFont != nullptr) // keep column <= rightMargin in the following code 88 | { 89 | const uint16_t startChar = pgm_read_word_near(&(currentFont->startCharacter)); 90 | const uint16_t endChar = pgm_read_word_near(&(currentFont->endCharacter)); 91 | 92 | if (ch < startChar || ch > endChar) 93 | { 94 | return 0; 95 | } 96 | 97 | const uint8_t fontWidth = pgm_read_byte_near(&(currentFont->width)); 98 | const uint8_t fontHeight = pgm_read_byte_near(&(currentFont->height)); 99 | const uint8_t bytesPerColumn = (fontHeight + 7)/8; 100 | const uint8_t bytesPerChar = (bytesPerColumn * fontWidth) + 1; 101 | const PROGMEM_PTR uint8_t * PROGMEM fontPtr = (const PROGMEM_PTR uint8_t*)pgm_read_word_near(&(currentFont->ptr)) + (bytesPerChar * (ch - startChar)); 102 | uint16_t cmask = (1u << fontHeight) - 1u; 103 | 104 | uint8_t nCols = pgm_read_byte_near(fontPtr++); 105 | 106 | // Update dirty rectangle coordinates, except for endCol which we do at the end 107 | { 108 | if (startRow > row) { startRow = row; } 109 | if (startCol > column) { startCol = column; } 110 | uint8_t nextRow = row + fontHeight; 111 | if (nextRow > numRows) { nextRow = numRows; } 112 | if (endRow < nextRow) { endRow = nextRow; } 113 | } 114 | 115 | // Decide whether to add a space column first (auto-kerning) 116 | // We don't add a space column before a space character. 117 | // We add a space column after a space character if we would have added one between the preceding and following characters. 118 | if (column < rightMargin) 119 | { 120 | uint16_t thisCharColData = pgm_read_word_near(fontPtr) & cmask; // atmega328p is little-endian 121 | if (thisCharColData == 0) // for characters with deliberate space row at the start, e.g. decimal point 122 | { 123 | thisCharColData = pgm_read_word_near(fontPtr + 2) & cmask; 124 | } 125 | bool wantSpace = ((thisCharColData | (thisCharColData << 1)) & (lastCharColData | (lastCharColData << 1))) != 0; 126 | if (wantSpace) 127 | { 128 | // Add space after character 129 | uint8_t mask = 0x80 >> (column & 7); 130 | uint8_t *p = image + ((row * (numCols/8)) + (column/8)); 131 | for (uint8_t i = 0; i < fontHeight && p < (image + sizeof(image)); ++i) 132 | { 133 | if (textInverted) 134 | { 135 | *p |= mask; 136 | } 137 | else 138 | { 139 | *p &= ~mask; 140 | } 141 | p += (numCols/8); 142 | } 143 | ++column; 144 | } 145 | } 146 | 147 | while (nCols != 0 && column < rightMargin) 148 | { 149 | uint16_t colData = pgm_read_word_near(fontPtr); 150 | fontPtr += bytesPerColumn; 151 | if (colData != 0) 152 | { 153 | lastCharColData = colData & cmask; 154 | } 155 | uint8_t mask1 = 0x80 >> (column & 7); 156 | uint8_t mask2 = ~mask1; 157 | uint8_t *p = image + ((row * (numCols/8)) + (column/8)); 158 | const uint16_t setPixelVal = (textInverted) ? 0 : 1; 159 | for (uint8_t i = 0; i < fontHeight && p < (image + sizeof(image)); ++i) 160 | { 161 | if ((colData & 1u) == setPixelVal) 162 | { 163 | *p |= mask1; // set pixel 164 | } 165 | else 166 | { 167 | *p &= mask2; // clear pixel 168 | } 169 | colData >>= 1; 170 | p += (numCols/8); 171 | } 172 | --nCols; 173 | ++column; 174 | } 175 | 176 | if (column > endCol) { endCol = column; } 177 | } 178 | justSetCursor = false; 179 | } 180 | return 1; 181 | } 182 | 183 | // Set the right margin. In graphics mode, anything written will be truncated at the right margin. Defaults to the right hand edge of the display. 184 | void Lcd7920::setRightMargin(uint8_t r) 185 | { 186 | rightMargin = (r > numCols) ? numCols : r; 187 | } 188 | 189 | // Clear a rectangle from the current position to the right margin (graphics mode only). The height of the rectangle is the height of the current font. 190 | void Lcd7920::clearToMargin() 191 | { 192 | if (currentFont != nullptr) 193 | { 194 | if (column < rightMargin) 195 | { 196 | const uint8_t fontHeight = pgm_read_byte_near(&(currentFont->height)); 197 | // Update dirty rectangle coordinates 198 | { 199 | if (startRow > row) { startRow = row; } 200 | if (startCol > column) { startCol = column; } 201 | uint8_t nextRow = row + fontHeight; 202 | if (nextRow > numRows) { nextRow = numRows; } 203 | if (endRow < nextRow) { endRow = nextRow; } 204 | if (endCol < rightMargin) { endCol = rightMargin; } 205 | } 206 | while (column < rightMargin) 207 | { 208 | // Add space after character 209 | uint8_t mask = 0x80 >> (column & 7); 210 | uint8_t *p = image + ((row * (numCols/8)) + (column/8)); 211 | for (uint8_t i = 0; i < fontHeight && p < (image + sizeof(image)); ++i) 212 | { 213 | if (textInverted) 214 | { 215 | *p |= mask; 216 | } 217 | else 218 | { 219 | *p &= ~mask; 220 | } 221 | p += (numCols/8); 222 | } 223 | ++column; 224 | } 225 | } 226 | } 227 | } 228 | 229 | // Select normal or inverted text (only works in graphics mode) 230 | void Lcd7920::textInvert(bool b) 231 | { 232 | if (b != textInverted) 233 | { 234 | textInverted = b; 235 | if (!justSetCursor) 236 | { 237 | lastCharColData = 0xFFFF; // always need space between inverted and non-inverted text 238 | } 239 | } 240 | } 241 | 242 | void Lcd7920::setFont(const PROGMEM_PTR LcdFont *newFont) 243 | { 244 | currentFont = newFont; 245 | } 246 | 247 | void Lcd7920::clear() 248 | { 249 | memset(image, 0, sizeof(image)); 250 | // Now flag the whole image as dirty 251 | startRow = 0; 252 | endRow = numRows; 253 | startCol = 0; 254 | endCol = numCols; 255 | setCursor(0, 0); 256 | textInverted = false; 257 | rightMargin = numCols; 258 | } 259 | 260 | // Draw a line using the Bresenham Algorithm (thanks Wikipedia) 261 | void Lcd7920::line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, PixelMode mode) 262 | { 263 | int dx = (x1 >= x0) ? x1 - x0 : x0 - x1; 264 | int dy = (y1 >= y0) ? y1 - y0 : y0 - y1; 265 | int sx = (x0 < x1) ? 1 : -1; 266 | int sy = (y0 < y1) ? 1 : -1; 267 | int err = dx - dy; 268 | 269 | for (;;) 270 | { 271 | setPixel(x0, y0, mode); 272 | if (x0 == x1 && y0 == y1) break; 273 | int e2 = err + err; 274 | if (e2 > -dy) 275 | { 276 | err -= dy; 277 | x0 += sx; 278 | } 279 | if (e2 < dx) 280 | { 281 | err += dx; 282 | y0 += sy; 283 | } 284 | } 285 | } 286 | 287 | // Draw a circle using the Bresenham Algorithm (thanks Wikipedia) 288 | void Lcd7920::circle(uint8_t x0, uint8_t y0, uint8_t radius, PixelMode mode) 289 | { 290 | int f = 1 - (int)radius; 291 | int ddF_x = 1; 292 | int ddF_y = -2 * (int)radius; 293 | int x = 0; 294 | int y = radius; 295 | 296 | setPixel(x0, y0 + radius, mode); 297 | setPixel(x0, y0 - radius, mode); 298 | setPixel(x0 + radius, y0, mode); 299 | setPixel(x0 - radius, y0, mode); 300 | 301 | while(x < y) 302 | { 303 | // keep ddF_x == 2 * x + 1; 304 | // keep ddF_y == -2 * y; 305 | // keep f == x*x + y*y - radius*radius + 2*x - y + 1; 306 | if(f >= 0) 307 | { 308 | y--; 309 | ddF_y += 2; 310 | f += ddF_y; 311 | } 312 | x++; 313 | ddF_x += 2; 314 | f += ddF_x; 315 | setPixel(x0 + x, y0 + y, mode); 316 | setPixel(x0 - x, y0 + y, mode); 317 | setPixel(x0 + x, y0 - y, mode); 318 | setPixel(x0 - x, y0 - y, mode); 319 | setPixel(x0 + y, y0 + x, mode); 320 | setPixel(x0 - y, y0 + x, mode); 321 | setPixel(x0 + y, y0 - x, mode); 322 | setPixel(x0 - y, y0 - x, mode); 323 | } 324 | } 325 | 326 | // Draw a bitmap 327 | void Lcd7920::bitmap(uint8_t x0, uint8_t y0, uint8_t width, uint8_t height, const PROGMEM_PTR uint8_t data[]) 328 | { 329 | for (uint8_t r = 0; r < height && r + y0 < numRows; ++r) 330 | { 331 | uint8_t *p = image + (((r + y0) * (numCols/8)) + (x0/8)); 332 | uint16_t bitMapOffset = r * (width/8); 333 | for (uint8_t c = 0; c < (width/8) && c + (x0/8) < numCols/8; ++c) 334 | { 335 | *p++ = pgm_read_byte_near(data + bitMapOffset++); 336 | } 337 | } 338 | if (x0 < startCol) startCol = x0; 339 | if (x0 + width > endCol) endCol = x0 + width; 340 | if (y0 < startRow) startRow = y0; 341 | if (y0 + height > endRow) endRow = y0 + height; 342 | } 343 | 344 | // Flush the dirty part of the image to the lcd 345 | void Lcd7920::flush() 346 | { 347 | if (endCol > startCol && endRow > startRow) 348 | { 349 | AssertCS(); 350 | uint8_t startColNum = startCol/16u; 351 | uint8_t endColNum = (endCol + 15)/16u; 352 | for (uint8_t r = startRow; r < endRow; ++r) 353 | { 354 | setGraphicsAddress(r, startColNum); 355 | const uint8_t *ptr = image + (((numCols/8) * r) + (2 * startColNum)); 356 | for (uint8_t i = startColNum; i < endColNum; ++i) 357 | { 358 | sendLcdData(*ptr++); 359 | //delayMicroseconds(LcdDataDelayMicros); 360 | sendLcdData(*ptr++); 361 | delayMicroseconds(LcdDataDelayMicros); 362 | } 363 | } 364 | startRow = numRows; 365 | startCol = numCols; 366 | endCol = endRow = 0; 367 | DeassertCS(); 368 | } 369 | } 370 | 371 | // Set the cursor position 372 | void Lcd7920::setCursor(uint8_t r, uint8_t c) 373 | { 374 | row = r; 375 | column = c; 376 | lastCharColData = 0u; // flag that we just set the cursor position, so no space before next character 377 | justSetCursor = true; 378 | } 379 | 380 | void Lcd7920::setPixel(uint8_t x, uint8_t y, PixelMode mode) 381 | { 382 | if (y < numRows && x < rightMargin) 383 | { 384 | uint8_t *p = image + ((y * (numCols/8)) + (x/8)); 385 | uint8_t mask = 0x80u >> (x%8); 386 | switch(mode) 387 | { 388 | case PixelClear: 389 | *p &= ~mask; 390 | break; 391 | case PixelSet: 392 | *p |= mask; 393 | break; 394 | case PixelFlip: 395 | *p ^= mask; 396 | break; 397 | } 398 | 399 | // Change the dirty rectangle to account for a pixel being dirty (we assume it was changed) 400 | if (startRow > y) { startRow = y; } 401 | if (endRow <= y) { endRow = y + 1; } 402 | if (startCol > x) { startCol = x; } 403 | if (endCol <= x) { endCol = x + 1; } 404 | } 405 | } 406 | 407 | bool Lcd7920::readPixel(uint8_t x, uint8_t y) const 408 | { 409 | if (y < numRows && x < numCols) 410 | { 411 | const uint8_t *p = image + ((y * (numCols/8)) + (x/8)); 412 | return (*p & (0x80u >> (x%8))) != 0; 413 | } 414 | return false; 415 | } 416 | 417 | // Set the address to write to. The column address is in 16-bit words, so it ranges from 0 to 7. 418 | void Lcd7920::setGraphicsAddress(unsigned int r, unsigned int c) 419 | { 420 | sendLcdCommand(LcdSetGdramAddress | (r & 31)); 421 | //commandDelay(); // don't seem to need this one 422 | sendLcdCommand(LcdSetGdramAddress | c | ((r & 32u) >> 2)); 423 | commandDelay(); // we definitely need this one 424 | } 425 | 426 | void Lcd7920::commandDelay() 427 | { 428 | delayMicroseconds(LcdCommandDelayMicros); 429 | } 430 | 431 | // Send a command to the LCD 432 | void Lcd7920::sendLcdCommand(uint8_t command) 433 | { 434 | sendLcd(0xF8, command); 435 | } 436 | 437 | // Send a data byte to the LCD 438 | void Lcd7920::sendLcdData(uint8_t data) 439 | { 440 | sendLcd(0xFA, data); 441 | } 442 | 443 | // Send a command to the lcd. Data1 is sent as-is, data2 is split into 2 bytes, high nibble first. 444 | void Lcd7920::sendLcd(uint8_t data1, uint8_t data2) 445 | { 446 | if (useSpi) 447 | { 448 | SPDR = data1; 449 | while ((SPSR & (1u << SPIF)) == 0) { } 450 | SPDR = data2 & 0xF0; 451 | while ((SPSR & (1u << SPIF)) == 0) { } 452 | SPDR = data2 << 4; 453 | while ((SPSR & (1u << SPIF)) == 0) { } 454 | } 455 | else 456 | { 457 | sendLcdSlow(data1); 458 | sendLcdSlow(data2 & 0xF0); 459 | sendLcdSlow(data2 << 4); 460 | } 461 | } 462 | 463 | void Lcd7920::sendLcdSlow(uint8_t data) 464 | { 465 | #if 1 466 | 467 | // Fast shiftOut function 468 | volatile uint8_t * const sclkPort = portOutputRegister(digitalPinToPort(clockPin)); 469 | volatile uint8_t * const mosiPort = portOutputRegister(digitalPinToPort(dataPin)); 470 | const uint8_t sclkMask = digitalPinToBitMask(clockPin); 471 | const uint8_t mosiMask = digitalPinToBitMask(dataPin); 472 | 473 | // uint8_t oldSREG = SREG; 474 | // cli(); 475 | for (uint8_t i = 0; i < 8; ++i) 476 | { 477 | if (data & 0x80) 478 | { 479 | *mosiPort |= mosiMask; 480 | } 481 | else 482 | { 483 | *mosiPort &= ~mosiMask; 484 | } 485 | *sclkPort |= sclkMask; 486 | data <<= 1; 487 | *sclkPort &= ~sclkMask; 488 | } 489 | // SREG = oldSREG; 490 | 491 | #else 492 | 493 | // really slow version, like Arduino shiftOut function 494 | for (uint8_t i = 0; i < 8; ++i) 495 | { 496 | // The following could be made much faster using direct port manipulation, however we normally use SPI anyway. 497 | // We could use the Arduino shiftOut function, but that is even slower than this. 498 | digitalWrite(dataPin, (data & 0x80) ? HIGH : LOW); 499 | digitalWrite(clockPin, HIGH); 500 | digitalWrite(clockPin, LOW); 501 | data <<= 1; 502 | } 503 | 504 | #endif 505 | } 506 | 507 | void Lcd7920::AssertCS() 508 | { 509 | digitalWrite(csPin, HIGH); 510 | //delayMicroseconds(20); 511 | } 512 | 513 | void Lcd7920::DeassertCS() 514 | { 515 | //delayMicroseconds(1); 516 | digitalWrite(csPin, LOW); 517 | } 518 | 519 | // End 520 | -------------------------------------------------------------------------------- /Libraries/Lcd7920/lcd7920.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define PROGMEM_PTR // nothing (used to flag that the pointer points to an object in PROGMEM) 6 | 7 | // Enumeration for specifying drawing modes 8 | enum PixelMode 9 | { 10 | PixelClear = 0, // clear the pixel(s) 11 | PixelSet = 1, // set the pixel(s) 12 | PixelFlip = 2 // invert the pixel(s) 13 | }; 14 | 15 | // Struct for describing a font table, always held in PROGMEM 16 | struct LcdFont 17 | { 18 | const PROGMEM_PTR uint8_t *ptr; // pointer to font table 19 | uint16_t startCharacter; // character code (e.g. ASCII) of the first character in the font 20 | uint16_t endCharacter; // character code of the last character in the font 21 | uint8_t height; // row height in pixels - only this number of pixels will be fetched and drawn - maximum 16 in this version of the software 22 | uint8_t width; // max character width in pixels (the font table contains this number of bytes or words per character, plus 1 for the active width) 23 | uint8_t numSpaces; // number of space columns between characters before kerning 24 | }; 25 | 26 | // Class for driving 128x64 graphical LCD fitted with ST7920 controller 27 | // This drives the GLCD in serial mode so that it needs just 2 pins. 28 | // Preferably, we use SPI to do the comms. 29 | 30 | // Derive the LCD class from the Print class so that we can print stuff to it in alpha mode 31 | class Lcd7920 : public Print 32 | { 33 | public: 34 | // Construct a GLCD driver. 35 | // cPin = clock pin (connects to E pin of ST7920) 36 | // dPin = data pin (connects to R/W pin of ST7920) 37 | // useSpi = true to use hardware SPI. If true then cPin must correspond to SCLK, dPin must correspond to MOSI, and SS must be configured as an output. 38 | Lcd7920(uint8_t p_clockPin, uint8_t p_dataPin, uint8_t p_csPin, bool spi); // constructor 39 | 40 | // Write a single character in the current font. Called by the 'print' functions. Works in both graphic and alphanumeric mode. 41 | // If in graphic mode, a call to setFont must have been made before calling this. 42 | // c = character to write 43 | // Returns the number of characters written (1 if we wrote it, 0 otherwise) 44 | virtual size_t write(uint8_t c); // write a character 45 | 46 | // Initialize the display. Call this in setup(). Also call setFont to select initial text font. 47 | void begin(); 48 | 49 | // Select the font to use for subsequent calls to write() in graphics mode. Must be called before calling write() in graphics mode. 50 | // newFont = pointer to font descriptor in PROGMEM 51 | void setFont(const PROGMEM_PTR LcdFont *newFont); 52 | 53 | // Select normal or inverted text (only works in graphics mode) 54 | void textInvert(bool b); 55 | 56 | // Clear the display. Works in both graphic and alphanumeric mode. Also selects non-inverted text. 57 | void clear(); 58 | 59 | // Set the cursor position 60 | // r = row. This is the number of pixels from the top of the display to the top of the character. 61 | // c = column. This is the number of pixels from the left hand edge of the display and the left hand edge of the character. 62 | void setCursor(uint8_t r, uint8_t c); 63 | 64 | // Get the cursor column. Useful we have written some text. 65 | uint8_t getColumn() const { return column; } 66 | 67 | // Set the right margin. In graphics mode, anything written will be truncated at the right margin. Defaults to the right hand edge of the display. 68 | void setRightMargin(uint8_t r); 69 | 70 | // Clear a rectangle from the current position to the right margin (graphics mode only). The height of the rectangle is the height of the current font. 71 | void clearToMargin(); 72 | 73 | // Flush the display buffer to the display. In graphics mode, calls to write, setPixel, line and circle will not be committed to the display until this is called. 74 | void flush(); 75 | 76 | // Set, clear or invert a pixel 77 | // x = x-coordinate of the pixel, measured from left hand edge of the display 78 | // y = y-coordinate of the pixel, measured down from the top of the display 79 | // mode = whether we want to set, clear or invert the pixel 80 | void setPixel(uint8_t x, uint8_t y, PixelMode mode); 81 | 82 | // Read a pixel. Returns true if the pixel is set, false if it is clear. 83 | // x = x-coordinate of the pixel, measured from left hand edge of the display 84 | // y = y-coordinate of the pixel, measured down from the top of the display 85 | bool readPixel(uint8_t x, uint8_t y) const; 86 | 87 | // Draw a line. 88 | // x0 = x-coordinate of one end of the line, measured from left hand edge of the display 89 | // y0 = y-coordinate of one end of the line, measured down from the top of the display 90 | // x1, y1 = coordinates of the other end od the line 91 | // mode = whether we want to set, clear or invert each pixel 92 | void line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, PixelMode mode); 93 | 94 | // Draw a circle 95 | // x0 = x-coordinate of the centre, measured from left hand edge of the display 96 | // y0 = y-coordinate of the centre, measured down from the top of the display 97 | // radius = radius of the circle in pixels 98 | // mode = whether we want to set, clear or invert each pixel 99 | void circle(uint8_t x0, uint8_t y0, uint8_t radius, PixelMode mode); 100 | 101 | // Draw a bitmap 102 | // x0 = x-coordinate of the top left, measured from left hand edge of the display. Currently, must be a multiple of 8. 103 | // y0 = y-coordinate of the top left, measured down from the top of the display 104 | // width = width of bitmap in pixels. Currently, must be a multiple of 8. 105 | // rows = height of bitmap in pixels 106 | // data = bitmap image in PROGMEM, must be ((width/8) * rows) bytes long 107 | void bitmap(uint8_t x0, uint8_t y0, uint8_t width, uint8_t height, const PROGMEM_PTR uint8_t data[]); 108 | 109 | private: 110 | bool useSpi; 111 | bool textInverted; 112 | bool justSetCursor; 113 | uint8_t clockPin, dataPin, csPin; 114 | uint16_t lastCharColData; // data for the last non-space column, used for kerning 115 | uint8_t row, column; 116 | uint8_t startRow, startCol, endRow, endCol; // coordinates of the dirty rectangle 117 | uint8_t rightMargin; 118 | uint8_t image[(128 * 64)/8]; // image buffer, 1K in size (= half the RAM of the Uno) 119 | const struct LcdFont *currentFont; // pointer to descriptor for current font 120 | 121 | void AssertCS(); 122 | void DeassertCS(); 123 | void sendLcdCommand(uint8_t command); 124 | void sendLcdData(uint8_t data); 125 | void sendLcd(uint8_t data1, uint8_t data2); 126 | void sendLcdSlow(uint8_t data); 127 | void commandDelay(); 128 | void setGraphicsAddress(unsigned int r, unsigned int c); 129 | }; 130 | -------------------------------------------------------------------------------- /Libraries/Lcd7920/lcd_retest/lcd_retest.ino: -------------------------------------------------------------------------------- 1 | // ST7920-based 128x64 pixel graphic LCD demo program 2 | // Written by D. Crocker, Escher Technologies Ltd. 3 | 4 | #include 5 | #include "lcd7920.h" 6 | 7 | extern PROGMEM LcdFont font10x10; // in glcd10x10.cpp 8 | extern PROGMEM LcdFont font16x16; // in glcs16x16.cpp 9 | 10 | // Define the following as true to use SPI to drive the LCD, false to drive it slowly instead 11 | #define LCD_USE_SPI (true) 12 | 13 | // Definition of Arduino type 14 | #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) 15 | #define IS_MEGA (1) 16 | #define IS_UNO (0) 17 | #else 18 | #define IS_MEGA (0) 19 | #define IS_UNO (1) 20 | #endif 21 | 22 | typedef unsigned char uint8_t; 23 | typedef unsigned int uint16_t; 24 | 25 | // Pins for serial LCD interface. For speed, we use the SPI interface, which means we need the MOSI and SCLK pins. 26 | // Connection of the 7920-based GLCD: 27 | // Vss to Gnd 28 | // Vdd to +5v 29 | // Vo through 10k variable resistor to +5v (not needed if your board has a contrast adjust pot on the back already, like mine does) 30 | // RS to +5v 31 | // PSB to gnd 32 | // RW to Arduino MOSI (see below for pin mapping) 33 | // E to Arduino SCLK (see below for pin mapping) 34 | // RST to +5v 35 | // D0-D7 unconnected 36 | // BLK to ground (or collector of NPN transistor if controlling backlight by PWM) 37 | // BLA via series resistor (if not included in LCD module) to +5v or other supply 38 | 39 | // The following pin numbers must not be changed because they must correspond to the processor MOSI and SCLK pins 40 | // We don't actually use the MISO pin, but it gets forced as an input when we enable SPI 41 | // The SS pin MUST be configured as an output (if it is used as an input and goes low, it will kill the communication with the LCD). 42 | #if IS_UNO 43 | const int MOSIpin = 11; 44 | const int MISOpin = 12; 45 | const int SCLKpin = 13; 46 | const int SSpin = 10; 47 | #else 48 | const int MOSIpin = 51; 49 | const int MISOpin = 50; 50 | const int SCLKpin = 52; 51 | const int SSpin = 53; 52 | #endif 53 | 54 | // End of configuration section 55 | 56 | // LCD 57 | static Lcd7920 lcd(SCLKpin, MOSIpin, LCD_USE_SPI); 58 | 59 | // Initialization 60 | void setup() 61 | { 62 | pinMode(SSpin, OUTPUT); // must do this before we use the lcd in SPI mode 63 | 64 | lcd.begin(true); // init lcd in graphics mode 65 | } 66 | 67 | // Find out how much free memory we have 68 | unsigned int getFreeMemory() 69 | { 70 | uint8_t* temp = (uint8_t*)malloc(16); // assumes there are no free holes so this is allocated at the end 71 | unsigned int rslt = (uint8_t*)SP - temp; 72 | free(temp); 73 | return rslt; 74 | } 75 | 76 | void loop() 77 | { 78 | lcd.setFont(&font10x10); 79 | lcd.setCursor(0, 0); 80 | lcd.print("Hello "); 81 | lcd.setFont(&font16x16); 82 | lcd.print("world!"); 83 | 84 | lcd.circle(110, 31, 12, PixelSet); 85 | lcd.circle(110, 31, 16, PixelSet); 86 | lcd.line(3, 60, 127, 33, PixelFlip); 87 | 88 | lcd.setCursor(44, 14); 89 | lcd.setFont(&font10x10); 90 | lcd.print("Free RAM "); 91 | lcd.print(getFreeMemory()); 92 | lcd.print(" bytes "); 93 | lcd.flush(); 94 | 95 | delay(200); 96 | } 97 | 98 | // End 99 | -------------------------------------------------------------------------------- /Libraries/Lcd7920/library.properties: -------------------------------------------------------------------------------- 1 | name=Lcd7920 2 | version=1.0.0 3 | author=dc42 4 | maintainer=D Crocker 5 | sentence=Driver for 128x85 displays using ST7920 controller 6 | paragraph=Includes variable width fonts and supports auto kerning 7 | category=Display 8 | architectures=* -------------------------------------------------------------------------------- /Libraries/PushButton/PushButton.cpp: -------------------------------------------------------------------------------- 1 | #include "PushButton.h" 2 | #include "arduino.h" 3 | 4 | static const int debounceCount = 10; 5 | 6 | void PushButton::init() 7 | { 8 | pinMode(pin, INPUT_PULLUP); 9 | count = 0; 10 | state = false; 11 | newPress = false; 12 | } 13 | 14 | // Call this every 1ms or so 15 | void PushButton::poll() 16 | { 17 | bool b = !digitalRead(pin); 18 | if (b != state) 19 | { 20 | ++count; 21 | if (count == debounceCount) 22 | { 23 | state = b; 24 | count = 0; 25 | if (state) 26 | { 27 | newPress = true; 28 | } 29 | } 30 | } 31 | else if (count > 0) 32 | { 33 | --count; 34 | } 35 | } 36 | 37 | // End 38 | 39 | -------------------------------------------------------------------------------- /Libraries/PushButton/PushButton.h: -------------------------------------------------------------------------------- 1 | #ifndef __PushButton_Included 2 | #define __PushButton_Included 3 | 4 | class PushButton 5 | { 6 | bool state; 7 | bool newPress; 8 | int count; 9 | int pin; 10 | 11 | public: 12 | PushButton(int p) : pin(p) {} 13 | void init(); 14 | void poll(); 15 | 16 | // Return the debounced state 17 | bool getState() 18 | { 19 | return state; 20 | } 21 | 22 | bool getNewPress() 23 | { 24 | if (newPress) 25 | { 26 | newPress = false; 27 | return true; 28 | } 29 | return false; 30 | } 31 | }; 32 | 33 | #endif 34 | 35 | 36 | -------------------------------------------------------------------------------- /Libraries/PushButton/library.properties: -------------------------------------------------------------------------------- 1 | name=PushButton 2 | version=1.0.0 3 | author=dc42 4 | maintainer=D Crocker 5 | sentence=Driver for push buttons 6 | paragraph=Includes debouncing. 7 | category=Sensors 8 | architectures=* -------------------------------------------------------------------------------- /Libraries/RotaryEncoder/RotaryEncoder.cpp: -------------------------------------------------------------------------------- 1 | #include "RotaryEncoder.h" 2 | #include "arduino.h" 3 | 4 | void RotaryEncoder::init() 5 | { 6 | pinMode(pin0, INPUT_PULLUP); 7 | pinMode(pin1, INPUT_PULLUP); 8 | change = 0; 9 | delay(2); // ensure we read the initial state correctly 10 | state = readState(); 11 | } 12 | 13 | void RotaryEncoder::poll() 14 | { 15 | // State transition table. Each entry has the following meaning: 16 | // 0 - the encoder hasn't moved 17 | // 1 - the encoder has moved 1 unit clockwise 18 | // -1 = the encoder has moved 1 unit anticlockwise 19 | // 2 = illegal transition, we must have missed a state 20 | static int tbl[16] = 21 | { 0, +1, -1, 0, // position 3 = 00 to 11, can't really do anything, so 0 22 | -1, 0, -2, +1, // position 2 = 01 to 10, assume it was a bounce and should be 01 -> 00 -> 10 23 | +1, +2, 0, -1, // position 1 = 10 to 01, assume it was a bounce and should be 10 -> 00 -> 01 24 | 0, -1, +1, 0 // position 0 = 11 to 10, can't really do anything 25 | }; 26 | unsigned int t = readState(); 27 | int movement = tbl[(state << 2) | t]; 28 | if (movement != 0) 29 | { 30 | change += movement; 31 | state = t; 32 | } 33 | } 34 | 35 | unsigned int RotaryEncoder::readState() 36 | { 37 | return (digitalRead(pin0) ? 1u : 0u) | (digitalRead(pin1) ? 2u : 0u); 38 | } 39 | 40 | int RotaryEncoder::getChange() 41 | { 42 | int r; 43 | noInterrupts(); 44 | if (change >= ppc - 1) 45 | { 46 | r = (change + 1)/ppc; 47 | } 48 | else if (change <= 1 - ppc) 49 | { 50 | r = -((1 - change)/ppc); 51 | } 52 | else 53 | { 54 | r = 0; 55 | } 56 | change -= (r * ppc); 57 | interrupts(); 58 | return r; 59 | } 60 | 61 | // End 62 | -------------------------------------------------------------------------------- /Libraries/RotaryEncoder/RotaryEncoder.h: -------------------------------------------------------------------------------- 1 | #ifndef __RotaryEncoderIncluded 2 | #define __RotaryEncoderIncluded 3 | 4 | class RotaryEncoder 5 | { 6 | unsigned int state; 7 | int pin0, pin1; 8 | int ppc; 9 | int change; 10 | 11 | unsigned int readState(); 12 | 13 | public: 14 | RotaryEncoder(int p0, int p1, int pulsesPerClick) : 15 | state(0), pin0(p0), pin1(p1), ppc(pulsesPerClick), change(0) {} 16 | 17 | void init(); 18 | void poll(); 19 | int getChange(); 20 | }; 21 | 22 | #endif 23 | 24 | 25 | -------------------------------------------------------------------------------- /Libraries/RotaryEncoder/library.properties: -------------------------------------------------------------------------------- 1 | name=RotaryEncoder 2 | version=1.0.0 3 | author=dc42 4 | maintainer=D Crocker 5 | sentence=Driver for mechanicsl rotary encoders 6 | paragraph=If the encoder has a push button, use the PushButton library to handle it. 7 | category=Sensors 8 | architectures=* -------------------------------------------------------------------------------- /Libraries/Scheduler/Scheduler.cpp: -------------------------------------------------------------------------------- 1 | #include "arduino.h" 2 | #include "Scheduler.h" 3 | 4 | // Set USE_TIMER2 nonzero to use timer 2 as the scheduler tick source. 5 | // This alows us to generate a tick interval of just over 1ms whether using an 8MHz or 16MHz processor. 6 | // PWM using the Timer2 registers is still possible. 7 | // Set USE_TIMER2 zero to use timer1 as the tick source. 8 | // This requires patching wiring.c to allow us to intercept the tick interrupt. 9 | // The tick rate interval will be just over 1ms using a 16MHz clock, and just over 2ms using an 8MHz clock. 10 | #define USE_TIMER2 (1) 11 | 12 | #if USE_TIMER2 13 | 14 | // We set the prescaler to 64 for 10MHz clock and above, and to 32 for < 10MHz clock 15 | # if F_CPU >= 10000000 16 | # define TCCR2_PRESCALER (64) 17 | # else 18 | # define TCCR2_PRESCALER (32) 19 | # endif 20 | # define TICKS_PER_SECOND ((F_CPU) / (256 * TCCR2_PRESCALER)) 21 | 22 | #else 23 | 24 | // Wiring.c always uses a prescaler of 64 (timer 0 doesn't support 32) 25 | # define TICKS_PER_SECOND ((F_CPU) / (256 * 64)) 26 | extern void (*tick_callback)(); // this has to be patched in to wiring.c 27 | 28 | #endif 29 | 30 | // Static data 31 | Task * volatile Task::rlr = 0; 32 | Task * volatile Task::dlr = 0; 33 | 34 | Task::Task() : ticksToWakeup(-1), state(suspended), next(0) 35 | { 36 | } 37 | 38 | #ifdef USE_TIMER2 39 | SIGNAL(TIMER2_OVF_vect) 40 | { 41 | Task::tick(); 42 | } 43 | #endif 44 | 45 | void Task::init() 46 | { 47 | uint8_t oldSREG = SREG; 48 | cli(); 49 | #if USE_TIMER2 50 | TCCR2A |= 0x03; // fast PWM mode 51 | # if TCCR2_PRESCALER == 64 52 | TCCR2B = (TCCR2B & 0xC0) | 0x04; // set prescaler to 64 53 | # elif TCCR2_PRESCALER == 32 54 | TCCR2B = (TCCR2B & 0xC0) | 0x03; // set prescaler to 32 55 | # else 56 | # error "Invalid value for TCCR2_PRESCALER" 57 | # endif 58 | TIMSK2 = 0x01; // enable interrupt on overflow 59 | ASSR = 0; // use internal clock 60 | #else 61 | tick_callback = &Task::tick; 62 | #endif 63 | SREG = oldSREG; 64 | } 65 | 66 | const unsigned int Task::ticksPerSecond = TICKS_PER_SECOND; 67 | 68 | void Task::wakeup(int sleepTime) 69 | { 70 | uint8_t oldSREG = SREG; 71 | cli(); 72 | if (state == suspended) 73 | { 74 | doWakeup(sleepTime); 75 | } 76 | SREG = oldSREG; 77 | } 78 | 79 | // This function may only be called with interrupts disabled 80 | void Task::doWakeup(int sleepTime) 81 | { 82 | next = (Task*)0; 83 | if (sleepTime == 0) 84 | { 85 | // Put task at end of ready list 86 | state = ready; 87 | ticksToWakeup = 0; 88 | Task** p = const_cast(&rlr); 89 | while (*p != 0) 90 | { 91 | p = const_cast(&((*p)->next)); 92 | } 93 | *p = this; 94 | } 95 | else if (sleepTime > 0) 96 | { 97 | // Insert task into delay list 98 | state = delaying; 99 | next = (Task*)0; 100 | Task** p = const_cast(&dlr); 101 | while (*p != 0) 102 | { 103 | Task* t = *p; 104 | if (t->ticksToWakeup > sleepTime) 105 | { 106 | t->ticksToWakeup -= sleepTime; 107 | next = t; 108 | break; 109 | } 110 | sleepTime -= t->ticksToWakeup; 111 | p = const_cast(&(t->next)); 112 | } 113 | ticksToWakeup = sleepTime; 114 | *p = this; 115 | } 116 | } 117 | 118 | 119 | void Task::suspend() 120 | { 121 | switch(state) 122 | { 123 | case suspended: 124 | break; 125 | 126 | case ready: 127 | if (this != rlr) // can't suspend current task 128 | { 129 | Task** p = const_cast(&rlr); 130 | uint8_t oldSREG = SREG; 131 | cli(); 132 | while (*p != 0) 133 | { 134 | if (*p == this) 135 | { 136 | *p = next; 137 | break; 138 | } 139 | p = const_cast(&((*p)->next)); 140 | } 141 | state = suspended; 142 | SREG = oldSREG; 143 | } 144 | break; 145 | 146 | case delaying: 147 | { 148 | Task** p = const_cast(&dlr); 149 | uint8_t oldSREG = SREG; 150 | cli(); 151 | while (*p != 0) 152 | { 153 | if (*p == this) 154 | { 155 | *p = const_cast(next); 156 | if (next != 0) 157 | { 158 | next->ticksToWakeup += ticksToWakeup; 159 | } 160 | break; 161 | } 162 | p = const_cast(&((*p)->next)); 163 | } 164 | state = suspended; 165 | SREG = oldSREG; 166 | ticksToWakeup = 0; 167 | } 168 | break; 169 | } 170 | } 171 | 172 | // Suspend all tasks other than this one. 173 | void Task::suspendOthers() 174 | { 175 | uint8_t oldSREG = SREG; 176 | cli(); // disable interrupts to prevent tasks on the DLR being woken up 177 | // Suspend all tasks on the delay list 178 | Task *t = dlr; 179 | while (t != 0) 180 | { 181 | t->state = suspended; 182 | t = t->next; 183 | } 184 | dlr = 0; 185 | 186 | // Suspend all tasks on the ready list except the current one 187 | t = rlr; 188 | if (t != 0) 189 | { 190 | while ((t = t->next) != 0) 191 | { 192 | t->state = suspended; 193 | } 194 | rlr->next = 0; 195 | } 196 | SREG = oldSREG; 197 | } 198 | 199 | void Task::loop() 200 | { 201 | uint8_t oldSREG = SREG; 202 | cli(); 203 | Task* current = rlr; 204 | SREG = oldSREG; 205 | if (current != 0) 206 | { 207 | int t = current->body(); 208 | uint8_t oldSREG = SREG; 209 | cli(); 210 | current->state = suspended; 211 | rlr = current->next; 212 | if (t >= 0) 213 | { 214 | current->doWakeup(t); 215 | } 216 | SREG = oldSREG; 217 | } 218 | } 219 | 220 | // Tick ISR, must be called with interrupts already disabled 221 | void Task::tick() 222 | { 223 | Task* t = dlr; 224 | if (t != 0) 225 | { 226 | --(t->ticksToWakeup); 227 | if (t->ticksToWakeup == 0) 228 | { 229 | // Remove this task any any more with zero ticks to wakeup from the delay list 230 | t->state = ready; 231 | Task** tt = const_cast(&(t->next)); 232 | while (*tt != 0 && (*tt)->ticksToWakeup == 0) 233 | { 234 | (*tt)->state = ready; 235 | tt = const_cast(&((*tt)->next)); 236 | } 237 | dlr = *tt; 238 | *tt = 0; 239 | 240 | // Append these tasks to the ready list 241 | Task** pp = const_cast(&rlr); 242 | while (*pp != 0) 243 | { 244 | pp = const_cast(&((*pp)->next)); 245 | } 246 | *pp = t; 247 | } 248 | } 249 | } 250 | 251 | // End 252 | 253 | 254 | -------------------------------------------------------------------------------- /Libraries/Scheduler/Scheduler.h: -------------------------------------------------------------------------------- 1 | // Scheduler for Arduino 2 | 3 | class Task 4 | { 5 | enum TaskState 6 | { 7 | suspended = 0, 8 | ready = 1, 9 | delaying = 2 10 | }; 11 | 12 | public: 13 | Task(); // build a new task but don't start it 14 | void suspend(); // suspend the task (i.e. cancel any scheduled wakeup). If the task is already executing then the call is ignored. 15 | static void suspendOthers(); // suspend all other tasks except the current one 16 | 17 | // Test whether the task is suspended 18 | bool isSuspended() const { 19 | return state == suspended; 20 | } 21 | 22 | static void loop(); // this is the function we must keep calling for the scheduler to run 23 | static void init(); 24 | static void tick(); 25 | 26 | static const unsigned int ticksPerSecond; 27 | 28 | protected: 29 | void wakeup(int sleepTime); // wake up a task after the specified time. Safe to call from an ISR. 30 | 31 | // This function is the body of the task. 32 | // The integer return value is the number of ticks we want to elapse before we are woken up again. 33 | // If it is zero then the task is placed at the back of the ready queue. 34 | // A negative number means we want to be suspended until another task wakes us up. 35 | virtual int body() = 0; 36 | 37 | private: 38 | 39 | // internal function to wake up a task, must be called with interruopts disabled and task in suspended state 40 | void doWakeup(int sleepTime); 41 | 42 | int ticksToWakeup; // if this task is the first on on the delay list, then this is the number of ticks until it gets scheduled. 43 | // if it is not the first task on the delay list, need to add the tick counts from all other tasks ahead of it. 44 | volatile TaskState state; // what state the task is in 45 | Task * volatile next; // link to next task in list 46 | 47 | static Task * volatile rlr; // ready list root 48 | static Task * volatile dlr; // delay list root 49 | }; 50 | 51 | class SimpleTask : public Task 52 | { 53 | public: 54 | // Definition of the type of a SimpleTask function. 55 | // The integer return value is the number of ticks we want to elapse before we are woken up again. 56 | // A negative number means we want to be suspended until another task wakes us up. 57 | typedef int (*TaskFunc)(); 58 | 59 | SimpleTask(TaskFunc f) : Task(), func(f) {} // build a new SimpleTask but don't start it 60 | 61 | void start(int sleepTime) 62 | { 63 | wakeup(sleepTime); 64 | } 65 | 66 | protected: 67 | /*override*/ int body() 68 | { 69 | return func(); 70 | } 71 | 72 | private: 73 | TaskFunc func; 74 | }; 75 | 76 | // End 77 | 78 | -------------------------------------------------------------------------------- /Libraries/Scheduler/Scheduler.ino: -------------------------------------------------------------------------------- 1 | // Demo function for task scheduler 2 | // This demo uses two intstances of SimpleTask to blink 2 LEDs at different rates. 3 | // A better approach (avoiding use of global variables) would be to derive a LED blink class from Task instead. 4 | 5 | #include "Scheduler.h" 6 | 7 | bool ledState1 = false; 8 | bool ledState2 = false; 9 | 10 | // This task blinks a led on pin 13, 200ms on, 200ms off 11 | int myTaskFunc1() 12 | { 13 | ledState1 = !ledState1; 14 | digitalWrite(13, ledState1 ? HIGH : LOW); 15 | return 200; 16 | } 17 | 18 | // This task blinks a led on pin 12, 500ms on, 500ms off 19 | int myTaskFunc2() 20 | { 21 | ledState2 = !ledState2; 22 | digitalWrite(12, ledState2 ? HIGH : LOW); 23 | return 500; 24 | } 25 | 26 | // Declare a SimpleTask to execute each task function 27 | SimpleTask myTask1(&myTaskFunc1); 28 | SimpleTask myTask2(&myTaskFunc2); 29 | 30 | void setup() 31 | { 32 | pinMode(13, OUTPUT); 33 | pinMode(12, OUTPUT); 34 | Task::init(); // initialize the task subsystem 35 | myTask1.start(0); // start the first task immediately 36 | myTask2.start(0); // start the second task immediately 37 | } 38 | 39 | void loop() 40 | { 41 | Task::loop(); 42 | } 43 | 44 | // End 45 | 46 | -------------------------------------------------------------------------------- /MetalDetector/KiCad/MetalDetector.kicad_pcb: -------------------------------------------------------------------------------- 1 | (kicad_pcb (version 20211014) (generator pcbnew) 2 | ) -------------------------------------------------------------------------------- /MetalDetector/KiCad/MetalDetector.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 0, 4 | "active_layer_preset": "", 5 | "auto_track_width": true, 6 | "hidden_nets": [], 7 | "high_contrast_mode": 0, 8 | "net_color_mode": 1, 9 | "opacity": { 10 | "pads": 1.0, 11 | "tracks": 1.0, 12 | "vias": 1.0, 13 | "zones": 0.6 14 | }, 15 | "ratsnest_display_mode": 0, 16 | "selection_filter": { 17 | "dimensions": true, 18 | "footprints": true, 19 | "graphics": true, 20 | "keepouts": true, 21 | "lockedItems": true, 22 | "otherItems": true, 23 | "pads": true, 24 | "text": true, 25 | "tracks": true, 26 | "vias": true, 27 | "zones": true 28 | }, 29 | "visible_items": [ 30 | 0, 31 | 1, 32 | 2, 33 | 3, 34 | 4, 35 | 5, 36 | 8, 37 | 9, 38 | 10, 39 | 11, 40 | 12, 41 | 13, 42 | 14, 43 | 15, 44 | 16, 45 | 17, 46 | 18, 47 | 19, 48 | 20, 49 | 21, 50 | 22, 51 | 23, 52 | 24, 53 | 25, 54 | 26, 55 | 27, 56 | 28, 57 | 29, 58 | 30, 59 | 32, 60 | 33, 61 | 34, 62 | 35, 63 | 36 64 | ], 65 | "visible_layers": "fffffff_ffffffff", 66 | "zone_display_mode": 0 67 | }, 68 | "meta": { 69 | "filename": "MetalDetector.kicad_prl", 70 | "version": 3 71 | }, 72 | "project": { 73 | "files": [] 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /MetalDetector/KiCad/MetalDetector.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "design_settings": { 4 | "defaults": { 5 | "board_outline_line_width": 0.1, 6 | "copper_line_width": 0.2, 7 | "copper_text_size_h": 1.5, 8 | "copper_text_size_v": 1.5, 9 | "copper_text_thickness": 0.3, 10 | "other_line_width": 0.15, 11 | "silk_line_width": 0.15, 12 | "silk_text_size_h": 1.0, 13 | "silk_text_size_v": 1.0, 14 | "silk_text_thickness": 0.15 15 | }, 16 | "diff_pair_dimensions": [], 17 | "drc_exclusions": [], 18 | "rules": { 19 | "min_copper_edge_clearance": 0.0, 20 | "solder_mask_clearance": 0.0, 21 | "solder_mask_min_width": 0.0 22 | }, 23 | "track_widths": [], 24 | "via_dimensions": [] 25 | }, 26 | "layer_presets": [] 27 | }, 28 | "boards": [], 29 | "cvpcb": { 30 | "equivalence_files": [] 31 | }, 32 | "erc": { 33 | "erc_exclusions": [], 34 | "meta": { 35 | "version": 0 36 | }, 37 | "pin_map": [ 38 | [ 39 | 0, 40 | 0, 41 | 0, 42 | 0, 43 | 0, 44 | 0, 45 | 1, 46 | 0, 47 | 0, 48 | 0, 49 | 0, 50 | 2 51 | ], 52 | [ 53 | 0, 54 | 2, 55 | 0, 56 | 1, 57 | 0, 58 | 0, 59 | 1, 60 | 0, 61 | 2, 62 | 2, 63 | 2, 64 | 2 65 | ], 66 | [ 67 | 0, 68 | 0, 69 | 0, 70 | 0, 71 | 0, 72 | 0, 73 | 1, 74 | 0, 75 | 1, 76 | 0, 77 | 1, 78 | 2 79 | ], 80 | [ 81 | 0, 82 | 1, 83 | 0, 84 | 0, 85 | 0, 86 | 0, 87 | 1, 88 | 1, 89 | 2, 90 | 1, 91 | 1, 92 | 2 93 | ], 94 | [ 95 | 0, 96 | 0, 97 | 0, 98 | 0, 99 | 0, 100 | 0, 101 | 1, 102 | 0, 103 | 0, 104 | 0, 105 | 0, 106 | 2 107 | ], 108 | [ 109 | 0, 110 | 0, 111 | 0, 112 | 0, 113 | 0, 114 | 0, 115 | 0, 116 | 0, 117 | 0, 118 | 0, 119 | 0, 120 | 2 121 | ], 122 | [ 123 | 1, 124 | 1, 125 | 1, 126 | 1, 127 | 1, 128 | 0, 129 | 1, 130 | 1, 131 | 1, 132 | 1, 133 | 1, 134 | 2 135 | ], 136 | [ 137 | 0, 138 | 0, 139 | 0, 140 | 1, 141 | 0, 142 | 0, 143 | 1, 144 | 0, 145 | 0, 146 | 0, 147 | 0, 148 | 2 149 | ], 150 | [ 151 | 0, 152 | 2, 153 | 1, 154 | 2, 155 | 0, 156 | 0, 157 | 1, 158 | 0, 159 | 2, 160 | 2, 161 | 2, 162 | 2 163 | ], 164 | [ 165 | 0, 166 | 2, 167 | 0, 168 | 1, 169 | 0, 170 | 0, 171 | 1, 172 | 0, 173 | 2, 174 | 0, 175 | 0, 176 | 2 177 | ], 178 | [ 179 | 0, 180 | 2, 181 | 1, 182 | 1, 183 | 0, 184 | 0, 185 | 1, 186 | 0, 187 | 2, 188 | 0, 189 | 0, 190 | 2 191 | ], 192 | [ 193 | 2, 194 | 2, 195 | 2, 196 | 2, 197 | 2, 198 | 2, 199 | 2, 200 | 2, 201 | 2, 202 | 2, 203 | 2, 204 | 2 205 | ] 206 | ], 207 | "rule_severities": { 208 | "bus_definition_conflict": "error", 209 | "bus_entry_needed": "error", 210 | "bus_label_syntax": "error", 211 | "bus_to_bus_conflict": "error", 212 | "bus_to_net_conflict": "error", 213 | "different_unit_footprint": "error", 214 | "different_unit_net": "error", 215 | "duplicate_reference": "error", 216 | "duplicate_sheet_names": "error", 217 | "extra_units": "error", 218 | "global_label_dangling": "warning", 219 | "hier_label_mismatch": "error", 220 | "label_dangling": "error", 221 | "lib_symbol_issues": "warning", 222 | "multiple_net_names": "warning", 223 | "net_not_bus_member": "warning", 224 | "no_connect_connected": "warning", 225 | "no_connect_dangling": "warning", 226 | "pin_not_connected": "error", 227 | "pin_not_driven": "error", 228 | "pin_to_pin": "warning", 229 | "power_pin_not_driven": "ignore", 230 | "similar_labels": "warning", 231 | "unannotated": "error", 232 | "unit_value_mismatch": "error", 233 | "unresolved_variable": "error", 234 | "wire_dangling": "error" 235 | } 236 | }, 237 | "libraries": { 238 | "pinned_footprint_libs": [], 239 | "pinned_symbol_libs": [] 240 | }, 241 | "meta": { 242 | "filename": "MetalDetector.kicad_pro", 243 | "version": 1 244 | }, 245 | "net_settings": { 246 | "classes": [ 247 | { 248 | "bus_width": 12.0, 249 | "clearance": 0.2, 250 | "diff_pair_gap": 0.25, 251 | "diff_pair_via_gap": 0.25, 252 | "diff_pair_width": 0.2, 253 | "line_style": 0, 254 | "microvia_diameter": 0.3, 255 | "microvia_drill": 0.1, 256 | "name": "Default", 257 | "pcb_color": "rgba(0, 0, 0, 0.000)", 258 | "schematic_color": "rgba(0, 0, 0, 0.000)", 259 | "track_width": 0.25, 260 | "via_diameter": 0.8, 261 | "via_drill": 0.4, 262 | "wire_width": 6.0 263 | } 264 | ], 265 | "meta": { 266 | "version": 2 267 | }, 268 | "net_colors": null 269 | }, 270 | "pcbnew": { 271 | "last_paths": { 272 | "gencad": "", 273 | "idf": "", 274 | "netlist": "", 275 | "specctra_dsn": "", 276 | "step": "", 277 | "vrml": "" 278 | }, 279 | "page_layout_descr_file": "" 280 | }, 281 | "schematic": { 282 | "annotate_start_num": 0, 283 | "drawing": { 284 | "default_line_thickness": 6.0, 285 | "default_text_size": 50.0, 286 | "field_names": [], 287 | "intersheets_ref_own_page": false, 288 | "intersheets_ref_prefix": "", 289 | "intersheets_ref_short": false, 290 | "intersheets_ref_show": false, 291 | "intersheets_ref_suffix": "", 292 | "junction_size_choice": 3, 293 | "label_size_ratio": 0.375, 294 | "pin_symbol_size": 25.0, 295 | "text_offset_ratio": 0.15 296 | }, 297 | "legacy_lib_dir": "", 298 | "legacy_lib_list": [], 299 | "meta": { 300 | "version": 1 301 | }, 302 | "net_format_name": "", 303 | "ngspice": { 304 | "fix_include_paths": true, 305 | "fix_passive_vals": false, 306 | "meta": { 307 | "version": 0 308 | }, 309 | "model_mode": 0, 310 | "workbook_filename": "" 311 | }, 312 | "page_layout_descr_file": "", 313 | "plot_directory": "./", 314 | "spice_adjust_passive_values": false, 315 | "spice_external_command": "spice \"%I\"", 316 | "subpart_first_id": 65, 317 | "subpart_id_separator": 0 318 | }, 319 | "sheets": [ 320 | [ 321 | "e63e39d7-6ac0-4ffd-8aa3-1841a4541b55", 322 | "" 323 | ] 324 | ], 325 | "text_variables": {} 326 | } 327 | -------------------------------------------------------------------------------- /MetalDetector/KiCad/MetalDetectorSchematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dc42/arduino/7a929a31244dfab6974932ccda896a8f54566347/MetalDetector/KiCad/MetalDetectorSchematic.pdf -------------------------------------------------------------------------------- /MetalDetector/MetalDetector.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define DEBUG_OUTPUT (0) 6 | 7 | extern const PROGMEM LcdFont font10x10; 8 | 9 | // Induction balance metal detector 10 | 11 | // We run the CPU at 16MHz and the ADC clock at 1MHz. ADC resolution is reduced to 8 bits at this speed. 12 | 13 | // Timer 1 is used to divide the system clock by about 256 to produce a 62.5kHz square wave. 14 | // This is used to drive timer 0 and also to trigger ADC conversions. 15 | // Timer 0 is used to divide the output of timer 1 by 8, giving a 7.8125kHz signal for driving the transmit coil. 16 | // This gives us 16 ADC clock cycles for each ADC conversion (it actually takes 13.5 cycles), and we take 8 samples per cycle of the coil drive voltage. 17 | // The ADC implements four phase-sensitive detectors at 45 degree intervals. Using 4 instead of just 2 allows us to cancel the third harmonic of the 18 | // coil frequency. 19 | 20 | // Timer 2 will be used to generate a tone for the earpiece or headset. 21 | 22 | // Other division ratios for timer 1 are possible, from about 235 upwards. 23 | 24 | // Wiring: 25 | // Connect digital pin 4 (alias T0) to digital pin 9 26 | // Connect digital pin 5 through resistor to primary coil and tuning capacitor 27 | // Connect output from receive amplifier to analog pin 0. Output of receive amplifier should be biased to about half of the analog reference. 28 | // When using USB power, change analog reference to the 3.3V pin, because there is too much noise on the +5V rail to get good sensitivity. 29 | 30 | #define TIMER1_TOP (244) // can adjust this to fine-tune the frequency to get the coil tuned (see above) 31 | 32 | #define USE_3V3_AREF (1) // set to 1 if running on an Arduino with USB power, 0 for an embedded atmega328p with no 3.3V supply available 33 | 34 | // Digital pin definitions 35 | // Digital pin 0 not used, however if we are using the serial port for debugging then it's serial input 36 | const int debugTxPin = 1; // transmit pin reserved for debugging 37 | 38 | const int PowerPin = 2; // setting this pin high maintains VIN power from the battery 39 | const int BuzzerPin = 3; // earpiece, aka OCR2B for tone generation 40 | const int T0InputPin = 4; 41 | const int coilDrivePin = 5; 42 | const int T0OutputPin = 9; 43 | 44 | const int EncoderAPin = 6; 45 | const int EncoderBPin = 7; 46 | const int EncoderButtonPin = 8; 47 | const int LcdCsPin = 10; // LCD chip select (active high for 12964) 48 | const int LcdDataPin = 11; // pins 11-13 also used for ICSP 49 | const int LcdMosiPin = 12; // pin 12 is not used, so we enable its pullup to keep it high 50 | const int LcdSclkPin = 13; 51 | 52 | // Analog pin definitions 53 | const int receiverInputPin = 0; 54 | const int batteryVoltagePin = 1; 55 | // Analog pins 2-5 not used 56 | 57 | const float BatteryVoltageRange = 3.3 * (100.0 + 47.0) / 47.0; // the battery voltage that wold give a maximum ADC reading 58 | 59 | const int EncoderPulsesPerClick = 4; 60 | 61 | // LCD rows 62 | const uint16_t row0 = 0; 63 | const uint16_t row1 = 11; 64 | const uint16_t row2 = 22; 65 | const uint16_t row3 = 33; 66 | const uint16_t row4 = 44; 67 | const uint16_t row5 = 55; 68 | 69 | // Variables used only by the ISR 70 | int16_t bins[4]; // bins used to accumulate ADC readings, one for each of the 4 phases 71 | uint16_t numSamples = 0; 72 | const uint16_t numSamplesToAverage = 1024; 73 | 74 | // Variables used by the ISR and outside it 75 | volatile int16_t averages[4]; // when we've accumulated enough readings in the bins, the ISR copies them to here and starts again 76 | volatile uint16_t ticks = 0; // system tick counter for timekeeping 77 | uint16_t whenButtonPressed; 78 | uint16_t LongPressTicks = 40000; 79 | volatile bool sampleReady = false; // indicates that the averages array has been updated 80 | bool printCalibration = true; 81 | bool printSensitivity = true; 82 | bool buttonDown = false; 83 | 84 | // Variables used only outside the ISR 85 | int16_t calib[4]; // values (set during calibration) that we subtract from the averages 86 | 87 | volatile uint8_t lastctr; 88 | volatile uint16_t misses = 0; // this counts how many times the ISR has been executed too late. Should remain at zero if everything is working properly. 89 | uint32_t lastPollTime = 0; 90 | const uint16_t PollInterval = 256; // Poll the button and the encoder every 256 ticks = every 4.096ms 91 | 92 | const double halfRoot2 = sqrt(0.5); 93 | const double quarterPi = 3.1415927/4.0; 94 | const double radiansToDegrees = 180.0/3.1415927; 95 | 96 | // The ADC sample and hold occurs 2 ADC clocks (= 32 system clocks) after the timer 1 overflow flag is set. 97 | // This introduces a slight phase error, which we adjust for in the calculations. 98 | const float phaseAdjust = (float)((45.0 * 32.0)/(double)(TIMER1_TOP + 1)); 99 | 100 | int sensitivity = 5; // lower = greater sensitivity. This is multipled by 5 to get the threshold. 101 | float threshold; 102 | 103 | Lcd7920 *lcd; 104 | RotaryEncoder *encoder; 105 | PushButton *button; 106 | 107 | void setup() 108 | { 109 | pinMode(PowerPin, OUTPUT); 110 | digitalWrite(PowerPin, HIGH); // Turn on power so that it will be maintained when the button is released 111 | 112 | digitalWrite(T0OutputPin, LOW); 113 | pinMode(T0OutputPin, OUTPUT); // pulse pin from timer 1 used to feed timer 0 114 | digitalWrite(coilDrivePin, LOW); 115 | pinMode(coilDrivePin, OUTPUT); // timer 0 output, square wave to drive transmit coil 116 | pinMode(LcdMosiPin, INPUT_PULLUP); 117 | pinMode(BuzzerPin, OUTPUT); 118 | 119 | lcd = new Lcd7920(LcdSclkPin, LcdDataPin, LcdCsPin, false /*true*/); 120 | lcd->begin(); 121 | button = new PushButton(EncoderButtonPin); 122 | button->init(); 123 | encoder = new RotaryEncoder(EncoderAPin, EncoderBPin, EncoderPulsesPerClick); 124 | encoder->init(); 125 | 126 | // Read the battery voltage 127 | analogReference(EXTERNAL); 128 | const uint16_t reading = analogRead(batteryVoltagePin); 129 | float batteryVoltage = (BatteryVoltageRange/1024.0) * reading; 130 | 131 | lcd->setFont(&font10x10); 132 | lcd->setRightMargin(128); 133 | lcd->setCursor(row0, 0); 134 | lcd->clear(); 135 | lcd->print("IB Metal Detector v0.0"); 136 | lcd->setCursor(row1, 0); 137 | lcd->print("Battery "); 138 | lcd->print(batteryVoltage, 1); 139 | lcd->print("V"); 140 | lcd->flush(); 141 | delay(2000); 142 | 143 | // WARNING! Do not call delay() or millis() after here, because the following code takes over the timer that Arduino uses for its tick counter 144 | 145 | cli(); 146 | // Stop timer 0 which was set up by the Arduino core 147 | TCCR0B = 0; // stop the timer 148 | TIMSK0 = 0; // disable interrupt 149 | TIFR0 = 0x07; // clear any pending interrupt 150 | 151 | // Set up ADC to trigger and read channel 0 on timer 1 overflow 152 | #if USE_3V3_AREF 153 | ADMUX = (1 << ADLAR); // use AREF pin (connected to 3.3V) as voltage reference, read pin A0, left-adjust result 154 | #else 155 | ADMUX = (1 << REFS0) | (1 << ADLAR); // use Avcc as voltage reference, read pin A0, left-adjust result 156 | #endif 157 | ADCSRB = (1 << ADTS2) | (1 << ADTS1); // auto-trigger ADC on timer/counter 1 overflow 158 | ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | (1 << ADPS2); // enable adc, enable auto-trigger, prescaler = 16 (1MHz ADC clock) 159 | DIDR0 = 1; 160 | 161 | // Set up timer 1. 162 | // Prescaler = 1, phase correct PWM mode, TOP = ICR1A 163 | TCCR1A = (1 << COM1A1) | (1 << WGM11); 164 | TCCR1B = (1 << WGM12) | (1 << WGM13) | (1 << CS10); // CTC mode, prescaler = 1 165 | TCCR1C = 0; 166 | OCR1AH = (TIMER1_TOP/2 >> 8); 167 | OCR1AL = (TIMER1_TOP/2 & 0xFF); 168 | ICR1H = (TIMER1_TOP >> 8); 169 | ICR1L = (TIMER1_TOP & 0xFF); 170 | TCNT1H = 0; 171 | TCNT1L = 0; 172 | TIFR1 = 0x07; // clear any pending interrupt 173 | TIMSK1 = (1 << TOIE1); 174 | 175 | // Set up timer 0 176 | // Clock source = T0, fast PWM mode, TOP (OCR0A) = 7, PWM output on OC0B 177 | TCCR0A = (1 << COM0B1) | (1 << WGM01) | (1 << WGM00); 178 | TCCR0B = (1 << CS00) | (1 << CS01) | (1 << CS02) | (1 << WGM02); 179 | OCR0A = 7; 180 | OCR0B = 3; 181 | TCNT0 = 0; 182 | 183 | // Set up timer 2 for tone generation 184 | TIMSK2 = 0; 185 | TCCR2A = (1 << COM2B0) | (1 << COM2B1) | (1 << WGM21) | (1 << WGM20); // set OC2B on compare match, clear OC2B at zero, fast PWM mode 186 | TCCR2B = (1 << WGM22) | (1 << CS22) | (1 << CS21); // prescaler 256, allows frequencies from 245Hz upwards 187 | OCR2A = 62; // 1kHz tone 188 | OCR2B = 255; // greater than OCR2A to disable the tone for now. Set OCR2B = half OCR2A to generate a tone. 189 | 190 | sei(); 191 | 192 | while (!sampleReady) {} // discard the first sample 193 | misses = 0; 194 | sampleReady = false; 195 | 196 | #if DEBUG_OUTPUT 197 | Serial.begin(19200); 198 | #endif 199 | } 200 | 201 | void tone(unsigned int freq) 202 | { 203 | if (freq == 0) 204 | { 205 | OCR2B = 255; 206 | } 207 | else 208 | { 209 | const uint8_t divisor = 62500u/constrain(freq, 245u, 6000); 210 | OCR2A = divisor; 211 | OCR2B = divisor/2; 212 | } 213 | } 214 | 215 | // Timer 0 overflow interrupt. This serves 2 purposes: 216 | // 1. It clears the timer 0 overflow flag. If we don't do this, the ADC will not see any more Timer 0 overflows and we will not get any more conversions. 217 | // 2. It increments the tick counter, allowing is to do timekeeping. We get 62500 ticks/second. 218 | // We now read the ADC in the timer interrupt routine instead of having a separate conversion complete interrupt. 219 | ISR(TIMER1_OVF_vect) 220 | { 221 | uint8_t ctr = TCNT0; 222 | uint8_t val = ADCH; // only need to read most significant 8 bits 223 | if (ctr != ((lastctr + 1) & 7)) 224 | { 225 | ++misses; 226 | } 227 | lastctr = ctr; 228 | int16_t *p = &bins[ctr & 3]; 229 | if (ctr < 4) 230 | { 231 | int16_t temp = *p + (int16_t)val; 232 | *p = (temp > 15000) ? 15000 : temp; 233 | } 234 | else 235 | { 236 | int16_t temp = *p - (int16_t)val; 237 | *p = (temp < -15000) ? -15000 : temp; 238 | } 239 | if (ctr == 7) 240 | { 241 | ++numSamples; 242 | if (numSamples == numSamplesToAverage) 243 | { 244 | numSamples = 0; 245 | if (!sampleReady) // if previous sample has been consumed 246 | { 247 | memcpy((void*)averages, bins, sizeof(averages)); 248 | sampleReady = true; 249 | } 250 | bins[0] = bins[1] = bins[2] = bins[3] = 0; 251 | } 252 | } 253 | ++ticks; 254 | } 255 | 256 | void loop() 257 | { 258 | uint16_t localTicks; 259 | do 260 | { 261 | localTicks = ticks; 262 | if ((localTicks - lastPollTime) >= PollInterval) 263 | { 264 | lastPollTime = localTicks; 265 | encoder->poll(); 266 | button->poll(); 267 | } 268 | } while (!sampleReady); 269 | 270 | if (button->getNewPress()) 271 | { 272 | buttonDown = true; 273 | whenButtonPressed = localTicks; 274 | } 275 | else if (buttonDown) 276 | { 277 | if (button->getState()) 278 | { 279 | if (localTicks - whenButtonPressed >= LongPressTicks) 280 | { 281 | // Button has been held down for long enough to indicate power down 282 | digitalWrite(PowerPin, false); 283 | lcd->clear(); 284 | lcd->setCursor(20, 0); 285 | lcd->print("Release to power off"); 286 | lcd->flush(); 287 | for (;;) {} 288 | } 289 | } 290 | else 291 | { 292 | // Button pressed and released. We save the current phase detector outputs and subtract them from future results. 293 | // This lets us use the detector if the coil is slightly off-balance. 294 | // It would be better to average several samples instead of taking just one. 295 | for (int i = 0; i < 4; ++i) 296 | { 297 | calib[i] = averages[i]; 298 | } 299 | sampleReady = false; 300 | printCalibration = true; 301 | buttonDown = false; 302 | } 303 | } 304 | else 305 | { 306 | const int sensChange = encoder->getChange(); 307 | if (sensChange != 0) 308 | { 309 | sensitivity = constrain(sensitivity + sensChange, 1, 50); 310 | printSensitivity = true; 311 | } 312 | } 313 | 314 | if (printCalibration) 315 | { 316 | lcd->setCursor(row1, 0); 317 | lcd->print("Cal"); 318 | for (int i = 0; i < 4; ++i) 319 | { 320 | lcd->write(' '); 321 | lcd->print(calib[i]); 322 | } 323 | lcd->clearToMargin(); 324 | printCalibration = false; 325 | } 326 | 327 | if (printSensitivity) 328 | { 329 | lcd->setCursor(row2, 0); 330 | lcd->print("Sens "); 331 | lcd->print(sensitivity); 332 | lcd->clearToMargin(); 333 | threshold = 5 * sensitivity; 334 | printSensitivity = false; 335 | } 336 | 337 | // Adjust the results for the calibration and divide by 200 338 | const double f = 1.0/200.0; 339 | double bin0 = (averages[0] - calib[0]) * f; 340 | double bin1 = (averages[1] - calib[0]) * f; 341 | double bin2 = (averages[2] - calib[0]) * f; 342 | double bin3 = (averages[3] - calib[0]) * f; 343 | sampleReady = false; // we've finished reading the averages, so the ISR is free to overwrite them again 344 | 345 | double amp1 = sqrt((bin0 * bin0) + (bin2 * bin2)); 346 | double amp2 = sqrt((bin1 * bin1) + (bin3 * bin3)); 347 | double ampAverage = (amp1 + amp2) * 0.5; 348 | 349 | double phase1 = atan2(bin0, bin2) * radiansToDegrees + 45.0; 350 | double phase2 = atan2(bin1, bin3) * radiansToDegrees; 351 | 352 | if (phase1 > phase2) 353 | { 354 | double temp = phase1; 355 | phase1 = phase2; 356 | phase2 = temp; 357 | } 358 | 359 | // The ADC sample/hold takes place 2 clocks after the timer overflow 360 | double phaseAverage = ((phase1 + phase2)/2.0) - phaseAdjust; 361 | if (phase2 - phase1 > 180.0) 362 | { 363 | if (phaseAverage < 0.0) 364 | { 365 | phaseAverage += 180.0; 366 | } 367 | else 368 | { 369 | phaseAverage -= 180.0; 370 | } 371 | } 372 | 373 | // Display results on LCD 374 | lcd->setCursor(row3, 0); 375 | lcd->print(amp1, 1); 376 | lcd->clearToMargin(); 377 | lcd->setCursor(row3, 32); 378 | lcd->print(amp2, 1); 379 | lcd->setCursor(row3, 64); 380 | lcd->print((int)phase1); 381 | lcd->setCursor(row3, 96); 382 | lcd->print((int)phase2); 383 | 384 | lcd->setCursor(row4, 0); 385 | if (ampAverage >= threshold) 386 | { 387 | // When held in line with the centre of the coil: 388 | // - non-ferrous metals give a negative phase shift, e.g. -90deg for thick copper or aluminium, a copper olive, -30deg for thin alumimium. 389 | // Ferrous metals give zero phase shift or a small positive phase shift. 390 | // So we'll say that anything with a phase shift below -20deg is non-ferrous. 391 | if (phaseAverage < -20.0) 392 | { 393 | lcd->print("Non-ferrous"); 394 | } 395 | else 396 | { 397 | lcd->print("Ferrous"); 398 | } 399 | tone(ampAverage * 10 + 245); 400 | } 401 | else 402 | { 403 | tone(0); 404 | } 405 | lcd->clearToMargin(); 406 | lcd->setCursor(row5, 0); 407 | float temp = ampAverage; 408 | while (temp > threshold) 409 | { 410 | lcd->write('*'); 411 | temp -= (threshold/2); 412 | } 413 | lcd->clearToMargin(); 414 | lcd->flush(); 415 | 416 | #if DEBUG_OUTPUT 417 | // For diagnostic purposes, print the individual bin counts and the 2 independently-calculated gains and phases 418 | Serial.print(misses); 419 | Serial.write(' '); 420 | 421 | if (bin0 >= 0.0) Serial.write(' '); 422 | Serial.print(bin0, 2); 423 | Serial.write(' '); 424 | if (bin1 >= 0.0) Serial.write(' '); 425 | Serial.print(bin1, 2); 426 | Serial.write(' '); 427 | if (bin2 >= 0.0) Serial.write(' '); 428 | Serial.print(bin2, 2); 429 | Serial.write(' '); 430 | if (bin3 >= 0.0) Serial.write(' '); 431 | Serial.print(bin3, 2); 432 | Serial.print(" "); 433 | Serial.print(amp1, 2); 434 | Serial.write(' '); 435 | Serial.print(amp2, 2); 436 | Serial.write(' '); 437 | if (phase1 >= 0.0) Serial.write(' '); 438 | Serial.print(phase1, 2); 439 | Serial.write(' '); 440 | if (phase2 >= 0.0) Serial.write(' '); 441 | Serial.print(phase2, 2); 442 | Serial.print(" "); 443 | 444 | // Print the final amplitude and phase, which we use to decide what (if anything) we have found) 445 | if (ampAverage >= 0.0) Serial.write(' '); 446 | Serial.print(ampAverage, 1); 447 | Serial.write(' '); 448 | if (phaseAverage >= 0.0) Serial.write(' '); 449 | Serial.print((int)phaseAverage); 450 | 451 | // Decide what we have found and tell the user 452 | if (ampAverage >= threshold) 453 | { 454 | // When held in line with the centre of the coil: 455 | // - non-ferrous metals give a negative phase shift, e.g. -90deg for thick copper or aluminium, a copper olive, -30deg for thin alumimium. 456 | // Ferrous metals give zero phase shift or a small positive phase shift. 457 | // So we'll say that anything with a phase shift below -20deg is non-ferrous. 458 | if (phaseAverage < -20.0) 459 | { 460 | Serial.print(" Non-ferrous"); 461 | } 462 | else 463 | { 464 | Serial.print(" Ferrous"); 465 | } 466 | float temp = ampAverage; 467 | while (temp > threshold) 468 | { 469 | Serial.write('!'); 470 | temp -= (threshold/2); 471 | } 472 | } 473 | Serial.println(); 474 | #endif 475 | } 476 | -------------------------------------------------------------------------------- /MetalDetector/Photos/DetectorHead.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dc42/arduino/7a929a31244dfab6974932ccda896a8f54566347/MetalDetector/Photos/DetectorHead.jpg -------------------------------------------------------------------------------- /MetalDetector/Photos/Metal Detector Board Top View Without Arduino.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dc42/arduino/7a929a31244dfab6974932ccda896a8f54566347/MetalDetector/Photos/Metal Detector Board Top View Without Arduino.jpg -------------------------------------------------------------------------------- /MetalDetector/Photos/Metal Detector Board Top View.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dc42/arduino/7a929a31244dfab6974932ccda896a8f54566347/MetalDetector/Photos/Metal Detector Board Top View.jpg -------------------------------------------------------------------------------- /MetalDetector/Photos/Metal Detector Board underside.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dc42/arduino/7a929a31244dfab6974932ccda896a8f54566347/MetalDetector/Photos/Metal Detector Board underside.jpg -------------------------------------------------------------------------------- /MetalDetector/PrintableParts/MetalDetectorArmRest.3mf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dc42/arduino/7a929a31244dfab6974932ccda896a8f54566347/MetalDetector/PrintableParts/MetalDetectorArmRest.3mf -------------------------------------------------------------------------------- /MetalDetector/PrintableParts/MetalDetectorArmRest.scad: -------------------------------------------------------------------------------- 1 | // Metal detector arm rest 2 | 3 | include ; 4 | 5 | overlap=0.05; 6 | m4Diameter=4; 7 | armDiameter=60; 8 | wallThickness=3; 9 | length=50; 10 | shaftDiameter=10; 11 | handleGripLength = armDiameter/2+wallThickness+4; 12 | handleSection = 25; 13 | handleOverlapLength=wallThickness-overlap; 14 | handleFixingLength = 8; 15 | handleLength = handleGripLength + handleOverlapLength + shaftDiameter + handleFixingLength; 16 | slotWidth=3; 17 | verlap=0.05; 18 | 19 | module handle() { 20 | difference() { 21 | union() { 22 | translate([0,handleLength/2,handleSection/4]) 23 | roundedBox([handleSection, handleLength, handleSection/2], 1, false); 24 | translate([0,0,handleSection/2]) rotate([-90,0,0]) 25 | cylinder(r=handleSection/2,h=handleLength,$fn=64); 26 | } 27 | translate([0,handleFixingLength+shaftDiameter/2,-overlap]) cylinder(r=shaftDiameter/2,h=handleSection+2*overlap,$fn=64); 28 | translate([-50,handleFixingLength/2,handleSection/2 - 3]) 29 | rotate([0,90,0]) cylinder(r=m4Diameter/2,h=100,$fn=32); 30 | translate([-slotWidth/2,-overlap,-overlap]) cube([slotWidth,handleFixingLength+shaftDiameter/2,handleSection+2*overlap]); 31 | } 32 | } 33 | 34 | module armRest() { 35 | difference() { 36 | union() { 37 | cylinder(h=length,r=armDiameter/2+wallThickness,$fn=128); 38 | translate([0,-handleLength,0]) handle(); 39 | } 40 | translate([0,0,-overlap]) cylinder(h=length+2*overlap,d=armDiameter,$fn=128); 41 | translate([-armDiameter,15,-overlap]) cube([2*armDiameter,armDiameter,length+2*overlap]); 42 | } 43 | } 44 | 45 | armRest(); 46 | 47 | -------------------------------------------------------------------------------- /MetalDetector/PrintableParts/MetalDetectorBracket.3mf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dc42/arduino/7a929a31244dfab6974932ccda896a8f54566347/MetalDetector/PrintableParts/MetalDetectorBracket.3mf -------------------------------------------------------------------------------- /MetalDetector/PrintableParts/MetalDetectorBracket.scad: -------------------------------------------------------------------------------- 1 | width=20; 2 | length=40; 3 | shaftDiameter=10; 4 | threadDiameter=8; 5 | screwDiameter=4.5; 6 | height=20; 7 | separation=17; 8 | slotWidth=3; 9 | screwOffset=(length-width/2+separation+shaftDiameter/2)/2; 10 | curvatureRadius=30; 11 | overlap=0.05; 12 | 13 | difference() { 14 | union() { 15 | translate([-width/2,0,0]) 16 | cube([width,length-width/2,height]); 17 | cylinder(r=width/2,h=height,$fn=128); 18 | } 19 | translate([0,0,-overlap]) 20 | cylinder(r=threadDiameter/2,h=height+2*overlap,$fn=64); 21 | translate([0,separation,-overlap]) 22 | cylinder(r=shaftDiameter/2,h=height+2*overlap,$fn=64); 23 | translate([-slotWidth/2,separation,-overlap]) 24 | cube([slotWidth,100,height+2*overlap]); 25 | translate([-width/2-overlap,screwOffset,height/2]) 26 | rotate([0,90,0]) 27 | cylinder(r=screwDiameter/2,h=width+2*overlap,$fn=32); 28 | translate([-width/2-overlap,0,height+curvatureRadius-2.5]) 29 | rotate([0,90,0]) 30 | cylinder(r=curvatureRadius,h=width+2*overlap,$fn=256); 31 | } 32 | -------------------------------------------------------------------------------- /MetalDetector/PrintableParts/MetalDetectorEnclosure.3mf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dc42/arduino/7a929a31244dfab6974932ccda896a8f54566347/MetalDetector/PrintableParts/MetalDetectorEnclosure.3mf -------------------------------------------------------------------------------- /MetalDetector/PrintableParts/MetalDetectorEnclosure.scad: -------------------------------------------------------------------------------- 1 | include ; 2 | 3 | holeSpacingH = 88; 4 | holeSpacingV = 65; 5 | holeToEdge = 4.5; 6 | holeToBottomEdge = 20; 7 | wallThickness = 3; 8 | baseThickness = 3; 9 | innerHeight = 44; 10 | pillarHeight = 5.6; 11 | pillarDiameter = 7; 12 | pillarHoleDiameter = 2.8; 13 | screenLength = 78.5; 14 | screenWidth = 51.5; 15 | m3Diameter = 3.4; 16 | m4Diameter=4; 17 | insertHoleDiameter=4.2; 18 | insertLength=5.4; 19 | overlap = 0.05; 20 | innerLength = holeSpacingH + 2 * holeToEdge; 21 | innerWidth = holeSpacingV + holeToEdge + holeToBottomEdge; 22 | holeVoffset=(holeToBottomEdge-holeToEdge)/2; 23 | encoderYoffsetFromHole=11.7; 24 | piezoYoffsetFromHole=encoderYoffsetFromHole; 25 | encoderXoffsetFromHole=7; 26 | piezoXoffsetFromHole=17.5+encoderXoffsetFromHole; 27 | encoderHoleDiameter=8; 28 | piezoHoleDiameter=3; 29 | 30 | lugSeparation = innerWidth - 10; 31 | lugHeight=3; 32 | lugWidth=6; 33 | lugHeightOffset=innerHeight+baseThickness-lugHeight-3; 34 | lidPillarSection=7; 35 | lidPillarHeight=20; 36 | lidPillarSeparation = innerLength - lidPillarSection + 2; 37 | lidPillarYoffset=-innerWidth/2+lidPillarSection/2-1; 38 | 39 | handleGripLength = 110; 40 | handleSection = 25; 41 | handleOverlapLength=wallThickness-overlap; 42 | shaftDiameter=10; 43 | handleFixingLength = 8; 44 | handleLength = handleGripLength + handleOverlapLength + shaftDiameter + handleFixingLength; 45 | slotWidth=3; 46 | 47 | wireSlotWidth=3; 48 | wireSlotDepth=7; 49 | 50 | module pillar(x, y, z) { 51 | translate([x,y,z]) { 52 | cylinder(r=pillarDiameter/2,h=pillarHeight+overlap,$fn=20); 53 | } 54 | } 55 | 56 | module m3Hole(x,y,height) { 57 | translate([x,y,0]) 58 | cylinder(r=m3Diameter/2,h=height,$fn=16); 59 | } 60 | 61 | module handle() { 62 | difference() { 63 | union() { 64 | translate([0,handleLength/2,handleSection/4]) 65 | roundedBox([handleSection, handleLength, handleSection/2], 1, false); 66 | translate([0,0,handleSection/2]) rotate([-90,0,0]) 67 | cylinder(r=handleSection/2,h=handleLength,$fn=64); 68 | } 69 | translate([0,handleFixingLength+shaftDiameter/2,-overlap]) cylinder(r=shaftDiameter/2,h=handleSection+2*overlap,$fn=64); 70 | translate([-50,handleFixingLength/2,handleSection/2 - 3]) 71 | rotate([0,90,0]) cylinder(r=m4Diameter/2,h=100,$fn=32); 72 | translate([-slotWidth/2,-overlap,-overlap]) cube([slotWidth,handleFixingLength+shaftDiameter/2,handleSection+2*overlap]); 73 | } 74 | } 75 | 76 | module lidPillar() { 77 | translate([-lidPillarSection/2,-lidPillarSection/2,0]) 78 | difference() { 79 | cube([lidPillarSection,lidPillarSection,lidPillarHeight]); 80 | rotate([0,30,0]) translate([0,-overlap,0]) cube([lidPillarSection+2*overlap,lidPillarSection+2*overlap,lidPillarHeight]); 81 | rotate([-30,0,0]) translate([-overlap,0,0]) cube([lidPillarSection+2*overlap,lidPillarSection+2*overlap,lidPillarHeight]); 82 | } 83 | } 84 | 85 | module enclosure() { 86 | difference() { 87 | union () { 88 | difference() { 89 | translate([0,0,(innerHeight+baseThickness)/2]) 90 | roundedBox([innerLength+2*wallThickness,innerWidth+2*wallThickness,innerHeight+baseThickness], 1, false); 91 | translate([-innerLength/2,-innerWidth/2,baseThickness]) 92 | cube([innerLength,innerWidth,innerHeight+baseThickness]); 93 | translate([-screenLength/2,-screenWidth/2+holeVoffset,-overlap]) 94 | cube([screenLength, screenWidth, baseThickness+2*overlap]); 95 | // Shaft hole 96 | translate([-holeSpacingH/2+encoderXoffsetFromHole,-holeSpacingV/2+holeVoffset-encoderYoffsetFromHole,-overlap]) 97 | cylinder(r=encoderHoleDiameter/2,h=baseThickness+2*overlap,$fn=32); 98 | // Piezo hole 99 | translate([-holeSpacingH/2+piezoXoffsetFromHole,-holeSpacingV/2+holeVoffset-piezoYoffsetFromHole,-overlap]) 100 | cylinder(r=piezoHoleDiameter/2,h=baseThickness+2*overlap,$fn=16); 101 | } 102 | pillar(-holeSpacingH/2,-holeSpacingV/2+holeVoffset,baseThickness-overlap); 103 | pillar(-holeSpacingH/2,holeSpacingV/2+holeVoffset,baseThickness-overlap); 104 | pillar(holeSpacingH/2,-holeSpacingV/2+holeVoffset,baseThickness-overlap); 105 | pillar(holeSpacingH/2,holeSpacingV/2+holeVoffset,baseThickness-overlap); 106 | 107 | // Pillars for fixing the lid 108 | translate([(-lidPillarSeparation)/2,lidPillarSection/2-innerWidth/2-1,baseThickness+innerHeight-lidPillarHeight]) 109 | lidPillar(); 110 | translate([(lidPillarSeparation)/2,lidPillarSection/2-innerWidth/2-1,baseThickness+innerHeight-lidPillarHeight]) 111 | rotate([0,0,90]) lidPillar(); 112 | } 113 | translate([0,0,-overlap]) m3Hole(-holeSpacingH/2,-holeSpacingV/2+holeVoffset,pillarHeight+baseThickness+2*overlap); 114 | translate([0,0,-overlap]) m3Hole(-holeSpacingH/2,holeSpacingV/2+holeVoffset,pillarHeight+baseThickness+2*overlap); 115 | translate([0,0,-overlap]) m3Hole(holeSpacingH/2,-holeSpacingV/2+holeVoffset,pillarHeight+baseThickness+2*overlap); 116 | translate([0,0,-overlap]) m3Hole(holeSpacingH/2,holeSpacingV/2+holeVoffset,pillarHeight+baseThickness+2*overlap); 117 | // Holes for lid lugs 118 | translate([-lugSeparation/2-lugWidth/2,innerWidth/2-overlap,lugHeightOffset]) cube([lugWidth, wallThickness+2*overlap, lugHeight]); 119 | translate([lugSeparation/2-lugWidth/2,innerWidth/2-overlap,lugHeightOffset]) cube([lugWidth, wallThickness+2*overlap, lugHeight]); 120 | // Holes for lid screws 121 | translate([-lidPillarSeparation/2,lidPillarYoffset,baseThickness+innerHeight-insertLength]) cylinder(r=insertHoleDiameter/2,h=50,$fn=32); 122 | translate([lidPillarSeparation/2,lidPillarYoffset,baseThickness+innerHeight-insertLength]) cylinder(r=insertHoleDiameter/2,h=50,$fn=32); 123 | } 124 | } 125 | 126 | difference() { 127 | union() { 128 | enclosure(); 129 | translate([0,-handleLength-innerWidth/2-wallThickness+handleOverlapLength,0]) handle(); 130 | } 131 | // Slot in handle for wires 132 | translate([-wireSlotWidth/2,-handleLength-innerWidth/2+handleFixingLength+shaftDiameter+2,handleSection-wireSlotDepth]) 133 | cube([wireSlotWidth,handleLength-handleFixingLength-shaftDiameter-6,wireSlotDepth]); 134 | // Slot in enclosure for wires 135 | translate([-wireSlotWidth/2,-innerWidth/2-wallThickness-overlap,baseThickness+innerHeight-wireSlotDepth]) 136 | cube([wireSlotWidth,baseThickness+2*overlap,wireSlotDepth+overlap]); 137 | } 138 | -------------------------------------------------------------------------------- /MetalDetector/Readme.md: -------------------------------------------------------------------------------- 1 | **Induction balance metal detector** 2 | 3 | This project is an Induction Balance Metal Detector built around Arduino Nano and a 12864 display module with encoder and piezo buzzer of the sort intended for use with 3D printers. The search coil comprises two D-shaped coils fastened on opposite side of a 250mm diameter plastic plate. The coils are wrapped in grounded aluminium foil (with a gap at one point so that it doesn't behave like a shorted turn) so that capacitance between the coils and the ground doesn't cause the system to go out of balance. 4 | 5 | The number of turns of wire in each coil must give an inductance of approx. 4.1mH so that the resonant frequency of each coil in conjunction with the 100nF tuning capacitors is 7.8125kHz. I used about 100 turns. The tuning capacitors can be adjusted if necessary. Fine adjustment can be made to the frequency in the firmware. The tuning capacitors should be metallised foil types because stable, accurate values are needed. The other capacitors can be ceramic. 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | arduino 2 | ======= 3 | 4 | This repository contains reusable modules, drivers and patches for the Arduino platform. Here is a description of the 5 | projects: 6 | 7 | Scheduler 8 | ========= 9 | This is a cooperative multi-tasking scheduler for the Arduino. Conventionally, if you need to control and/or monitor 10 | several devices concurrently, then you need to do this in the Arduino loop() function. The code for all the devices is mixed 11 | up, making it difficult to understand and hard to maintain. In contrast, if you use the task scheduler, you can write 12 | the code for each device separately, then just create a task for each device to run its code. 13 | 14 | Limitations of the task scheduler: 15 | 16 | 1. The scheduler is based on a regular tick (normally at 1ms intervals), so any time intervals between things happening 17 | must either be much less that 1ms, or must be multiples of 1ms, or must be specially programmed using one of the 18 | hardware timers. 19 | 20 | 2. You must write the code for each task so that it executes in much less than 1ms before it returns, so that other 21 | tasks can run. Anything that takes longer must be broke down into smaller steps. 22 | 23 | 3. You cannot call any library functions that may take more than a millisecond to execute. Library functions that wait 24 | for things to complete need to be rewritten to use the task scheduler instead. 25 | 26 | Lcd7920 27 | ======= 28 | This is a simple library to drive 128x64 graphic LCDs based on the ST7920 chip in serial mode, thereby using only two 29 | Arduino pins (preferably the MOSI and SCLK pins to get best speed). It is smaller but less comprehensive than U8glib. 30 | 31 | PushButton 32 | ========== 33 | This is a class to read and debounce a push button. It can be used in conjunction with the task scheduler, or without 34 | if you arrange for your code to call the poll() function at regular intervals. 35 | 36 | RotaryEncoder 37 | ============= 38 | This is a class to read rotary encoders, allowing for contact bounce and variation in the detent position. It can be 39 | used with or without the task scheduler. To maintain responsiveness, the poll() function must be called at intervals 40 | of 1ms or 2ms. 41 | --------------------------------------------------------------------------------