app: working clock generator firmware
This commit is contained in:
parent
d40972200d
commit
88349d3918
37
README.md
37
README.md
|
@ -1,35 +1,28 @@
|
||||||
This firmware template is designed for development boards based around [STM32 F4 series micro-controller](https://www.st.com/en/microcontrollers-microprocessors/stm32f4-series.html).
|
clock generator (up to 125 MHz) using AD9850.
|
||||||
|
|
||||||
project
|
capabilities:
|
||||||
=======
|
|
||||||
|
|
||||||
summary
|
- up to 125 MHz@5V, 100MHz@3.3V
|
||||||
-------
|
- display shows desired frequency in mHz
|
||||||
|
- actual output uses AD9850's 0.0291 Hz resolution
|
||||||
|
- square and sine wave output (and it's inverse)
|
||||||
|
- 3.3V and 5V output
|
||||||
|
|
||||||
*describe project purpose*
|
hardware
|
||||||
|
========
|
||||||
|
|
||||||
technology
|
the clock generator uses following parts:
|
||||||
----------
|
|
||||||
|
|
||||||
*described electronic details*
|
|
||||||
|
|
||||||
board
|
|
||||||
=====
|
|
||||||
|
|
||||||
The underlying template also supports following board:
|
|
||||||
|
|
||||||
- [WeAct MiniF4](https://github.com/WeActTC/MiniF4-STM32F4x1), based on a STM32F401CCU6
|
- [WeAct MiniF4](https://github.com/WeActTC/MiniF4-STM32F4x1), based on a STM32F401CCU6
|
||||||
|
- Analog Devices AD9850, to generator the clock
|
||||||
**Which board is used is defined in the Makefile**.
|
- HD44780 LCD display (1x16), to display the set frequency
|
||||||
This is required to map the user LED and button provided on the board
|
- rotary encoder, to set the frequency
|
||||||
|
- switch, to set the output voltage
|
||||||
|
- coin cell, to save set frequency
|
||||||
|
|
||||||
connections
|
connections
|
||||||
===========
|
===========
|
||||||
|
|
||||||
Connect the peripherals the following way (STM32F4xx signal; STM32F4xx pin; peripheral pin; peripheral signal; comment):
|
|
||||||
|
|
||||||
- *list board to peripheral pin connections*
|
|
||||||
|
|
||||||
All pins are configured using `define`s in the corresponding source code.
|
All pins are configured using `define`s in the corresponding source code.
|
||||||
|
|
||||||
code
|
code
|
||||||
|
|
294
application.c
294
application.c
|
@ -1,8 +1,8 @@
|
||||||
/** STM32F4 application example
|
/** clock generator, using AD9850
|
||||||
* @file
|
* @file
|
||||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
* @date 2016-2021
|
* @date 2016-2022
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* standard libraries */
|
/* standard libraries */
|
||||||
|
@ -11,6 +11,7 @@
|
||||||
#include <string.h> // string utilities
|
#include <string.h> // string utilities
|
||||||
#include <time.h> // date/time utilities
|
#include <time.h> // date/time utilities
|
||||||
#include <ctype.h> // utilities to check chars
|
#include <ctype.h> // utilities to check chars
|
||||||
|
#include <math.h> // float utilities
|
||||||
|
|
||||||
/* STM32 (including CM3) libraries */
|
/* STM32 (including CM3) libraries */
|
||||||
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
#include <libopencm3/stm32/dbgmcu.h> // debug utilities
|
#include <libopencm3/stm32/dbgmcu.h> // debug utilities
|
||||||
#include <libopencm3/stm32/desig.h> // design utilities
|
#include <libopencm3/stm32/desig.h> // design utilities
|
||||||
#include <libopencm3/stm32/flash.h> // flash utilities
|
#include <libopencm3/stm32/flash.h> // flash utilities
|
||||||
|
#include <libopencm3/stm32/exti.h> // external interrupt defines
|
||||||
|
|
||||||
/* own libraries */
|
/* own libraries */
|
||||||
#include "global.h" // board definitions
|
#include "global.h" // board definitions
|
||||||
|
@ -32,6 +34,7 @@
|
||||||
#include "usb_cdcacm.h" // USB CDC ACM utilities
|
#include "usb_cdcacm.h" // USB CDC ACM utilities
|
||||||
#include "terminal.h" // handle the terminal interface
|
#include "terminal.h" // handle the terminal interface
|
||||||
#include "menu.h" // menu utilities
|
#include "menu.h" // menu utilities
|
||||||
|
#include "lcd_hd44780.h" // LCD display
|
||||||
|
|
||||||
/** watchdog period in ms */
|
/** watchdog period in ms */
|
||||||
#define WATCHDOG_PERIOD 10000
|
#define WATCHDOG_PERIOD 10000
|
||||||
|
@ -49,6 +52,24 @@ static volatile bool second_flag = false; /**< flag set when a second passed */
|
||||||
/** number of seconds since boot */
|
/** number of seconds since boot */
|
||||||
static uint32_t boot_time = 0;
|
static uint32_t boot_time = 0;
|
||||||
|
|
||||||
|
/** connection to AD9850 */
|
||||||
|
#define AD9850_DATA PA7
|
||||||
|
#define AD9850_FQUD PA6
|
||||||
|
#define AD9850_WCLK PA5
|
||||||
|
//#define AD9850_D0 PA0
|
||||||
|
//#define AD9850_D1 PA1
|
||||||
|
//#define AD9850_D2 PA2
|
||||||
|
|
||||||
|
/** AD9850 frequency to set (in mHz) */
|
||||||
|
static uint64_t ad9850_freq = 0;
|
||||||
|
/** maximum frequency (in mHz) */
|
||||||
|
#define AD9850_MAX_FREQ 125000000000ULL
|
||||||
|
|
||||||
|
/** connections to rotary encoder (common is ground) */
|
||||||
|
#define ROTARY_A PA1
|
||||||
|
#define ROTARY_B PA2
|
||||||
|
static volatile int8_t rotary_flag = 0; /** flag set when rotary encoder is turned (1 = CW, -1 = CCW) */
|
||||||
|
|
||||||
size_t putc(char c)
|
size_t putc(char c)
|
||||||
{
|
{
|
||||||
size_t length = 0; // number of characters printed
|
size_t length = 0; // number of characters printed
|
||||||
|
@ -267,6 +288,75 @@ static void command_bootloader(void* argument)
|
||||||
dfu_bootloader(); // start DFU bootloader
|
dfu_bootloader(); // start DFU bootloader
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** set AD9850 output frequency
|
||||||
|
* @param[in] frequency frequency to set (in Hz)
|
||||||
|
* @return actual frequency set (in Hz)
|
||||||
|
* @note a frequency of 0 disables the output
|
||||||
|
*/
|
||||||
|
static double ad9850_set_freq(double frequency)
|
||||||
|
{
|
||||||
|
if (frequency > AD9850_MAX_FREQ / 1000) {
|
||||||
|
frequency = AD9850_MAX_FREQ / 1000;
|
||||||
|
} else if (frequency < 0) {
|
||||||
|
frequency = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// start with default state
|
||||||
|
gpio_clear(GPIO_PORT(AD9850_WCLK), GPIO_PIN(AD9850_WCLK));
|
||||||
|
gpio_clear(GPIO_PORT(AD9850_FQUD), GPIO_PIN(AD9850_FQUD));
|
||||||
|
|
||||||
|
// enable serial mode (W0 must we xxxxx011, D0=1, D1=1, D2=0)
|
||||||
|
gpio_set(GPIO_PORT(AD9850_WCLK), GPIO_PIN(AD9850_WCLK));
|
||||||
|
sleep_us(1); // tWH = 3.5 ns
|
||||||
|
gpio_clear(GPIO_PORT(AD9850_WCLK), GPIO_PIN(AD9850_WCLK));
|
||||||
|
gpio_set(GPIO_PORT(AD9850_FQUD), GPIO_PIN(AD9850_FQUD));
|
||||||
|
sleep_us(1); // tFH = 7 ns
|
||||||
|
gpio_clear(GPIO_PORT(AD9850_FQUD), GPIO_PIN(AD9850_FQUD));
|
||||||
|
|
||||||
|
// shift out data
|
||||||
|
const uint32_t freq = round(frequency * 0xffffffff / 125E6); // output 100 kHz
|
||||||
|
const uint8_t control = 0; // must be 0 for serial data
|
||||||
|
uint8_t power_down = 0; // power up
|
||||||
|
if (0 == frequency) {
|
||||||
|
power_down = 1;
|
||||||
|
}
|
||||||
|
const uint8_t phase = 0;
|
||||||
|
uint64_t shift_out = ((uint64_t)freq << 0) | ((uint64_t)control << 32) | ((uint64_t)power_down << 34) | ((uint64_t)phase << 35); // data to be shifted out
|
||||||
|
for (uint8_t b = 0; b < 40; b++) { // shift out data, LSb first
|
||||||
|
if (shift_out & 0x01) {
|
||||||
|
gpio_set(GPIO_PORT(AD9850_DATA), GPIO_PIN(AD9850_DATA));
|
||||||
|
} else {
|
||||||
|
gpio_clear(GPIO_PORT(AD9850_DATA), GPIO_PIN(AD9850_DATA));
|
||||||
|
}
|
||||||
|
sleep_us(1); // tDS = 3.5 ns
|
||||||
|
gpio_set(GPIO_PORT(AD9850_WCLK), GPIO_PIN(AD9850_WCLK));
|
||||||
|
sleep_us(1); // tWH = 3.5 ns
|
||||||
|
gpio_clear(GPIO_PORT(AD9850_WCLK), GPIO_PIN(AD9850_WCLK));
|
||||||
|
sleep_us(1); // tWL = 3.5 ns
|
||||||
|
// tDH = 3.5ns
|
||||||
|
shift_out >>= 1; // prepare next bit
|
||||||
|
}
|
||||||
|
|
||||||
|
// latch data
|
||||||
|
// tFD = 7.0 ns
|
||||||
|
gpio_set(GPIO_PORT(AD9850_FQUD), GPIO_PIN(AD9850_FQUD));
|
||||||
|
sleep_us(1); // tFH = 7.0 ns
|
||||||
|
gpio_clear(GPIO_PORT(AD9850_FQUD), GPIO_PIN(AD9850_FQUD));
|
||||||
|
sleep_us(1); // tFL = 7.0 ns
|
||||||
|
|
||||||
|
return freq * (125E6 / 0xffffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** set AD9850 output frequency */
|
||||||
|
static void command_freq(void* argument)
|
||||||
|
{
|
||||||
|
if (argument) {
|
||||||
|
ad9850_freq = *(double*)argument * 1000.0; // get user provided frequency
|
||||||
|
}
|
||||||
|
const double freq = ad9850_set_freq(ad9850_freq / 1000.0); // set frequency and get the one set
|
||||||
|
printf("frequency set to %0.3f Hz\n", freq);
|
||||||
|
}
|
||||||
|
|
||||||
/** list of all supported commands */
|
/** list of all supported commands */
|
||||||
static const struct menu_command_t menu_commands[] = {
|
static const struct menu_command_t menu_commands[] = {
|
||||||
{
|
{
|
||||||
|
@ -325,6 +415,14 @@ static const struct menu_command_t menu_commands[] = {
|
||||||
.argument_description = NULL,
|
.argument_description = NULL,
|
||||||
.command_handler = &command_bootloader,
|
.command_handler = &command_bootloader,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.shortcut = 'f',
|
||||||
|
.name = "frequency",
|
||||||
|
.command_description = "set output frequency",
|
||||||
|
.argument = MENU_ARGUMENT_FLOAT,
|
||||||
|
.argument_description = "[Hz]",
|
||||||
|
.command_handler = &command_freq,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static void command_help(void* argument)
|
static void command_help(void* argument)
|
||||||
|
@ -356,6 +454,96 @@ static void process_command(char* str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** create 16 char representation of number
|
||||||
|
* @number number to represent
|
||||||
|
* @return 16 char representation
|
||||||
|
*/
|
||||||
|
static char* freq2s(uint64_t freq)
|
||||||
|
{
|
||||||
|
static char line[16 + 1];
|
||||||
|
for (uint8_t i = 0; i < LENGTH(line) - 1; i++) {
|
||||||
|
line[i] = ' '; // clear line
|
||||||
|
}
|
||||||
|
line[LENGTH(line) - 1] = '\0'; // terminate string
|
||||||
|
bool zero_padding = false;
|
||||||
|
uint64_t divider = 100000000000UL;
|
||||||
|
uint8_t pos = 1; // position in the line
|
||||||
|
for (uint8_t d = 0; d < 12; d++) {
|
||||||
|
if (3 == d || 6 == d) {
|
||||||
|
if (zero_padding) {
|
||||||
|
line[pos] = ','; // add separator
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
} else if (9 == d) {
|
||||||
|
if (zero_padding) {
|
||||||
|
line[pos] = '.'; // add separator
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
if (8 == d) {
|
||||||
|
zero_padding = true; // enforce hertz unit display
|
||||||
|
}
|
||||||
|
const uint8_t digit = (freq / divider) % 10;
|
||||||
|
if (digit > 0) {
|
||||||
|
line[pos] = '0' + digit; // set digit
|
||||||
|
zero_padding = true; // remember to pad with zeros now
|
||||||
|
} else if (zero_padding) {
|
||||||
|
line[pos] = '0';
|
||||||
|
} else {
|
||||||
|
line[pos] = ' ';
|
||||||
|
}
|
||||||
|
divider /= 10; // go to next digit
|
||||||
|
pos++; // go to next position
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_display(uint64_t freq, uint8_t position, bool selected)
|
||||||
|
{
|
||||||
|
const uint8_t position2cursor_lut[] = {0x47, 0x46, 0x45, 0x43, 0x42, 0x41, 0x07, 0x06, 0x05, 0x03, 0x02, 0x01};
|
||||||
|
if (position >= LENGTH(position2cursor_lut)) {
|
||||||
|
position = LENGTH(position2cursor_lut) - 1;
|
||||||
|
}
|
||||||
|
const char* line = freq2s(freq); // get frequency representation
|
||||||
|
lcd_hd44780_write_line(0, &line[0], 8); // display set frequency
|
||||||
|
lcd_hd44780_write_line(1, &line[8], 8); // display set frequency
|
||||||
|
lcd_hd44780_set_ddram_address(position2cursor_lut[position]); // set cursor position
|
||||||
|
lcd_hd44780_display_control(true, true, selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** load settings from SRAM */
|
||||||
|
static void load_settings(uint8_t* position, uint64_t* frequency)
|
||||||
|
{
|
||||||
|
if (position) {
|
||||||
|
*position = RTC_BKPXR(0);
|
||||||
|
if (*position >= 12) {
|
||||||
|
*position = 11;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frequency) {
|
||||||
|
*frequency = (RTC_BKPXR(1) << 0) + ((uint64_t)RTC_BKPXR(2) << 32);
|
||||||
|
if (*frequency > AD9850_MAX_FREQ) {
|
||||||
|
*frequency = AD9850_MAX_FREQ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** save settings to SRAM */
|
||||||
|
static void save_settings(uint8_t position, uint64_t frequency)
|
||||||
|
{
|
||||||
|
if (position >= 12) {
|
||||||
|
position = 11;
|
||||||
|
}
|
||||||
|
RTC_BKPXR(0) = position;
|
||||||
|
|
||||||
|
if (frequency > AD9850_MAX_FREQ) {
|
||||||
|
frequency = AD9850_MAX_FREQ;
|
||||||
|
}
|
||||||
|
RTC_BKPXR(1) = frequency >> 0;
|
||||||
|
RTC_BKPXR(2) = frequency >> 32;
|
||||||
|
}
|
||||||
|
|
||||||
/** program entry point
|
/** program entry point
|
||||||
* this is the firmware function started by the micro-controller
|
* this is the firmware function started by the micro-controller
|
||||||
*/
|
*/
|
||||||
|
@ -379,7 +567,7 @@ void main(void)
|
||||||
board_setup(); // setup board
|
board_setup(); // setup board
|
||||||
uart_setup(); // setup USART (for printing)
|
uart_setup(); // setup USART (for printing)
|
||||||
usb_cdcacm_setup(); // setup USB CDC ACM (for printing)
|
usb_cdcacm_setup(); // setup USB CDC ACM (for printing)
|
||||||
puts("\nwelcome to the CuVoodoo STM32F4 example firmware\n"); // print welcome message
|
puts("\nwelcome to the CuVoodoo clock generator\n"); // print welcome message
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// show reset cause
|
// show reset cause
|
||||||
|
@ -448,6 +636,43 @@ void main(void)
|
||||||
// important: do not re-enable backup_domain_write_protect, since this will prevent clearing flags (but RTC registers do not need to be unlocked)
|
// important: do not re-enable backup_domain_write_protect, since this will prevent clearing flags (but RTC registers do not need to be unlocked)
|
||||||
puts_debug("OK\n");
|
puts_debug("OK\n");
|
||||||
|
|
||||||
|
puts_debug("setup rotary encoder: ");
|
||||||
|
rcc_periph_clock_enable(GPIO_RCC(ROTARY_B)); // enable clock for button
|
||||||
|
gpio_mode_setup(GPIO_PORT(ROTARY_B), GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN(ROTARY_B)); // set GPIO to input and pull up
|
||||||
|
rcc_periph_clock_enable(GPIO_RCC(ROTARY_A)); // enable clock for button
|
||||||
|
gpio_mode_setup(GPIO_PORT(ROTARY_A), GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN(ROTARY_A)); // set GPIO to input and pull up
|
||||||
|
exti_select_source(GPIO_EXTI(ROTARY_A), GPIO_PORT(ROTARY_A)); // mask external interrupt of this pin only for this port
|
||||||
|
exti_set_trigger(GPIO_EXTI(ROTARY_A), EXTI_TRIGGER_FALLING); // trigger when button is pressed
|
||||||
|
exti_enable_request(GPIO_EXTI(ROTARY_A)); // enable external interrupt
|
||||||
|
nvic_enable_irq(GPIO_NVIC_EXTI_IRQ(ROTARY_A)); // enable interrupt
|
||||||
|
uint8_t digit_position = 0; // which digit is selected
|
||||||
|
load_settings(&digit_position, NULL); // load saved position
|
||||||
|
bool digit_selected = false; // if a digit is selected
|
||||||
|
puts_debug("OK\n");
|
||||||
|
|
||||||
|
puts_debug("setup AD9850: ");
|
||||||
|
rcc_periph_clock_enable(GPIO_RCC(AD9850_DATA)); // enable clock for GPIO
|
||||||
|
gpio_clear(GPIO_PORT(AD9850_DATA), GPIO_PIN(AD9850_DATA)); // don't care about data
|
||||||
|
gpio_mode_setup(GPIO_PORT(AD9850_DATA), GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN(AD9850_DATA)); // set pin as output
|
||||||
|
gpio_set_output_options(GPIO_PORT(AD9850_DATA), GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, GPIO_PIN(AD9850_DATA)); // set output as push-pull
|
||||||
|
rcc_periph_clock_enable(GPIO_RCC(AD9850_WCLK)); // enable clock for GPIO
|
||||||
|
gpio_clear(GPIO_PORT(AD9850_WCLK), GPIO_PIN(AD9850_WCLK)); // idle low
|
||||||
|
gpio_mode_setup(GPIO_PORT(AD9850_WCLK), GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN(AD9850_WCLK)); // set pin as output
|
||||||
|
gpio_set_output_options(GPIO_PORT(AD9850_WCLK), GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, GPIO_PIN(AD9850_WCLK)); // set output as push-pull
|
||||||
|
rcc_periph_clock_enable(GPIO_RCC(AD9850_FQUD)); // enable clock for GPIO
|
||||||
|
gpio_clear(GPIO_PORT(AD9850_FQUD), GPIO_PIN(AD9850_FQUD)); // idle low
|
||||||
|
gpio_mode_setup(GPIO_PORT(AD9850_FQUD), GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN(AD9850_FQUD)); // set pin as output
|
||||||
|
gpio_set_output_options(GPIO_PORT(AD9850_FQUD), GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, GPIO_PIN(AD9850_FQUD)); // set output as push-pull
|
||||||
|
load_settings(NULL, &ad9850_freq); // load saved frequency
|
||||||
|
ad9850_set_freq(ad9850_freq);
|
||||||
|
puts_debug("OK\n");
|
||||||
|
|
||||||
|
puts_debug("setup HD44780 LCD: ");
|
||||||
|
lcd_hd44780_setup(true, false); // I don't know why the initialisation does not always works the first time (but replugging the power in solveds it)
|
||||||
|
update_display(ad9850_freq, digit_position, digit_selected);
|
||||||
|
puts_debug("OK\n");
|
||||||
|
|
||||||
|
load_settings(&digit_position, &ad9850_freq);
|
||||||
// setup terminal
|
// setup terminal
|
||||||
terminal_prefix = ""; // set default prefix
|
terminal_prefix = ""; // set default prefix
|
||||||
terminal_process = &process_command; // set central function to process commands
|
terminal_process = &process_command; // set central function to process commands
|
||||||
|
@ -467,16 +692,62 @@ void main(void)
|
||||||
}
|
}
|
||||||
if (button_flag) { // user pressed button
|
if (button_flag) { // user pressed button
|
||||||
action = true; // action has been performed
|
action = true; // action has been performed
|
||||||
puts("button pressed\n");
|
sleep_ms(10); // debounce
|
||||||
led_toggle(); // toggle LED
|
if (!gpio_get(GPIO_PORT(BUTTON_PIN), GPIO_PIN(BUTTON_PIN))) { // only allow press (not release)
|
||||||
|
digit_selected = !digit_selected;
|
||||||
|
update_display(ad9850_freq, digit_position, digit_selected);
|
||||||
|
}
|
||||||
sleep_ms(100); // wait a bit to remove noise and double trigger
|
sleep_ms(100); // wait a bit to remove noise and double trigger
|
||||||
button_flag = false; // reset flag
|
button_flag = false; // reset flag
|
||||||
}
|
}
|
||||||
|
if (rotary_flag) { // user turned rotary encoder
|
||||||
|
action = true; // action has been performed
|
||||||
|
if (rotary_flag > 0) {
|
||||||
|
if (digit_selected) {
|
||||||
|
uint64_t unit = 1;
|
||||||
|
for (uint8_t i = 0; i < digit_position; i++) {
|
||||||
|
unit *= 10;
|
||||||
|
}
|
||||||
|
ad9850_freq += unit;
|
||||||
|
if (ad9850_freq > AD9850_MAX_FREQ) {
|
||||||
|
ad9850_freq = AD9850_MAX_FREQ;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (digit_position > 0) {
|
||||||
|
digit_position--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (digit_selected) {
|
||||||
|
uint64_t unit = 1;
|
||||||
|
for (uint8_t i = 0; i < digit_position; i++) {
|
||||||
|
unit *= 10;
|
||||||
|
}
|
||||||
|
if (unit > ad9850_freq) {
|
||||||
|
ad9850_freq = 0;
|
||||||
|
} else {
|
||||||
|
ad9850_freq -= unit;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (digit_position < 11) {
|
||||||
|
digit_position++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
save_settings(digit_position, ad9850_freq); // save setting to SRAM
|
||||||
|
ad9850_set_freq(ad9850_freq / 1000.0); // reset frequency (in case the target has been reset)
|
||||||
|
update_display(ad9850_freq, digit_position, digit_selected);
|
||||||
|
sleep_ms(100); // wait a bit to remove noise and double trigger
|
||||||
|
rotary_flag = 0; // reset flag
|
||||||
|
}
|
||||||
if (wakeup_flag) { // time to do periodic checks
|
if (wakeup_flag) { // time to do periodic checks
|
||||||
wakeup_flag = false; // clear flag
|
wakeup_flag = false; // clear flag
|
||||||
}
|
}
|
||||||
if (second_flag) { // one second passed
|
if (second_flag) { // one second passed
|
||||||
second_flag = false; // clear flag
|
second_flag = false; // clear flag
|
||||||
|
action = true; // action has been performed
|
||||||
|
ad9850_set_freq(ad9850_freq / 1000.0); // reset frequency (in case the target has been reset)
|
||||||
|
update_display(ad9850_freq, digit_position, digit_selected);
|
||||||
led_toggle(); // toggle LED to indicate if main function is stuck
|
led_toggle(); // toggle LED to indicate if main function is stuck
|
||||||
}
|
}
|
||||||
if (action) { // go to sleep if nothing had to be done, else recheck for activity
|
if (action) { // go to sleep if nothing had to be done, else recheck for activity
|
||||||
|
@ -500,3 +771,16 @@ void rtc_wkup_isr(void)
|
||||||
tick = WAKEUP_FREQ; // restart count down
|
tick = WAKEUP_FREQ; // restart count down
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** interrupt service routine called when rotary encoder is turned */
|
||||||
|
void GPIO_EXTI_ISR(ROTARY_A)(void)
|
||||||
|
{
|
||||||
|
exti_reset_request(GPIO_EXTI(ROTARY_A)); // reset interrupt
|
||||||
|
if (rotary_flag) { // flag not cleared yet
|
||||||
|
return;
|
||||||
|
} else if (gpio_get(GPIO_PORT(ROTARY_B), GPIO_PIN(ROTARY_B))) {
|
||||||
|
rotary_flag = 1;
|
||||||
|
} else {
|
||||||
|
rotary_flag = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -229,7 +229,7 @@ static char usb_serial[] = "0123456789ab";
|
||||||
*/
|
*/
|
||||||
static const char* usb_strings[] = {
|
static const char* usb_strings[] = {
|
||||||
"CuVoodoo", /**< manufacturer string */
|
"CuVoodoo", /**< manufacturer string */
|
||||||
"CuVoodoo STM32F4xx firmware", /**< product string */
|
"CuVoodoo clock generator", /**< product string */
|
||||||
(const char*)usb_serial, /**< device ID used as serial number */
|
(const char*)usb_serial, /**< device ID used as serial number */
|
||||||
"DFU bootloader (runtime mode)", /**< DFU interface string */
|
"DFU bootloader (runtime mode)", /**< DFU interface string */
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue