Last active
April 1, 2019 20:59
-
-
Save lorol/b41c43db7f3ac0990b5639921658f024 to your computer and use it in GitHub Desktop.
Updated font choice
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
/******************** | |
DDS AD9833 Generator | |
https://github.com/neu-rah/ArduinoMenu | |
Uses: | |
https://github.com/lorol/AD9833-Library-Arduino - a fork SPIx of | |
https://github.com/Billwilliams1952/AD9833-Library-Arduino | |
output: U8G2 https://github.com/olikraus/U8g2_Arduino + Serial | |
input: Serial + ClickEncoder | |
MCU STM32L15x (NUCLEO 64) | |
Custom floatField by ferchinas | |
https://github.com/neu-rah/ArduinoMenu/pull/238 | |
*/ | |
#if defined (ARDUINO_ARCH_STM32) | |
#define SPIX_STM32 | |
typedef double my_fp; //on STM32 double is 64 bit | |
#else | |
typedef float my_fp; | |
#endif | |
//OLED@SPI1 | |
#define OLED_SS PA4 //D14 (NC) HW_SPI1_SS | |
#define OLED_SCLK PA5 //D13 HW_SPI1_SCLK | |
#define OLED_MISO PA6 //D12 (NC) HW_SPI1_MISO | |
#define OLED_MOSI PA7 //D11 HW_SPI1_MOSI | |
#define OLED_CS PB4 //D5 GPIO | |
#define OLED_DC PC7 //D9 GPIO | |
#define OLED_RST PA8 //D7 GPIO | |
//DDS&POT@SPIx (2 or 3) | |
#define SSD_SS PB12 //(NC) HW_SPIx_SS | |
#define SSD_SCLK PB13 //HW_SPI2_SCLK //PC10 //HW_SPI3_SCLK | |
#define SSD_MISO PB14 //(NC) HW_SPIx_MISO | |
#define SSD_MOSI PB15 //HW_SPI2_MOSI | |
#define FNC_PIN PC4 //GPIO | |
#define CS_PIN PC2 //Potentiometer | |
#define ENCSTEP 2 | |
#define encA PB8 //D15 | |
#define encB PB9 //D14 | |
#define encBtn PB5 //D4 | |
#define SENS_PIN PC3 //Test | |
#define EECH 0x20 | |
#define EECNT EECH + 1 | |
#define EEBEGIN EECNT + 1 | |
#define EEMARK 0x5A | |
#define MEMMAX 7 | |
#define NO_OUT 0xFFFF | |
#include <SPI.h> | |
#include <EEPROM.h> | |
#include <menu.h> | |
#include <menuIO/u8g2Out.h> | |
#include <menuIO/clickEncoderIn.h> | |
#include <menuIO/chainStream.h> | |
#include <menuIO/serialOut.h> | |
#include <menuIO/serialIn.h> | |
#include <AD9833.h> | |
#include <MCP41_Simple.h> | |
#include <Button.h> | |
using namespace Menu; | |
#include "floatfield.h" | |
#define fontName u8g2_font_pcsenior_8r | |
#define fontX 8 | |
#define fontY 10 | |
//#define fontName u8g2_font_5x8_t_cyrillic | |
//#define fontX 5 | |
//#define fontY 9 | |
#define offsetX 0 | |
#define offsetY 1 | |
#define U8_Width 128 | |
#define U8_Height 64 | |
//prototypes | |
void limitFreq(void); | |
void doOut(uint16_t outx); | |
result setOut(eventMask event, navNode& nav, prompt &item); | |
result setLevel(void); | |
result mapFreq(void); | |
result writeEE(void); | |
result readEE(void); | |
result confMem(void); | |
result loadDef(void); | |
result setFreq(void); | |
result oledContrast(void); | |
result alert(menuOut& o,idleEvent e); | |
result doAlert(eventMask e, prompt &item); | |
//prototypes | |
//U8G2_SSD1322_NHD_128X64_F_4W_HW_SPI u8g2(U8G2_R0, OLED_CS, OLED_DC, OLED_RST); // clock=PA5, data=PA7 | |
U8G2_SSD1305_128X64_ADAFRUIT_F_4W_HW_SPI u8g2(U8G2_R0, OLED_CS, OLED_DC, OLED_RST); // clock=PA5, data=PA7 | |
// {{disabled normal,disabled selected},{enabled normal,enabled selected, enabled editing}} | |
const colorDef<uint8_t> colors[] MEMMODE = { | |
{{0, 0}, {0, 1, 1}}, //bgColor | |
{{1, 1}, {1, 0, 0}}, //fgColor | |
{{1, 1}, {1, 0, 0}}, //valColor | |
{{1, 1}, {1, 0, 0}}, //unitColor | |
{{0, 1}, {0, 0, 1}}, //cursorColor {{0,1},{0,0,1}},//cursorColor | |
{{1, 1}, {1, 1, 0}}, //titleColor | |
}; | |
#if defined SPIX_STM32 | |
SPIClass SPIX(SSD_MOSI, SSD_MISO, SSD_SCLK); | |
AD9833 gen(FNC_PIN, 25000000UL, SPIX); | |
MCP41_Simple MyPot(SPIX); | |
#else | |
AD9833 gen(FNC_PIN); // Use single standard SPI for all | |
MCP41_Simple MyPot; | |
#endif | |
Button sens = Button(SENS_PIN, BUTTON_PULLUP_INTERNAL); | |
struct EE_bl { | |
byte memid; | |
byte potamp; | |
uint16_t out; | |
uint16_t kilo; | |
float herz; | |
}; | |
EE_bl ee = {0, SINE_WAVE, 12345, 678.9}; | |
byte mem = 0; | |
byte memch = 0; | |
byte o_cnt = 0; // oled contrast | |
char* constMEM dx1 MEMMODE="01"; | |
char* constMEM dx9 MEMMODE="0123456789"; | |
char* constMEM dNr[] MEMMODE={dx1,dx9,dx9,dx9,dx9,dx9,dx9,dx9,".",dx9," ","H","z"}; ////char buf1[]= "12345678.9 Hz" | |
char str_temp[11]; | |
char bufr[14]; | |
my_fp freq = 0; | |
ClickEncoder clickEncoder(encA, encB, encBtn, ENCSTEP); | |
ClickEncoderStream encStream(clickEncoder, 1); | |
SELECT(ee.out, outMenu, "Wave", doNothing, noEvent, wrapStyle | |
, VALUE("Sine", SINE_WAVE, setOut, focusEvent) | |
, VALUE("Triangle", TRIANGLE_WAVE, setOut, focusEvent) | |
, VALUE("Square", SQUARE_WAVE, setOut, focusEvent) | |
, VALUE("Half Sqr", HALF_SQUARE_WAVE, setOut, focusEvent) | |
, VALUE("Off", NO_OUT, setOut, focusEvent) | |
); | |
SELECT(mem, memMenu,"Cfg", confMem, exitEvent, wrapStyle | |
, VALUE("Ok",0, doNothing, noEvent) | |
, VALUE("Load Mem",1, readEE, enterEvent) | |
, VALUE("Save Mem",2, writeEE, enterEvent) | |
, VALUE("Defaults",3, loadDef, enterEvent) | |
); | |
MENU(mainMenu, " DDS GENERATOR ", doNothing, noEvent, noStyle | |
, FIELD(memch,"Select Mem","",0,MEMMAX,1,0,doNothing, noEvent,wrapStyle) | |
, SUBMENU(memMenu) | |
, SUBMENU(outMenu) | |
, EDIT("F",bufr,dNr,mapFreq,exitEvent,noStyle) | |
, FIELD(ee.kilo,"Tune"," KHz",0,12500,100,1,setFreq,enterEvent,wrapStyle) | |
, dFIELD(ee.herz,"Fine"," Hz",0,999.9,10,0.1,setFreq,enterEvent,wrapStyle) | |
, FIELD(o_cnt,"Contrast","",0,7,1,0,oledContrast, enterEvent,wrapStyle) | |
, FIELD(ee.potamp,"Level","",0,255,10,1,setLevel,enterEvent,wrapStyle) | |
, OP("Debug",doAlert,enterEvent) | |
); | |
#define MAX_DEPTH 2 | |
void doOut(uint16_t outx){ | |
if (outx == NO_OUT) gen.EnableOutput(false); | |
else{ | |
gen.SetWaveform(REG0,(WaveformType)outx); | |
gen.SetOutputSource(REG0); | |
gen.EnableOutput(true); | |
} | |
} | |
result setOut(eventMask event, navNode& nav, prompt &item) { | |
/* | |
Serial.print("selected index of current menu:"); | |
Serial.println(nav.sel); | |
Serial.print("selected value(int):"); | |
Serial.println(((menuValue<int>*)&item)->target()); | |
*/ | |
doOut(((menuValue<uint16_t>*)&item)->target()); | |
return proceed; | |
} | |
result setLevel(){ | |
MyPot.setWiper(ee.potamp); | |
return proceed; | |
} | |
void limitFreq(){ | |
if (freq > 12499999.99){ | |
freq = 12499999.99; | |
ee.kilo = 12500; | |
ee.herz = 0.0; | |
dtostrf(freq,10,1, str_temp); | |
sprintf(bufr,"%s Hz", str_temp); | |
} | |
} | |
result setFreq(){ | |
char i = 0; | |
freq =(ee.kilo*1000.00) + ee.herz; | |
limitFreq(); | |
dtostrf(freq,10,1, str_temp); | |
sprintf(bufr,"%s Hz", str_temp); | |
for (i=0;i<8;i++){ | |
if (bufr[i] == ' ') bufr[i] = '0'; | |
} | |
gen.SetFrequency(REG0,(my_fp)freq); | |
return proceed; | |
} | |
result mapFreq() { | |
freq = atof(bufr); | |
ee.kilo = freq / 1000; | |
ee.herz = freq - 1000.0*ee.kilo; | |
limitFreq(); | |
gen.SetFrequency(REG0,(my_fp)freq); | |
return proceed; | |
} | |
result writeEE() { | |
ee.memid = EEMARK; | |
EEPROM.put(EECH, memch); | |
EEPROM.put(EECNT, o_cnt); | |
EEPROM.put(EEBEGIN + memch*sizeof(ee), ee); | |
return proceed; | |
} | |
result readEE() { | |
byte ChkEE; | |
if (memch > MEMMAX) memch = 0; | |
EEPROM.get(EEBEGIN + memch*sizeof(ee), ChkEE); | |
if (ChkEE == EEMARK){ | |
EEPROM.get(EEBEGIN + memch*sizeof(ee), ee); | |
setFreq(); | |
setOut; | |
setLevel(); | |
} | |
return proceed; | |
} | |
result confMem(){ | |
mem = 0; | |
return proceed; | |
} | |
result loadDef(){ | |
memch = 0; | |
ee.kilo = 1; | |
ee.herz = 0.0; | |
ee.out = SINE_WAVE; | |
setFreq(); | |
doOut(ee.out); | |
setLevel(); | |
return proceed; | |
} | |
// Timer STM32 | |
#define TIMER_ENC TIM5 | |
volatile uint16_t count = 0; | |
static stimer_t TimHandle; | |
static void checkenc(stimer_t *htim) { | |
UNUSED(htim); | |
clickEncoder.service(); | |
// if (delCnt > 0) delCnt--; | |
} | |
serialIn serial(Serial); | |
//------------------------------------------------------- | |
MENU_INPUTS(in, &encStream | |
, &serial | |
);///, &encButton); | |
MENU_OUTPUTS(out, MAX_DEPTH | |
, U8G2_OUT(u8g2, colors, fontX, fontY, offsetX, offsetY, {0, 0, U8_Width / fontX, U8_Height / fontY}) | |
, SERIAL_OUT(Serial) | |
); | |
NAVROOT(nav, mainMenu, MAX_DEPTH, in, out); | |
//-------------------------------------------------------- | |
result oledContrast(){ | |
u8x8_cad_StartTransfer(u8g2.getU8x8()); | |
// u8x8_cad_SendCmd(u8g2.getU8x8(), 0x0c8); /* my SSD1305 c0: scan dir normal, c8: reverse */ | |
// u8x8_cad_SendCmd(u8g2.getU8x8(), 0x0a0); /* my SSD1305 flip segment remap a0/a1*/ | |
u8x8_cad_SendCmd(u8g2.getU8x8(), 0x0db); | |
u8x8_cad_SendArg(u8g2.getU8x8(), o_cnt << 4); //0-7 contrast 1 | |
u8x8_cad_SendCmd(u8g2.getU8x8(), 0x0d9); | |
u8x8_cad_SendArg(u8g2.getU8x8(), (7 << 4) | 15 ); //4-1, 1-15 contrast ~2,3 | |
u8x8_cad_EndTransfer(u8g2.getU8x8()); | |
return proceed; | |
} | |
result alert(menuOut& o,idleEvent e) { | |
if (e==idling) { | |
o.setCursor(0,0); | |
o.print("O:"); | |
o.print(ee.out, HEX); | |
o.setCursor(0,1); | |
o.print("F:"); | |
o.print(freq,2); | |
o.setCursor(0,2); | |
o.print("X:"); | |
o.print(gen.GetActualProgrammedFrequency(REG0),2); | |
o.setCursor(0,3); | |
o.print("R:"); | |
o.print(gen.GetResolution(),5); | |
o.setCursor(0,4); | |
o.print("W:"); | |
o.print(gen.GetFrequencyWord(REG0), HEX); | |
} | |
return proceed; | |
} | |
result doAlert(eventMask e, prompt &item) { | |
nav.idleOn(alert); | |
return proceed; | |
} | |
void setup() { | |
Serial.begin(115200); | |
while (!Serial); | |
Serial.println("AD9833 DDS Signal Generator"); | |
Serial.flush(); | |
//pinMode(LED_BUILTIN, OUTPUT); //if needed for debug (LED on pin 13) | |
//digitalWrite(LED_BUILTIN, LOW); | |
u8g2.begin(); | |
//u8g2.enableUTF8Print(); //extended ascii (UTF) support абв | |
u8g2.setContrast(255); | |
u8g2.setFont(fontName); | |
gen.Begin(); // The loaded defaults are 1000 Hz SINE_WAVE using REG0 | |
gen.EnableOutput(false); // Turn OFF the output | |
MyPot.begin(CS_PIN); | |
EEPROM.get(EECH, memch); | |
EEPROM.get(EECNT, o_cnt); | |
o_cnt &= 0x07; | |
oledContrast(); | |
readEE(); | |
setFreq(); // in case not yet memorized | |
doOut(ee.out); | |
setLevel(); | |
//mainMenu[8].enabled = disabledStatus; // disable debug | |
/* Set TIMx instance. */ | |
TimHandle.timer = TIMER_ENC; | |
/* Timer set to 1ms */ | |
TimerHandleInit(&TimHandle, 1000 - 1, ((uint32_t)(getTimerClkFreq(TIMER_ENC) / (1000000)) - 1)); | |
attachIntHandle(&TimHandle, checkenc); | |
clickEncoder.setAccelerationEnabled(true); | |
delay(100); | |
Serial.println("Setup done."); | |
Serial.flush(); | |
} | |
void loop() { | |
//if(sens.isPressed()) digitalWrite(LED_BUILTIN, LOW); | |
// else digitalWrite(LED_BUILTIN, HIGH); | |
nav.doInput(); | |
if (nav.changed(0)) {//only when a change | |
u8g2.firstPage(); | |
do nav.doOutput(); while (u8g2.nextPage()); | |
} | |
//delay(10); //10ms for other things | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment