Skip to content

Instantly share code, notes, and snippets.

@lorol
Last active April 1, 2019 20:59
Show Gist options
  • Save lorol/b41c43db7f3ac0990b5639921658f024 to your computer and use it in GitHub Desktop.
Save lorol/b41c43db7f3ac0990b5639921658f024 to your computer and use it in GitHub Desktop.
Updated font choice
#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