updated ui added new features

This commit is contained in:
zach
2025-12-27 15:32:32 -07:00
parent 02ca7801ea
commit a2cfae3a22
589 changed files with 181780 additions and 569 deletions
+85
View File
@@ -0,0 +1,85 @@
/*---------------------------------------------------------------------------*\
FILE........: adc_rec_usb.c
AUTHOR......: David Rowe
DATE CREATED: Nov 2015
Records a 16 kHz sample rate raw file from one of the ADC channels,
which are connected to pins PA1 (ADC1) and PA2 (ADC2). Uploads to the
host PC via the STM32F4 USB port, which appears as /dev/ttyACM0.
On the SM1000:
ADC1 -> PA1 -> "from radio"
ADC2 -> PA2 -> "mic amp"
I used this to record:
$ sudo dd if=/dev/ttyACM0 of=test.raw count=100
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2015 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. This program is
distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <math.h>
#include "stm32f4_adc.h"
#include "stm32f4_usb_vcp.h"
#include "sm1000_leds_switches.h"
#define N (ADC_BUF_SZ*6)
/* test tone parameters */
#define FREQ 999.0 /* make sure no alignment with frame boundaries */
#define FS 16000.0
#define AMP 10000.0
extern int adc_overflow1;
extern int adc_overflow2;
int main(void){
short buf[N];
#ifdef TEST_TONE
float phase = 0.0;
float sam;
int i;
#endif
usb_vcp_init();
adc_open(ADC_FS_96KHZ, 4*N, NULL, NULL);
sm1000_leds_switches_init();
/* set up test buffer, lets us test USB comms indep of ADC, record to a file
then play back/examine waveform to make sure no clicks */
while(1) {
while(adc1_read(buf, N) == -1);
#ifdef TEST_TONE
for(i=0; i<N; i++) {
phase += 2.0*M_PI*FREQ/FS;
phase -= 2.0*M_PI*floor(phase/(2.0*M_PI));
sam = AMP*cos(phase);
buf[i] = (short)sam;
}
#endif
led_pwr(1);
VCP_send_buffer((uint8_t*)buf, sizeof(buf));
led_pwr(0);
}
}
+57
View File
@@ -0,0 +1,57 @@
/*---------------------------------------------------------------------------*\
FILE........: dac_ut.c
AUTHOR......: David Rowe
DATE CREATED: May 31 2013
Plays a 500 Hz sine wave sampled at 16 kHz out of PA5 on a Discovery board,
or the speaker output of the SM1000.
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2013 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. This program is
distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include "stm32f4_dac.h"
#define SINE_SAMPLES 32
/* 32 sample sine wave which at Fs=16kHz will be 500Hz. Note samples
are 16 bit 2's complement, the DAC driver convertsto 12 bit
unsigned. */
short aSine[] = {
-16, 6384, 12528, 18192, 23200, 27232, 30256, 32128,
32752, 32128, 30256, 27232, 23152, 18192, 12528, 6384,
-16, -6416, -12560, -18224, -23184, -27264, -30288, -32160,
-32768, -32160, -30288, -27264, -23184, -18224, -12560, -6416
};
int main(void) {
dac_open(DAC_FS_16KHZ, 4*DAC_BUF_SZ, 0, 0);
while (1) {
/* keep DAC FIFOs topped up */
dac1_write((short*)aSine, SINE_SAMPLES, 0);
dac2_write((short*)aSine, SINE_SAMPLES, 0);
}
}
+57
View File
@@ -0,0 +1,57 @@
/*---------------------------------------------------------------------------*\
FILE........: debugblinky.c
AUTHOR......: David Rowe
DATE CREATED: 12 August 2014
Configures GPIO pins used for debug blinkies
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2014 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. This program is
distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "stm32f4xx.h"
void init_debug_blinky(void) {
GPIO_InitTypeDef GPIO_InitStruct;
/* PE0-3 used to indicate activity, PE4-5 for SM2000 +12V rail switching */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOE, &GPIO_InitStruct);
}
/* SM2000: 0 for +12V RX power, 1 for +12V TX power */
void txrx_12V(int state) {
if (state) {
GPIOE->ODR &= ~(1 << 5); /* +12VRXENB off */
GPIOE->ODR |= (1 << 4); /* +12VTXENB on */
}
else {
GPIOE->ODR &= ~(1 << 4); /* +12VTXENB off */
GPIOE->ODR |= (1 << 5); /* +12VRXENB on */
}
}
+67
View File
@@ -0,0 +1,67 @@
/*
memtools.h
June 2019
Tools for looking at memory on the stm32. See also debug_alloc.h
*/
#include <stdlib.h>
#include <sys/types.h>
#include <math.h>
#include "memtools.h"
/* Required memory allocation wrapper for embedded platforms. For SM1000, we can just use stdlib's memory functions. */
void* codec2_malloc(size_t size)
{
return malloc(size);
}
void* codec2_calloc(size_t nmemb, size_t size)
{
return calloc(nmemb, size);
}
void codec2_free(void* ptr)
{
free(ptr);
}
/* startup_stm32f4xx.s has been modified to fill RAM segment from bss up with 0x0x55555555 */
void memtools_find_unused( int (*printf_func)(const char *fmt, ...) ) {
int32_t *p, *start;
int found = 0;
(*printf_func)("chunks of RAM segment > 256 bytes containing start up pattern:\n");
/* count down from top of memory through stack, empty memory, then to heap */
for (p =(int32_t*)0x20000000; p<(int32_t*)0x20020000; p++) {
if (found == 0) {
if (*p == 0x55555555) {
start = p;
found = 1;
}
}
if (found == 1) {
if (*p != 0x55555555) {
found = 0;
int bytes = (void*)p - (void*)start;
if (bytes >= 0x100)
(*printf_func)(" start: 0x%x end: 0x%x bytes: %d\n", (int) start, (int)p, bytes);
}
}
}
}
void memtools_isnan(float *vec, int n, char *label, int (*printf_func)(const char *fmt, ...)) {
int count = 0;
for(int i=0; i<n; i++) {
if (isnan(vec[i])) {
(*printf_func)("%s memtools_isnan: %d %p\n", label, i, &vec[i]);
if (count++ == 5) return;
}
}
}
+98
View File
@@ -0,0 +1,98 @@
/*!
* Callback driven menu handler.
*
* The following is an implementation of a callback-driven menu system.
* It supports arbitrary levels of menus (limited by size of return stack)
* and supports arbitrary user events.
*
* Author Stuart Longland <me@vk4msl.id.au>
* Copyright (C) 2015 FreeDV project.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation. This program is
* distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see
* <http://www.gnu.org/licenses/>.
*/
#include "menu.h"
#include <stdlib.h>
/*!
* Return the Nth item on the stack.
*/
static const struct menu_stack_item_t* const menu_stack(
const struct menu_t* const menu,
uint8_t index)
{
if (menu->stack_depth <= index)
return NULL;
return &(menu->stack[menu->stack_depth - index - 1]);
}
/*!
* Return the Nth item on the stack.
*/
const struct menu_item_t* const menu_item(
const struct menu_t* const menu, uint8_t index)
{
const struct menu_stack_item_t* const current
= menu_stack(menu, index);
if (!current)
return NULL;
return current->item;
}
/*!
* Enter a (sub)-menu.
*/
int menu_enter(struct menu_t* const menu,
const struct menu_item_t* const item)
{
if (menu->stack_depth == MENU_STACK_SZ)
return -1;
menu->stack[menu->stack_depth].item = item;
menu->stack[menu->stack_depth].index = menu->current;
menu->stack_depth++;
(item->event_cb)(menu, MENU_EVT_ENTERED);
return 0;
}
/*!
* Return from a (sub)-menu.
*/
void menu_leave(struct menu_t* const menu)
{
if (!menu->stack_depth)
return; /* Already out of the menu */
menu->last = menu_item(menu, 0);
menu->stack_depth--;
const struct menu_stack_item_t* current = menu_stack(menu, 0);
if (current && current->item) {
menu->current = current->index;
(current->item->event_cb)(menu, MENU_EVT_RETURNED);
}
}
/*!
* Execute the callback for the current item with a user-supplied event.
*/
void menu_exec(struct menu_t* const menu, uint32_t event)
{
const struct menu_item_t* item = menu_item(menu, 0);
if (item && item->event_cb)
(item->event_cb)(menu, event);
}
+175
View File
@@ -0,0 +1,175 @@
/*!
* Morse code library.
*
* This implements a state machine for playing back morse code messages.
*
* Author Stuart Longland <me@vk4msl.id.au>
* Copyright (C) 2015 FreeDV project.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation. This program is
* distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see
* <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include "morse.h"
#include <stdio.h>
/*! Symbol table element definition */
struct morse_sym_table_t {
uint8_t code; uint8_t len;
};
/*! Symbol table: "digits" */
static const struct morse_sym_table_t morse_digits[] = {
{ .code = 0xf8, .len = 5 }, /* 0: ----- */
{ .code = 0x78, .len = 5 }, /* 1: .---- */
{ .code = 0x38, .len = 5 }, /* 2: ..--- */
{ .code = 0x18, .len = 5 }, /* 3: ...-- */
{ .code = 0x08, .len = 5 }, /* 4: ....- */
{ .code = 0x00, .len = 5 }, /* 5: ..... */
{ .code = 0x80, .len = 5 }, /* 6: -.... */
{ .code = 0xc0, .len = 5 }, /* 7: --... */
{ .code = 0xe0, .len = 5 }, /* 8: ---.. */
{ .code = 0xf0, .len = 5 }, /* 9: ----. */
};
/*! Symbol table: "letters" */
static const struct morse_sym_table_t morse_letters[] = {
{ .code = 0x40, .len = 2 }, /* A: .- */
{ .code = 0x80, .len = 4 }, /* B: -... */
{ .code = 0xa0, .len = 4 }, /* C: -.-. */
{ .code = 0x80, .len = 3 }, /* D: -.. */
{ .code = 0x00, .len = 1 }, /* E: . */
{ .code = 0x20, .len = 4 }, /* F: ..-. */
{ .code = 0xc0, .len = 3 }, /* G: --. */
{ .code = 0x00, .len = 4 }, /* H: .... */
{ .code = 0x00, .len = 2 }, /* I: .. */
{ .code = 0x70, .len = 4 }, /* J: .--- */
{ .code = 0xa0, .len = 3 }, /* K: -.- */
{ .code = 0x40, .len = 4 }, /* L: .-.. */
{ .code = 0xc0, .len = 2 }, /* M: -- */
{ .code = 0x80, .len = 2 }, /* N: -. */
{ .code = 0xe0, .len = 3 }, /* O: --- */
{ .code = 0x60, .len = 4 }, /* P: .--. */
{ .code = 0xd0, .len = 4 }, /* Q: --.- */
{ .code = 0x40, .len = 3 }, /* R: .-. */
{ .code = 0x00, .len = 3 }, /* S: ... */
{ .code = 0x80, .len = 1 }, /* T: - */
{ .code = 0x20, .len = 3 }, /* U: ..- */
{ .code = 0x10, .len = 4 }, /* V: ...- */
{ .code = 0x60, .len = 3 }, /* W: .-- */
{ .code = 0x90, .len = 4 }, /* X: -..- */
{ .code = 0xb0, .len = 4 }, /* Y: -.-- */
{ .code = 0xc0, .len = 4 }, /* Z: --.. */
};
static void morse_next_sym(struct morse_player_t* const morse_player)
{
struct sfx_player_t* sfx_player = &(morse_player->sfx_player);
if (!morse_player->msg) {
sfx_play(sfx_player, NULL);
return;
}
uint8_t sym_rem = 0;
uint8_t sym_code = 0;
const struct morse_sym_table_t* sym = NULL;
const char* c = morse_player->msg;
while(!sym) {
if ((*c >= 'A') && (*c <= 'Z'))
/* Play a letter. (capitals) */
sym = &morse_letters[*c - 'A'];
else if ((*c >= 'a') && (*c <= 'z'))
/* Play a letter. (lowercase) */
sym = &morse_letters[*c - 'a'];
else if ((*c >= '0') && (*c <= '9'))
/* Play a digit. */
sym = &morse_digits[*c - '0'];
else if (*c == 0) {
morse_player->msg = NULL;
return;
}
c++;
}
morse_player->msg = c;
struct sfx_note_t* note = morse_player->sym;
sym_rem = sym->len;
sym_code = sym->code;
while(sym_rem) {
note->freq = morse_player->freq;
if (sym_code & 0x80)
/* Play a "dah" */
note->duration = morse_player->dit_time*3;
else
/* Play a "dit" */
note->duration = morse_player->dit_time;
note++;
sym_code <<= 1;
sym_rem--;
/* A gap follows */
note->freq = 0;
if (sym_rem) {
/* More of the character */
note->duration = morse_player->dit_time;
note++;
}
}
/* What comes next? */
if (*c == ' ') {
/* End of word */
note->duration = morse_player->dit_time*7;
note++;
} else if (*c) {
/* End of character */
note->duration = morse_player->dit_time*3;
note++;
}
/* Terminate the sequence */
note->freq = 0;
note->duration = 0;
/* Set the player up */
sfx_play(sfx_player, morse_player->sym);
}
/*!
* Start playing a particular effect.
* @param sfx_player Effect player state machine
* @param effect Pointer to sound effect (NULL == stop)
*/
void morse_play(struct morse_player_t* const morse_player,
const char* msg)
{
morse_player->msg = msg;
morse_next_sym(morse_player);
}
/*!
* Retrieve the next sample to be played.
*/
int16_t morse_next(struct morse_player_t* const morse_player)
{
if (!morse_player)
return(0);
if (!morse_player->sfx_player.note)
morse_next_sym(morse_player);
return sfx_next(&(morse_player->sfx_player));
}
+67
View File
@@ -0,0 +1,67 @@
/*!
* Sound effect player library.
*
* This implements a state machine for playing back various monophonic
* sound effects such as morse code symbols, clicks and alert tones.
*
* Author Stuart Longland <me@vk4msl.id.au>
* Copyright (C) 2015 FreeDV project.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation. This program is
* distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see
* <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include "sfx.h"
static void sfx_next_tone(struct sfx_player_t* const sfx_player)
{
struct tone_gen_t* tone_gen = &(sfx_player->tone_gen);
const struct sfx_note_t* note = sfx_player->note;
if (!note) {
tone_reset(tone_gen, 0, 0);
} else {
tone_reset(tone_gen, note->freq, note->duration);
if (!note->duration)
/* We are done */
sfx_player->note = NULL;
else
/* Move to next note */
sfx_player->note++;
}
}
/*!
* Start playing a particular effect.
* @param sfx_player Effect player state machine
* @param effect Pointer to sound effect (NULL == stop)
*/
void sfx_play(struct sfx_player_t* const sfx_player,
const struct sfx_note_t* effect)
{
sfx_player->note = effect;
sfx_next_tone(sfx_player);
}
/*!
* Retrieve the next sample to be played.
*/
int16_t sfx_next(struct sfx_player_t* const sfx_player)
{
if (!sfx_player)
return(0);
if (!sfx_player->tone_gen.remain)
sfx_next_tone(sfx_player);
return tone_next(&(sfx_player->tone_gen));
}
+229
View File
@@ -0,0 +1,229 @@
/*---------------------------------------------------------------------------*\
FILE........: sm1000_leds_switches.c
AUTHOR......: David Rowe
DATE CREATED: 18 July 2014
Functions for controlling LEDs and reading switches on the SM1000.
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2014 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. This program is
distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define _CPTT GPIO_Pin_10
#define LED_PWR GPIO_Pin_12
#define LED_PTT GPIO_Pin_13
#define LED_RT GPIO_Pin_14
#define LED_ERR GPIO_Pin_15
#define SWITCH_PTT GPIO_Pin_7
#define SWITCH_SELECT GPIO_Pin_0
#define SWITCH_BACK GPIO_Pin_1
#define EXT_PTT GPIO_Pin_8
#include <stm32f4xx.h>
#include <stm32f4xx_gpio.h>
#include "sm1000_leds_switches.h"
void sm1000_leds_switches_init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
/* output pins */
GPIO_InitStruct.GPIO_Pin = LED_PWR | LED_PTT | LED_RT | LED_ERR | _CPTT;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &GPIO_InitStruct);
/* input pins */
GPIO_InitStruct.GPIO_Pin = SWITCH_PTT | SWITCH_SELECT | SWITCH_BACK;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; /* we have our own external pull ups */
GPIO_Init(GPIOD, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = EXT_PTT;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; /* use internal pull up */
GPIO_Init(GPIOD, &GPIO_InitStruct);
}
void led_pwr(int state) {
if (state > 0)
GPIOD->ODR |= (1 << 12);
else if (state < 0)
GPIOD->ODR ^= (1 << 12);
else
GPIOD->ODR &= ~(1 << 12);
}
void led_ptt(int state) {
if (state > 0)
GPIOD->ODR |= (1 << 13);
else if (state < 0)
GPIOD->ODR |= (1 << 13);
else
GPIOD->ODR &= ~(1 << 13);
}
void led_rt(int state) {
if (state > 0)
GPIOD->ODR |= (1 << 14);
else if (state < 0)
GPIOD->ODR ^= (1 << 14);
else
GPIOD->ODR &= ~(1 << 14);
}
void led_err(int state) {
if (state > 0)
GPIOD->ODR |= (1 << 15);
else if (state < 0)
GPIOD->ODR ^= (1 << 15);
else
GPIOD->ODR &= ~(1 << 15);
}
void not_cptt(int state) {
if (state)
GPIOD->ODR |= (1 << 10);
else
GPIOD->ODR &= ~(1 << 10);
}
int switch_ptt(void) {
return GPIOD->IDR & (1 << 7);
}
int switch_select(void) {
return GPIOD->IDR & (1 << 0);
}
int switch_back(void) {
return GPIOD->IDR & (1 << 1);
}
int ext_ptt(void) {
return GPIOD->IDR & (1 << 8);
}
/*
FUNCTION: ColorfulRingOfDeath()
AUTHOR..: xenovacivus
Colourful ring of death, blink LEDs like crazy forever if something
really nasty happens. Adapted from USB Virtual COM Port (VCP)
module adapted from code I found here:
https://github.com/xenovacivus/STM32DiscoveryVCP
Call this to indicate a failure. Blinks the STM32F4 discovery LEDs
in sequence. At 168Mhz, the blinking will be very fast - about 5
Hz. Keep that in mind when debugging, knowing the clock speed
might help with debugging.
*/
int mycode; /* examine this with debugger if it dies */
void ColorfulRingOfDeath(int code) {
mycode = code;
uint16_t ring = 1;
while (1) {
uint32_t count = 0;
while (count++ < 5000000);
GPIOD->BSRRH = (ring << 12);
ring = ring << 1;
if (ring >= 1<<4) {
ring = 1;
}
GPIOD->BSRRL = (ring << 12);
}
}
void HardFault_Handler(void) { ColorfulRingOfDeath(1); }
void MemManage_Handler(void) { ColorfulRingOfDeath(2); }
void BusFault_Handler(void) { ColorfulRingOfDeath(3); }
void UsageFault_Handler(void){ ColorfulRingOfDeath(4); }
void switch_tick(struct switch_t* const sw)
{
if (sw->sw != sw->raw) {
/* State transition, reset timer */
if (sw->state == SW_STEADY)
sw->last = sw->sw;
sw->state = SW_DEBOUNCE;
sw->timer = DEBOUNCE_DELAY;
sw->sw = sw->raw;
} else if (sw->state == SW_DEBOUNCE) {
if (sw->timer > 0) {
/* Steady so far, keep waiting */
sw->timer--;
} else {
/* Steady state reached */
sw->state = SW_STEADY;
}
} else if (sw->sw) {
/* Hold state. Yes this will wrap, but who cares? */
sw->timer++;
}
}
void switch_update(struct switch_t* const sw, uint8_t state)
{
sw->raw = state;
if (sw->raw == sw->sw)
return;
if (sw->state == SW_STEADY)
sw->last = sw->sw;
sw->timer = DEBOUNCE_DELAY;
sw->sw = sw->raw;
sw->state = SW_DEBOUNCE;
}
uint32_t switch_pressed(const struct switch_t* const sw)
{
if ((sw->state == SW_STEADY) && sw->sw)
return sw->timer;
return 0;
}
int switch_released(const struct switch_t* const sw)
{
if (sw->state != SW_STEADY)
return 0;
if (!sw->last)
return 0;
if (sw->sw)
return 0;
return 1;
}
void switch_ack(struct switch_t* const sw)
{
if (sw->state == SW_STEADY)
sw->last = sw->sw;
}
+41
View File
@@ -0,0 +1,41 @@
/*---------------------------------------------------------------------------*\
FILE........: sm1000_leds_switches_ut.c
AUTHOR......: David Rowe
DATE CREATED: August 5 2014
Unit Test program for the SM1000 switches and LEDs driver.
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2014 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. This program is
distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include "sm1000_leds_switches.h"
int main(void) {
sm1000_leds_switches_init();
while(1) {
led_pwr(switch_select());
led_ptt(switch_ptt());
led_rt(switch_back());
led_err(!switch_back());
}
}
File diff suppressed because it is too large Load Diff
+62
View File
@@ -0,0 +1,62 @@
/*!
* Sound effect library.
*
* This provides some sound effects for the SM1000 UI.
*
* Author Stuart Longland <me@vk4msl.id.au>
* Copyright (C) 2015 FreeDV project.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation. This program is
* distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see
* <http://www.gnu.org/licenses/>.
*/
#include "sounds.h"
const struct sfx_note_t sound_startup[] = {
{.freq = 600, .duration = 80},
{.freq = 800, .duration = 80},
{.freq = 1000, .duration = 80},
{.freq = 0, .duration = 0}
};
const struct sfx_note_t sound_returned[] = {
{.freq = 1000, .duration = 80},
{.freq = 800, .duration = 80},
{.freq = 600, .duration = 80},
{.freq = 0, .duration = 0}
};
const struct sfx_note_t sound_click[] = {
{.freq = 1200, .duration = 10},
{.freq = 0, .duration = 0}
};
const struct sfx_note_t sound_death_march[] = {
{.freq = 340, .duration = 400},
{.freq = 0, .duration = 80},
{.freq = 340, .duration = 400},
{.freq = 0, .duration = 80},
{.freq = 340, .duration = 400},
{.freq = 0, .duration = 80},
{.freq = 420, .duration = 400},
{.freq = 0, .duration = 80},
{.freq = 400, .duration = 300},
{.freq = 0, .duration = 80},
{.freq = 340, .duration = 120},
{.freq = 0, .duration = 80},
{.freq = 340, .duration = 120},
{.freq = 0, .duration = 80},
{.freq = 300, .duration = 200},
{.freq = 0, .duration = 80},
{.freq = 340, .duration = 400},
{.freq = 0, .duration = 0},
};
+526
View File
@@ -0,0 +1,526 @@
/**
******************************************************************************
* @file startup_stm32f4xx.s
* @author MCD Application Team
* @version V1.0.0
* @date 30-September-2011
* @brief STM32F4xx Devices vector table for Atollic TrueSTUDIO toolchain.
* This module performs:
* - Set the initial SP
* - Set the initial PC == Reset_Handler,
* - Set the vector table entries with the exceptions ISR address
* - Configure the clock system and the external SRAM mounted on
* STM324xG-EVAL board to be used as data memory (optional,
* to be enabled by user)
* - Branches to main in the C library (which eventually
* calls main()).
* After Reset the Cortex-M4 processor is in Thread mode,
* priority is Privileged, and the Stack is set to Main.
******************************************************************************
* @attention
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
* <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
******************************************************************************
*/
.syntax unified
.cpu cortex-m3
.fpu softvfp
.thumb
.global g_pfnVectors
.global Default_Handler
.global EndofMain
/* start address for the initialization values of the .data section.
defined in linker script */
.word _sidata
/* start address for the .data section. defined in linker script */
.word _sdata
/* end address for the .data section. defined in linker script */
.word _edata
/* start address for the .bss section. defined in linker script */
.word _sbss
/* end address for the .bss section. defined in linker script */
.word _ebss
/* stack used for SystemInit_ExtMemCtl; always internal RAM used */
/**
* @brief This is the code that gets called when the processor first
* starts execution following a reset event. Only the absolutely
* necessary set is performed, after which the application
* supplied main() routine is called.
* @param None
* @retval : None
*/
.section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
/* Copy the data segment initializers from flash to SRAM */
movs r1, #0
b LoopCopyDataInit
CopyDataInit:
ldr r3, =_sidata
ldr r3, [r3, r1]
str r3, [r0, r1]
adds r1, r1, #4
LoopCopyDataInit:
ldr r0, =_sdata
ldr r3, =_edata
adds r2, r0, r1
cmp r2, r3
bcc CopyDataInit
ldr r2, =_sbss
b LoopFillZerobss
/* Zero fill all memory from bss up */
FillZerobss:
movs r3, #0
str r3, [r2], #4
LoopFillZerobss:
ldr r3, = _ebss
cmp r2, r3
bcc FillZerobss
/* Zero memory from bss up with a sentinel value */
b LoopFillsentinel
Fillsentinel:
ldr r3, = 0x55555555 /* sentinel value we put in memory */
str r3, [r2], #4
LoopFillsentinel:
ldr r3, = 0x2001fffc /* end of ram */
cmp r2, r3
bcc Fillsentinel
/* Call the clock system initialization function.*/
bl SystemInit
/* Call static constructors */
bl __libc_init_array
/* Call the application's entry point.*/
bl main
EndofMain:
bl .
.size Reset_Handler, .-Reset_Handler
/**
* @brief This is the code that gets called when the processor receives an
* unexpected interrupt. This simply enters an infinite loop, preserving
* the system state for examination by a debugger.
* @param None
* @retval None
*/
.section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
b Infinite_Loop
.size Default_Handler, .-Default_Handler
/******************************************************************************
*
* The minimal vector table for a Cortex M3. Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
*
*******************************************************************************/
.section .isr_vector,"a",%progbits
.type g_pfnVectors, %object
.size g_pfnVectors, .-g_pfnVectors
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
/* External Interrupts */
.word WWDG_IRQHandler /* Window WatchDog */
.word PVD_IRQHandler /* PVD through EXTI Line detection */
.word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */
.word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */
.word FLASH_IRQHandler /* FLASH */
.word RCC_IRQHandler /* RCC */
.word EXTI0_IRQHandler /* EXTI Line0 */
.word EXTI1_IRQHandler /* EXTI Line1 */
.word EXTI2_IRQHandler /* EXTI Line2 */
.word EXTI3_IRQHandler /* EXTI Line3 */
.word EXTI4_IRQHandler /* EXTI Line4 */
.word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */
.word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */
.word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */
.word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */
.word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */
.word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */
.word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */
.word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */
.word CAN1_TX_IRQHandler /* CAN1 TX */
.word CAN1_RX0_IRQHandler /* CAN1 RX0 */
.word CAN1_RX1_IRQHandler /* CAN1 RX1 */
.word CAN1_SCE_IRQHandler /* CAN1 SCE */
.word EXTI9_5_IRQHandler /* External Line[9:5]s */
.word TIM1_BRK_TIM9_IRQHandler /* TIM1 Break and TIM9 */
.word TIM1_UP_TIM10_IRQHandler /* TIM1 Update and TIM10 */
.word TIM1_TRG_COM_TIM11_IRQHandler /* TIM1 Trigger and Commutation and TIM11 */
.word TIM1_CC_IRQHandler /* TIM1 Capture Compare */
.word TIM2_IRQHandler /* TIM2 */
.word TIM3_IRQHandler /* TIM3 */
.word TIM4_IRQHandler /* TIM4 */
.word I2C1_EV_IRQHandler /* I2C1 Event */
.word I2C1_ER_IRQHandler /* I2C1 Error */
.word I2C2_EV_IRQHandler /* I2C2 Event */
.word I2C2_ER_IRQHandler /* I2C2 Error */
.word SPI1_IRQHandler /* SPI1 */
.word SPI2_IRQHandler /* SPI2 */
.word USART1_IRQHandler /* USART1 */
.word USART2_IRQHandler /* USART2 */
.word USART3_IRQHandler /* USART3 */
.word EXTI15_10_IRQHandler /* External Line[15:10]s */
.word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */
.word OTG_FS_WKUP_IRQHandler /* USB OTG FS Wakeup through EXTI line */
.word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */
.word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */
.word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */
.word TIM8_CC_IRQHandler /* TIM8 Capture Compare */
.word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */
.word FSMC_IRQHandler /* FSMC */
.word SDIO_IRQHandler /* SDIO */
.word TIM5_IRQHandler /* TIM5 */
.word SPI3_IRQHandler /* SPI3 */
.word UART4_IRQHandler /* UART4 */
.word UART5_IRQHandler /* UART5 */
.word TIM6_DAC_IRQHandler /* TIM6 and DAC1&2 underrun errors */
.word TIM7_IRQHandler /* TIM7 */
.word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */
.word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */
.word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */
.word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */
.word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */
.word ETH_IRQHandler /* Ethernet */
.word ETH_WKUP_IRQHandler /* Ethernet Wakeup through EXTI line */
.word CAN2_TX_IRQHandler /* CAN2 TX */
.word CAN2_RX0_IRQHandler /* CAN2 RX0 */
.word CAN2_RX1_IRQHandler /* CAN2 RX1 */
.word CAN2_SCE_IRQHandler /* CAN2 SCE */
.word OTG_FS_IRQHandler /* USB OTG FS */
.word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */
.word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */
.word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */
.word USART6_IRQHandler /* USART6 */
.word I2C3_EV_IRQHandler /* I2C3 event */
.word I2C3_ER_IRQHandler /* I2C3 error */
.word OTG_HS_EP1_OUT_IRQHandler /* USB OTG HS End Point 1 Out */
.word OTG_HS_EP1_IN_IRQHandler /* USB OTG HS End Point 1 In */
.word OTG_HS_WKUP_IRQHandler /* USB OTG HS Wakeup through EXTI */
.word OTG_HS_IRQHandler /* USB OTG HS */
.word DCMI_IRQHandler /* DCMI */
.word CRYP_IRQHandler /* CRYP crypto */
.word HASH_RNG_IRQHandler /* Hash and Rng */
.word FPU_IRQHandler /* FPU */
/*******************************************************************************
*
* Provide weak aliases for each Exception handler to the Default_Handler.
* As they are weak aliases, any function with the same name will override
* this definition.
*
*******************************************************************************/
.weak NMI_Handler
.thumb_set NMI_Handler,Default_Handler
.weak HardFault_Handler
.thumb_set HardFault_Handler,Default_Handler
.weak MemManage_Handler
.thumb_set MemManage_Handler,Default_Handler
.weak BusFault_Handler
.thumb_set BusFault_Handler,Default_Handler
.weak UsageFault_Handler
.thumb_set UsageFault_Handler,Default_Handler
.weak SVC_Handler
.thumb_set SVC_Handler,Default_Handler
.weak DebugMon_Handler
.thumb_set DebugMon_Handler,Default_Handler
.weak PendSV_Handler
.thumb_set PendSV_Handler,Default_Handler
.weak SysTick_Handler
.thumb_set SysTick_Handler,Default_Handler
.weak WWDG_IRQHandler
.thumb_set WWDG_IRQHandler,Default_Handler
.weak PVD_IRQHandler
.thumb_set PVD_IRQHandler,Default_Handler
.weak TAMP_STAMP_IRQHandler
.thumb_set TAMP_STAMP_IRQHandler,Default_Handler
.weak RTC_WKUP_IRQHandler
.thumb_set RTC_WKUP_IRQHandler,Default_Handler
.weak FLASH_IRQHandler
.thumb_set FLASH_IRQHandler,Default_Handler
.weak RCC_IRQHandler
.thumb_set RCC_IRQHandler,Default_Handler
.weak EXTI0_IRQHandler
.thumb_set EXTI0_IRQHandler,Default_Handler
.weak EXTI1_IRQHandler
.thumb_set EXTI1_IRQHandler,Default_Handler
.weak EXTI2_IRQHandler
.thumb_set EXTI2_IRQHandler,Default_Handler
.weak EXTI3_IRQHandler
.thumb_set EXTI3_IRQHandler,Default_Handler
.weak EXTI4_IRQHandler
.thumb_set EXTI4_IRQHandler,Default_Handler
.weak DMA1_Stream0_IRQHandler
.thumb_set DMA1_Stream0_IRQHandler,Default_Handler
.weak DMA1_Stream1_IRQHandler
.thumb_set DMA1_Stream1_IRQHandler,Default_Handler
.weak DMA1_Stream2_IRQHandler
.thumb_set DMA1_Stream2_IRQHandler,Default_Handler
.weak DMA1_Stream3_IRQHandler
.thumb_set DMA1_Stream3_IRQHandler,Default_Handler
.weak DMA1_Stream4_IRQHandler
.thumb_set DMA1_Stream4_IRQHandler,Default_Handler
.weak DMA1_Stream5_IRQHandler
.thumb_set DMA1_Stream5_IRQHandler,Default_Handler
.weak DMA1_Stream6_IRQHandler
.thumb_set DMA1_Stream6_IRQHandler,Default_Handler
.weak ADC_IRQHandler
.thumb_set ADC_IRQHandler,Default_Handler
.weak CAN1_TX_IRQHandler
.thumb_set CAN1_TX_IRQHandler,Default_Handler
.weak CAN1_RX0_IRQHandler
.thumb_set CAN1_RX0_IRQHandler,Default_Handler
.weak CAN1_RX1_IRQHandler
.thumb_set CAN1_RX1_IRQHandler,Default_Handler
.weak CAN1_SCE_IRQHandler
.thumb_set CAN1_SCE_IRQHandler,Default_Handler
.weak EXTI9_5_IRQHandler
.thumb_set EXTI9_5_IRQHandler,Default_Handler
.weak TIM1_BRK_TIM9_IRQHandler
.thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler
.weak TIM1_UP_TIM10_IRQHandler
.thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler
.weak TIM1_TRG_COM_TIM11_IRQHandler
.thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler
.weak TIM1_CC_IRQHandler
.thumb_set TIM1_CC_IRQHandler,Default_Handler
.weak TIM2_IRQHandler
.thumb_set TIM2_IRQHandler,Default_Handler
.weak TIM3_IRQHandler
.thumb_set TIM3_IRQHandler,Default_Handler
.weak TIM4_IRQHandler
.thumb_set TIM4_IRQHandler,Default_Handler
.weak I2C1_EV_IRQHandler
.thumb_set I2C1_EV_IRQHandler,Default_Handler
.weak I2C1_ER_IRQHandler
.thumb_set I2C1_ER_IRQHandler,Default_Handler
.weak I2C2_EV_IRQHandler
.thumb_set I2C2_EV_IRQHandler,Default_Handler
.weak I2C2_ER_IRQHandler
.thumb_set I2C2_ER_IRQHandler,Default_Handler
.weak SPI1_IRQHandler
.thumb_set SPI1_IRQHandler,Default_Handler
.weak SPI2_IRQHandler
.thumb_set SPI2_IRQHandler,Default_Handler
.weak USART1_IRQHandler
.thumb_set USART1_IRQHandler,Default_Handler
.weak USART2_IRQHandler
.thumb_set USART2_IRQHandler,Default_Handler
.weak USART3_IRQHandler
.thumb_set USART3_IRQHandler,Default_Handler
.weak EXTI15_10_IRQHandler
.thumb_set EXTI15_10_IRQHandler,Default_Handler
.weak RTC_Alarm_IRQHandler
.thumb_set RTC_Alarm_IRQHandler,Default_Handler
.weak OTG_FS_WKUP_IRQHandler
.thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler
.weak TIM8_BRK_TIM12_IRQHandler
.thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler
.weak TIM8_UP_TIM13_IRQHandler
.thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler
.weak TIM8_TRG_COM_TIM14_IRQHandler
.thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler
.weak TIM8_CC_IRQHandler
.thumb_set TIM8_CC_IRQHandler,Default_Handler
.weak DMA1_Stream7_IRQHandler
.thumb_set DMA1_Stream7_IRQHandler,Default_Handler
.weak FSMC_IRQHandler
.thumb_set FSMC_IRQHandler,Default_Handler
.weak SDIO_IRQHandler
.thumb_set SDIO_IRQHandler,Default_Handler
.weak TIM5_IRQHandler
.thumb_set TIM5_IRQHandler,Default_Handler
.weak SPI3_IRQHandler
.thumb_set SPI3_IRQHandler,Default_Handler
.weak UART4_IRQHandler
.thumb_set UART4_IRQHandler,Default_Handler
.weak UART5_IRQHandler
.thumb_set UART5_IRQHandler,Default_Handler
.weak TIM6_DAC_IRQHandler
.thumb_set TIM6_DAC_IRQHandler,Default_Handler
.weak TIM7_IRQHandler
.thumb_set TIM7_IRQHandler,Default_Handler
.weak DMA2_Stream0_IRQHandler
.thumb_set DMA2_Stream0_IRQHandler,Default_Handler
.weak DMA2_Stream1_IRQHandler
.thumb_set DMA2_Stream1_IRQHandler,Default_Handler
.weak DMA2_Stream2_IRQHandler
.thumb_set DMA2_Stream2_IRQHandler,Default_Handler
.weak DMA2_Stream3_IRQHandler
.thumb_set DMA2_Stream3_IRQHandler,Default_Handler
.weak DMA2_Stream4_IRQHandler
.thumb_set DMA2_Stream4_IRQHandler,Default_Handler
.weak ETH_IRQHandler
.thumb_set ETH_IRQHandler,Default_Handler
.weak ETH_WKUP_IRQHandler
.thumb_set ETH_WKUP_IRQHandler,Default_Handler
.weak CAN2_TX_IRQHandler
.thumb_set CAN2_TX_IRQHandler,Default_Handler
.weak CAN2_RX0_IRQHandler
.thumb_set CAN2_RX0_IRQHandler,Default_Handler
.weak CAN2_RX1_IRQHandler
.thumb_set CAN2_RX1_IRQHandler,Default_Handler
.weak CAN2_SCE_IRQHandler
.thumb_set CAN2_SCE_IRQHandler,Default_Handler
.weak OTG_FS_IRQHandler
.thumb_set OTG_FS_IRQHandler,Default_Handler
.weak DMA2_Stream5_IRQHandler
.thumb_set DMA2_Stream5_IRQHandler,Default_Handler
.weak DMA2_Stream6_IRQHandler
.thumb_set DMA2_Stream6_IRQHandler,Default_Handler
.weak DMA2_Stream7_IRQHandler
.thumb_set DMA2_Stream7_IRQHandler,Default_Handler
.weak USART6_IRQHandler
.thumb_set USART6_IRQHandler,Default_Handler
.weak I2C3_EV_IRQHandler
.thumb_set I2C3_EV_IRQHandler,Default_Handler
.weak I2C3_ER_IRQHandler
.thumb_set I2C3_ER_IRQHandler,Default_Handler
.weak OTG_HS_EP1_OUT_IRQHandler
.thumb_set OTG_HS_EP1_OUT_IRQHandler,Default_Handler
.weak OTG_HS_EP1_IN_IRQHandler
.thumb_set OTG_HS_EP1_IN_IRQHandler,Default_Handler
.weak OTG_HS_WKUP_IRQHandler
.thumb_set OTG_HS_WKUP_IRQHandler,Default_Handler
.weak OTG_HS_IRQHandler
.thumb_set OTG_HS_IRQHandler,Default_Handler
.weak DCMI_IRQHandler
.thumb_set DCMI_IRQHandler,Default_Handler
.weak CRYP_IRQHandler
.thumb_set CRYP_IRQHandler,Default_Handler
.weak HASH_RNG_IRQHandler
.thumb_set HASH_RNG_IRQHandler,Default_Handler
.weak FPU_IRQHandler
.thumb_set FPU_IRQHandler,Default_Handler
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
+286
View File
@@ -0,0 +1,286 @@
/*---------------------------------------------------------------------------*\
FILE........: stm32f4_adc.c
AUTHOR......: David Rowe
DATE CREATED: 4 June 2013
Two channel ADC driver module for STM32F4. Pin PA1 connects to ADC1, pin
PA2 connects to ADC2.
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2013 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. This program is
distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "stm32f4xx_adc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "codec2_fifo.h"
#include "stm32f4_adc.h"
#include "debugblinky.h"
struct FIFO *adc1_fifo;
struct FIFO *adc2_fifo;
unsigned short adc_buf[ADC_BUF_SZ];
int adc_overflow1, adc_overflow2;
int half,full;
#define ADCx_DR_ADDRESS ((uint32_t)0x4001204C)
#define DMA_CHANNELx DMA_Channel_0
#define DMA_STREAMx DMA2_Stream0
#define ADCx ADC1
void adc_configure();
static void tim2_config(int fs_divisor);
// You can optionally supply your own storage for the FIFO buffers bu1 and buf2,
// or set them to NULL and they will be malloc-ed for you
void adc_open(int fs_divisor, int fifo_sz, short *buf1, short *buf2) {
if (buf1 == NULL) {
adc1_fifo = codec2_fifo_create(fifo_sz);
adc2_fifo = codec2_fifo_create(fifo_sz);
} else {
adc1_fifo = codec2_fifo_create_buf(fifo_sz, buf1);
adc2_fifo = codec2_fifo_create_buf(fifo_sz, buf2);
}
tim2_config(fs_divisor);
adc_configure();
init_debug_blinky();
}
/* n signed 16 bit samples in buf[] if return != -1 */
int adc1_read(short buf[], int n) {
return codec2_fifo_read(adc1_fifo, buf, n);
}
/* n signed 16 bit samples in buf[] if return != -1 */
int adc2_read(short buf[], int n) {
return codec2_fifo_read(adc2_fifo, buf, n);
}
/* Returns number of signed 16 bit samples in the FIFO currently */
int adc1_samps(){
return codec2_fifo_used(adc1_fifo);
}
/* Returns number of signed 16 bit samples in the FIFO currently */
int adc2_samps(){
return codec2_fifo_used(adc2_fifo);
}
static void tim2_config(int fs_divisor)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/* TIM2 Periph clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* --------------------------------------------------------
TIM2 input clock (TIM2CLK) is set to 2 * APB1 clock (PCLK1), since
APB1 prescaler is different from 1 (see system_stm32f4xx.c and Fig
13 clock tree figure in DM0031020.pdf).
Sample rate Fs = 2*PCLK1/TIM_ClockDivision
= (HCLK/2)/TIM_ClockDivision
----------------------------------------------------------- */
/* Time base configuration */
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = fs_divisor - 1;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* TIM2 TRGO selection */
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
}
void adc_configure(){
ADC_InitTypeDef ADC_init_structure;
GPIO_InitTypeDef GPIO_initStructre;
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// Clock configuration
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1ENR_GPIOAEN,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
// Analog pin configuration ADC1->PA1, ADC2->PA2
GPIO_initStructre.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_initStructre.GPIO_Mode = GPIO_Mode_AN;
GPIO_initStructre.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA,&GPIO_initStructre);
// ADC structure configuration
ADC_DeInit();
ADC_init_structure.ADC_DataAlign = ADC_DataAlign_Left;
ADC_init_structure.ADC_Resolution = ADC_Resolution_12b;
ADC_init_structure.ADC_ContinuousConvMode = DISABLE;
ADC_init_structure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
ADC_init_structure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
ADC_init_structure.ADC_NbrOfConversion = 2;
ADC_init_structure.ADC_ScanConvMode = ENABLE;
ADC_Init(ADCx,&ADC_init_structure);
// Select the channel to be read from
ADC_RegularChannelConfig(ADCx,ADC_Channel_1,1,ADC_SampleTime_144Cycles);
ADC_RegularChannelConfig(ADCx,ADC_Channel_2,2,ADC_SampleTime_144Cycles);
//ADC_VBATCmd(ENABLE);
/* DMA configuration **************************************/
DMA_DeInit(DMA_STREAMx);
DMA_InitStructure.DMA_Channel = DMA_CHANNELx;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADCx_DR_ADDRESS;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)adc_buf;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = ADC_BUF_SZ;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA_STREAMx, &DMA_InitStructure);
/* Enable DMA request after last transfer (Single-ADC mode) */
ADC_DMARequestAfterLastTransferCmd(ADCx, ENABLE);
/* Enable ADC1 DMA */
ADC_DMACmd(ADCx, ENABLE);
/* DMA2_Stream0 enable */
DMA_Cmd(DMA_STREAMx, ENABLE);
/* Enable DMA Half & Complete interrupts */
DMA_ITConfig(DMA2_Stream0, DMA_IT_TC | DMA_IT_HT, ENABLE);
/* Enable the DMA Stream IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// Enable and start ADC conversion
ADC_Cmd(ADC1,ENABLE);
ADC_SoftwareStartConv(ADC1);
}
/*
This function handles DMA Stream interrupt request.
*/
void DMA2_Stream0_IRQHandler(void) {
int i, j, sam;
short signed_buf1[ADC_BUF_SZ/2];
short signed_buf2[ADC_BUF_SZ/2];
GPIOE->ODR |= (1 << 0);
/* Half transfer interrupt */
if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_HTIF0) != RESET) {
half++;
/* convert to signed */
for(i=0, j=0; i<ADC_BUF_SZ/2; i+=2,j++) {
sam = (int)adc_buf[i] - 32768;
signed_buf1[j] = sam;
sam = (int)adc_buf[i+1] - 32768;
signed_buf2[j] = sam;
}
/* write first half to fifo */
if (codec2_fifo_write(adc1_fifo, signed_buf1, ADC_BUF_SZ/4) == -1) {
adc_overflow1++;
}
if (codec2_fifo_write(adc2_fifo, signed_buf2, ADC_BUF_SZ/4) == -1) {
adc_overflow2++;
}
/* Clear DMA Stream Transfer Complete interrupt pending bit */
DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_HTIF0);
}
/* Transfer complete interrupt */
if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0) != RESET) {
full++;
/* convert to signed */
for(i=0, j=0; i<ADC_BUF_SZ/2; i+=2,j++) {
sam = (int)adc_buf[ADC_BUF_SZ/2 + i] - 32768;
signed_buf1[j] = sam;
sam = (int)adc_buf[ADC_BUF_SZ/2 + i+1] - 32768;
signed_buf2[j] = sam;
}
/* write second half to fifo */
if (codec2_fifo_write(adc1_fifo, signed_buf1, ADC_BUF_SZ/4) == -1) {
adc_overflow1++;
}
if (codec2_fifo_write(adc2_fifo, signed_buf2, ADC_BUF_SZ/4) == -1) {
adc_overflow2++;
}
/* Clear DMA Stream Transfer Complete interrupt pending bit */
DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
}
GPIOE->ODR &= ~(1 << 0);
}
+427
View File
@@ -0,0 +1,427 @@
/*---------------------------------------------------------------------------*\
FILE........: stm32f4_dac.c
AUTHOR......: David Rowe
DATE CREATED: 1 June 2013
DAC driver module for STM32F4. DAC1 is connected to pin PA4, DAC2
is connected to pin PA5.
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2013 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. This program is
distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "stm32f4xx.h"
#include "codec2_fifo.h"
#include "stm32f4_dac.h"
#include "debugblinky.h"
/* write to these registers for 12 bit left aligned data, as per data sheet
make sure 4 least sig bits set to 0 */
#define DAC_DHR12R1_ADDRESS 0x40007408
#define DAC_DHR12R2_ADDRESS 0x40007414
#define DAC_MAX 4096 /* maximum amplitude */
/* y=mx+c mapping of samples16 bit shorts to DAC samples. Table: 74
of data sheet indicates With DAC buffer on, DAC range is limited to
0x0E0 to 0xF1C at VREF+ = 3.6 V, we have Vref=3.3V which is close.
*/
#define M ((3868.0-224.0)/65536.0)
#define C 2047.0
static struct FIFO *dac1_fifo;
static struct FIFO *dac2_fifo;
static unsigned short dac1_buf[DAC_BUF_SZ];
static unsigned short dac2_buf[DAC_BUF_SZ];
static void tim6_config(int fs_divisor);
static void dac1_config(void);
static void dac2_config(void);
int dac_underflow;
// You can optionally supply your own storage for the FIFO buffers bu1 and buf2,
// or set them to NULL and they will be malloc-ed for you
void dac_open(int fs_divisor, int fifo_size, short *buf1, short *buf2) {
memset(dac1_buf, 32768, sizeof(short)*DAC_BUF_SZ);
memset(dac2_buf, 32768, sizeof(short)*DAC_BUF_SZ);
/* Create fifos */
if ((buf1 == NULL) && (buf2 == NULL)) {
dac1_fifo = codec2_fifo_create(fifo_size);
dac2_fifo = codec2_fifo_create(fifo_size);
} else {
dac1_fifo = codec2_fifo_create_buf(fifo_size, buf1);
dac2_fifo = codec2_fifo_create_buf(fifo_size, buf2);
}
/* Turn on the clocks we need -----------------------------------------------*/
/* DMA1 clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
/* GPIOA clock enable (to be used with DAC) */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
/* DAC Periph clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
/* GPIO Pin configuration DAC1->PA.4, DAC2->PA.5 configuration --------------*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Timer and DAC 1 & 2 Configuration ----------------------------------------*/
tim6_config(fs_divisor);
dac1_config();
dac2_config();
init_debug_blinky();
}
/* Call these functions to send samples to the DACs. For your
convenience they accept signed 16 bit samples. You can optionally
limit how much data to store in the fifo */
int dac1_write(short buf[], int n, int limit) {
/* artificial limit < FIFO size */
if (limit) {
if ((codec2_fifo_used(dac1_fifo) + n) <= limit)
return codec2_fifo_write(dac1_fifo, buf, n);
else
return -1;
}
/* normal operation */
return codec2_fifo_write(dac1_fifo, buf, n);
}
int dac2_write(short buf[], int n, int limit) {
/* artificial limit < FIFO size */
if (limit) {
if ((codec2_fifo_used(dac2_fifo) + n) <= limit)
return codec2_fifo_write(dac2_fifo, buf, n);
else
return -1;
}
/* normal operation */
return codec2_fifo_write(dac2_fifo, buf, n);
}
int dac1_free() {
return codec2_fifo_free(dac1_fifo);
}
int dac2_free() {
return codec2_fifo_free(dac2_fifo);
}
static void tim6_config(int fs_divisor)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/* TIM6 Periph clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
/* --------------------------------------------------------
TIM6 input clock (TIM6CLK) is set to 2 * APB1 clock (PCLK1), since
APB1 prescaler is different from 1 (see system_stm32f4xx.c and Fig
13 clock tree figure in DM0031020.pdf).
Sample rate Fs = 2*PCLK1/TIM_ClockDivision
= (HCLK/2)/TIM_ClockDivision
----------------------------------------------------------- */
/* Time base configuration */
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = fs_divisor - 1;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
/* TIM6 TRGO selection */
TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);
/* TIM6 enable counter */
TIM_Cmd(TIM6, ENABLE);
}
static void dac1_config(void)
{
DAC_InitTypeDef DAC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* DAC channel 1 Configuration */
/*
This line fixed a bug that cost me 5 days, bad wave amplitude
value, and some STM32F4 periph library bugs caused triangle wave
generation to be enable resulting in a low level tone on the
SM1000, that we thought was caused by analog issues like layour
or power supply biasing
*/
DAC_StructInit(&DAC_InitStructure);
DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO;
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
/* DMA1_Stream5 channel7 configuration **************************************/
/* Table 35 page 219 of the monster data sheet */
DMA_DeInit(DMA1_Stream5);
DMA_InitStructure.DMA_Channel = DMA_Channel_7;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)DAC_DHR12R1_ADDRESS;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)dac1_buf;
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStructure.DMA_BufferSize = DAC_BUF_SZ;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA1_Stream5, &DMA_InitStructure);
/* Enable DMA Half & Complete interrupts */
DMA_ITConfig(DMA1_Stream5, DMA_IT_TC | DMA_IT_HT, ENABLE);
/* Enable the DMA Stream IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Enable DMA1_Stream5 */
DMA_Cmd(DMA1_Stream5, ENABLE);
/* Enable DAC Channel 1 */
DAC_Cmd(DAC_Channel_1, ENABLE);
/* Enable DMA for DAC Channel 1 */
DAC_DMACmd(DAC_Channel_1, ENABLE);
}
static void dac2_config(void)
{
DAC_InitTypeDef DAC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* DAC channel 2 Configuration (see notes in dac1_config() above) */
DAC_StructInit(&DAC_InitStructure);
DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO;
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
DAC_Init(DAC_Channel_2, &DAC_InitStructure);
/* DMA1_Stream6 channel7 configuration **************************************/
DMA_DeInit(DMA1_Stream6);
DMA_InitStructure.DMA_Channel = DMA_Channel_7;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)DAC_DHR12R2_ADDRESS;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)dac2_buf;
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStructure.DMA_BufferSize = DAC_BUF_SZ;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA1_Stream6, &DMA_InitStructure);
/* Enable DMA Half & Complete interrupts */
DMA_ITConfig(DMA1_Stream6, DMA_IT_TC | DMA_IT_HT, ENABLE);
/* Enable the DMA Stream IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream6_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Enable DMA1_Stream6 */
DMA_Cmd(DMA1_Stream6, ENABLE);
/* Enable DAC Channel 2 */
DAC_Cmd(DAC_Channel_2, ENABLE);
/* Enable DMA for DAC Channel 2 */
DAC_DMACmd(DAC_Channel_2, ENABLE);
}
/******************************************************************************/
/* STM32F4xx Peripherals Interrupt Handlers */
/* Add here the Interrupt Handler for the used peripheral(s) (PPP), for the */
/* available peripheral interrupt handler's name please refer to the startup */
/* file (startup_stm32f40xx.s/startup_stm32f427x.s). */
/******************************************************************************/
/*
This function handles DMA1 Stream 5 interrupt request for DAC1.
*/
void DMA1_Stream5_IRQHandler(void) {
int i, j, sam;
short signed_buf[DAC_BUF_SZ/2];
GPIOE->ODR |= (1 << 1);
/* Transfer half empty interrupt - refill first half */
if(DMA_GetITStatus(DMA1_Stream5, DMA_IT_HTIF5) != RESET) {
/* fill first half from fifo */
if (codec2_fifo_read(dac1_fifo, signed_buf, DAC_BUF_SZ/2) == -1) {
memset(signed_buf, 0, sizeof(short)*DAC_BUF_SZ/2);
dac_underflow++;
}
/* convert to unsigned */
for(i=0; i<DAC_BUF_SZ/2; i++) {
sam = (int)(M*(float)signed_buf[i] + C);
dac1_buf[i] = (unsigned short)sam;
}
/* Clear DMA Stream Transfer Complete interrupt pending bit */
DMA_ClearITPendingBit(DMA1_Stream5, DMA_IT_HTIF5);
}
/* Transfer complete interrupt - refill 2nd half */
if(DMA_GetITStatus(DMA1_Stream5, DMA_IT_TCIF5) != RESET) {
/* fill second half from fifo */
if (codec2_fifo_read(dac1_fifo, signed_buf, DAC_BUF_SZ/2) == -1) {
memset(signed_buf, 0, sizeof(short)*DAC_BUF_SZ/2);
dac_underflow++;
}
/* convert to unsigned */
for(i=0, j=DAC_BUF_SZ/2; i<DAC_BUF_SZ/2; i++,j++) {
sam = (int)(M*(float)signed_buf[i] + C);
dac1_buf[j] = (unsigned short)sam;
}
/* Clear DMA Stream Transfer Complete interrupt pending bit */
DMA_ClearITPendingBit(DMA1_Stream5, DMA_IT_TCIF5);
}
GPIOE->ODR &= ~(1 << 1);
}
/*
This function handles DMA1 Stream 6 interrupt request for DAC2.
*/
void DMA1_Stream6_IRQHandler(void) {
int i, j, sam;
short signed_buf[DAC_BUF_SZ/2];
GPIOE->ODR |= (1 << 2);
/* Transfer half empty interrupt - refill first half */
if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_HTIF6) != RESET) {
/* fill first half from fifo */
if (codec2_fifo_read(dac2_fifo, signed_buf, DAC_BUF_SZ/2) == -1) {
memset(signed_buf, 0, sizeof(short)*DAC_BUF_SZ/2);
dac_underflow++;
}
/* convert to unsigned */
for(i=0; i<DAC_BUF_SZ/2; i++) {
sam = (int)(M*(float)signed_buf[i] + C);
dac2_buf[i] = (unsigned short)sam;
}
/* Clear DMA Stream Transfer Complete interrupt pending bit */
DMA_ClearITPendingBit(DMA1_Stream6, DMA_IT_HTIF6);
}
/* Transfer complete interrupt - refill 2nd half */
if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_TCIF6) != RESET) {
/* fill second half from fifo */
if (codec2_fifo_read(dac2_fifo, signed_buf, DAC_BUF_SZ/2) == -1) {
memset(signed_buf, 0, sizeof(short)*DAC_BUF_SZ/2);
dac_underflow++;
}
/* convert to unsigned */
for(i=0, j=DAC_BUF_SZ/2; i<DAC_BUF_SZ/2; i++,j++) {
sam = (int)(M*(float)signed_buf[i] + C);
dac2_buf[j] = (unsigned short)sam;
}
/* Clear DMA Stream Transfer Complete interrupt pending bit */
DMA_ClearITPendingBit(DMA1_Stream6, DMA_IT_TCIF6);
}
GPIOE->ODR &= ~(1 << 2);
}
+92
View File
@@ -0,0 +1,92 @@
/*---------------------------------------------------------------------------*\
FILE........: stm32f4_machdep.c
AUTHOR......: David Rowe
DATE CREATED: May 2 2013
STM32F4 implementation of the machine dependent timer functions,
e.g. profiling using a clock cycle counter..
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2013 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. This program is
distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include "machdep.h"
#ifdef SEMIHOST_USE_STDIO
#include "stdio.h"
#else
#include "gdb_stdio.h"
#define printf gdb_stdio_printf
#endif
volatile unsigned int *DWT_CYCCNT = (volatile unsigned int *)0xE0001004;
volatile unsigned int *DWT_CONTROL = (volatile unsigned int *)0xE0001000;
volatile unsigned int *SCB_DEMCR = (volatile unsigned int *)0xE000EDFC;
#define CORE_CLOCK 168E6
#define BUF_SZ 4096
static char buf[BUF_SZ];
void machdep_profile_init(void)
{
static int enabled = 0;
if (!enabled) {
*SCB_DEMCR = *SCB_DEMCR | 0x01000000;
*DWT_CYCCNT = 0; // reset the counter
*DWT_CONTROL = *DWT_CONTROL | 1 ; // enable the counter
enabled = 1;
}
*buf = 0;
}
void machdep_profile_reset(void)
{
*DWT_CYCCNT = 0; // reset the counter
}
unsigned int machdep_profile_sample(void) {
return *DWT_CYCCNT;
}
/* log to a buffer, we only call printf after timing finished as it is slow */
unsigned int machdep_profile_sample_and_log(unsigned int start, char s[])
{
char tmp[80];
float msec;
unsigned int dwt = *DWT_CYCCNT - start;
msec = 1000.0*(float)dwt/CORE_CLOCK;
snprintf(tmp, sizeof(tmp), "%s %5.2f msecs\n",s,(double)msec);
if ((strlen(buf) + strlen(tmp)) < BUF_SZ)
strncat(buf, tmp, sizeof(buf)-1);
return *DWT_CYCCNT;
}
void machdep_profile_print_logged_samples(void)
{
printf("%s", buf);
*buf = 0;
}
+71
View File
@@ -0,0 +1,71 @@
/*
stm32f4_usart.c
David Rowe May 2019
Basic USART tty support for the stm32.
From:
http://stm32projectconsulting.blogspot.com/2013/04/stm32f4-discovery-usart-example.html
*/
#include <stm32f4xx.h>
#include <stm32f4xx_usart.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "stm32f4_usart.h"
#define MAX_FMT_SIZE 256
void usart_init(void){
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* enable peripheral clock for USART3 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
/* GPIOB clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
/* GPIOA Configuration: USART3 TX on PB10 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Connect USART3 pins to AF2 */
// TX = PB10
GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART3);
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx;
USART_Init(USART3, &USART_InitStructure);
USART_Cmd(USART3, ENABLE); // enable USART3
}
void usart_puts(const char s[]) {
for (int i=0; i<strlen(s); i++) {
USART_SendData(USART3, s[i]);
while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET);
}
}
int usart_printf(const char *fmt, ...)
{
char s[MAX_FMT_SIZE];
va_list ap;
va_start(ap, fmt);
vsnprintf(s, MAX_FMT_SIZE, fmt, ap);
va_end(ap);
usart_puts(s);
return 1;
}
+90
View File
@@ -0,0 +1,90 @@
/*---------------------------------------------------------------------------*\
FILE........: stm32f4_usb_vcp.c
AUTHOR......: xenovacivus
DATE CREATED: 3 Sep 2014
USB Virtual COM Port (VCP) module adapted from code I found here:
https://github.com/xenovacivus/STM32DiscoveryVCP
\*---------------------------------------------------------------------------*/
#include "stm32f4xx_conf.h"
#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_exti.h"
#include "usbd_cdc_core.h"
#include "usbd_usr.h"
#include "usbd_desc.h"
#include "usbd_cdc_vcp.h"
#include "usb_dcd_int.h"
#include "sm1000_leds_switches.h"
#include "stm32f4_usb_vcp.h"
/*
* The USB data must be 4 byte aligned if DMA is enabled. This macro handles
* the alignment, if necessary (it's actually magic, but don't tell anyone).
*/
__ALIGN_BEGIN USB_OTG_CORE_HANDLE USB_OTG_dev __ALIGN_END;
/*
* Define prototypes for interrupt handlers here. The conditional "extern"
* ensures the weak declarations from startup_stm32f4xx.c are overridden.
*/
#ifdef __cplusplus
extern "C" {
#endif
void NMI_Handler(void);
void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void SVC_Handler(void);
void DebugMon_Handler(void);
void PendSV_Handler(void);
void OTG_FS_IRQHandler(void);
void OTG_FS_WKUP_IRQHandler(void);
#ifdef __cplusplus
}
#endif
void usb_vcp_init() {
/* Setup USB */
USBD_Init(&USB_OTG_dev,
USB_OTG_FS_CORE_ID,
&USR_desc,
&USBD_CDC_cb,
&USR_cb);
}
/*
* Interrupt Handlers
*/
void NMI_Handler(void) {}
void SVC_Handler(void) {}
void DebugMon_Handler(void) {}
void PendSV_Handler(void) {}
void OTG_FS_IRQHandler(void)
{
USBD_OTG_ISR_Handler (&USB_OTG_dev);
}
void OTG_FS_WKUP_IRQHandler(void)
{
if(USB_OTG_dev.cfg.low_power)
{
*(uint32_t *)(0xE000ED10) &= 0xFFFFFFF9 ;
SystemInit();
USB_OTG_UngateClock(&USB_OTG_dev);
}
EXTI_ClearITPendingBit(EXTI_Line18);
}
+724
View File
@@ -0,0 +1,724 @@
/*!
* STM32F4 Virtual EEPROM driver
*
* This module implements a crude virtual EEPROM device stored in on-board
* flash. The STM32F405 has 4 16kB flash sectors starting at address
* 0x80000000, followed by a 64kB sector, then 128kB sectors.
*
* The Cortex M4 core maps these all to address 0x00000000 when booting
* from normal flash, so the first sector is reserved for interrupt
* vectors.
*
* Everything else however is free game, and so we use these smaller
* sectors to store our configuration.
*
* Author Stuart Longland <me@vk4msl.id.au>
* Copyright (C) 2015 FreeDV project.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation. This program is
* distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see
* <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#include "stm32f4_vrom.h"
#include "stm32f4xx_flash.h"
#include "stm32f4xx_crc.h"
#define VROM_SECT_SZ (16384) /*!< Size of a flash sector */
#define VROM_SECT_CNT (3) /*!< Number of sectors */
#define VROM_BLOCK_SZ (256) /*!< Size of a flash block */
/*!
* Starting address for the flash area
*/
#define VROM_START_ADDR (0x08004000)
/*!
* Number of blocks we can fit per sector, including the index block.
*/
#define VROM_BLOCK_CNT (VROM_SECT_SZ / VROM_BLOCK_SZ)
/*!
* Number of application blocks we can fit per sector.
*/
#define VROM_SECT_APP_BLOCK_CNT (VROM_BLOCK_CNT - 1)
/*!
* Total number of application blocks we can fit in flash.
*/
#define VROM_APP_BLOCK_CNT (VROM_SECT_CNT * VROM_SECT_APP_BLOCK_CNT)
/*!
* Maximum number of erase cycles per sector.
* Table 42 (page 109) of STM32F405 datasheet (DocID022152 Rev 5).
*/
#define VROM_MAX_CYCLES (10000)
/*!
* EEPROM block header.
*/
struct __attribute__ ((__packed__)) vrom_block_hdr_t {
/*!
* CRC32 checksum of the data, offset, size and ROM ID.
* A CRC32 of 0x00000000 indicates an obsoleted block.
* A CRC32 of 0xffffffff indicates an erased block.
*/
uint32_t crc32;
/*!
* ROM ID.
*/
uint8_t rom;
/*!
* Block number in the virtual EEPROM.
*/
uint8_t idx;
/*!
* Number of bytes from the virtual EEPROM stored in this block.
*/
uint8_t size;
/*!
* Reserved for future use.
*/
uint8_t reserved;
};
/*!
* The size of a block header in bytes.
*/
#define VROM_BLOCK_HDR_SZ (sizeof(struct vrom_block_hdr_t))
/*!
* The amount of data available for application use.
*/
#define VROM_DATA_SZ (VROM_BLOCK_SZ - VROM_BLOCK_HDR_SZ)
/*!
* EEPROM data block.
*/
struct __attribute__ ((__packed__)) vrom_data_block_t {
/*! Block header */
struct vrom_block_hdr_t header;
/*! Block data */
uint8_t data[VROM_DATA_SZ];
};
/*!
* The first block in a sector is the sector index block. This indicates
* the used/free state of the entire block and counts the number of
* erase cycles for the sector. The index block has no header.
*/
struct __attribute__ ((__packed__)) vrom_sector_idx_t {
/*!
* Number of erase cycles remaining for the sector.
* 0xffffffff == unprogrammed.
*/
uint32_t cycles_remain;
/*!
* Block metadata flags. One for each data block in the sector.
* Does not include the index block.
*/
uint16_t flags[VROM_SECT_APP_BLOCK_CNT];
};
#define VROM_SFLAGS_USED (1 << 0) /*!< Block in use */
/*!
* Return the address of a virtual EEPROM sector header.
*/
static const struct vrom_sector_idx_t* vrom_get_sector_hdr(uint8_t sector)
{
return (const struct vrom_sector_idx_t*)(
VROM_START_ADDR + (VROM_SECT_SZ * sector));
}
/*!
* Return the address of a virtual EEPROM block.
*/
static const struct vrom_data_block_t* vrom_get_block(
uint8_t sector, uint8_t block)
{
return (const struct vrom_data_block_t*)(
(void*)vrom_get_sector_hdr(sector)
+ (VROM_BLOCK_SZ * (block + 1)));
}
/*!
* Compute the CRC32 of a block.
*/
static uint32_t vrom_crc32(
const struct vrom_data_block_t* const block)
{
struct vrom_data_block_t temp_block;
uint32_t size = sizeof(temp_block);
const uint8_t* in = (const uint8_t*)(&temp_block);
uint32_t tmp;
uint32_t crc;
memcpy(&temp_block, block, sizeof(temp_block));
temp_block.header.crc32 = 0;
CRC_ResetDR();
while(size) {
tmp = 0;
if (size) {
tmp |= (uint32_t)(*(in++)) << 24;
size--;
}
if (size) {
tmp |= (uint32_t)(*(in++)) << 16;
size--;
}
if (size) {
tmp |= (uint32_t)(*(in++)) << 8;
size--;
}
if (size) {
tmp |= (uint32_t)(*(in++));
size--;
}
crc = CRC_CalcCRC(tmp);
}
return crc;
}
/*!
* Find the block storing the given index.
*/
static const struct vrom_data_block_t* vrom_find(uint8_t rom, uint8_t idx)
{
int sector, block;
for (sector = 0; sector < VROM_SECT_CNT; sector++) {
const struct vrom_sector_idx_t* sect_hdr
= vrom_get_sector_hdr(sector);
if (sect_hdr->cycles_remain == UINT32_MAX)
/* unformatted */
continue;
for (block = 0; block < VROM_SECT_APP_BLOCK_CNT; block++) {
const struct vrom_data_block_t* block_ptr;
if (sect_hdr->flags[block] == UINT16_MAX)
/* unformatted */
continue;
if (sect_hdr->flags[block] == 0)
/* obsolete */
continue;
block_ptr = vrom_get_block(sector, block);
/* Verify the content */
if (vrom_crc32(block_ptr)
!= block_ptr->header.crc32)
/* corrupt */
continue;
if (block_ptr->header.rom != rom)
/* different ROM */
continue;
if (block_ptr->header.idx != idx)
/* wrong index */
continue;
return block_ptr;
}
}
return NULL;
}
/*!
* Get the sector number of a given address.
*/
static uint8_t vrom_sector_num(const void* address)
{
/* Get the offset from the base address */
uint32_t offset = (uint32_t)address - VROM_START_ADDR;
return offset / VROM_SECT_SZ;
}
/*!
* Get the block number of a given address.
*/
static uint8_t vrom_block_num(const void* address)
{
/* Get the sector number */
uint8_t sector = vrom_sector_num(address);
/* Get the offset from the sector base */
uint32_t offset = (uint32_t)(address
- (const void*)vrom_get_sector_hdr(sector));
offset /= VROM_BLOCK_SZ;
return offset - 1;
}
/*!
* (Erase and) Format a sector.
*
* @retval -EIO Erase failed
* @retval -EPERM Erase counter depleted.
*/
static int vrom_format_sector(const struct vrom_sector_idx_t* sector)
{
uint8_t sector_num = vrom_sector_num(sector);
uint32_t cycles_remain = VROM_MAX_CYCLES;
if (sector->cycles_remain != UINT32_MAX) {
if (sector->cycles_remain == 0)
/* This sector is exhausted */
return -EPERM;
/* This sector has been formatted before */
cycles_remain = sector->cycles_remain - 1;
if (FLASH_EraseSector(sector_num + 1, VoltageRange_3))
/* Erase failed */
return -EIO;
}
/* Program the new sector cycle counter */
if (FLASH_ProgramWord((uint32_t)sector,
cycles_remain) == FLASH_COMPLETE)
return 0; /* All good */
/* If we get here, then programming failed */
return -EIO;
}
/*!
* Find the next available block.
*/
static const struct vrom_data_block_t* vrom_find_free(uint8_t run_gc)
{
int sector;
if (run_gc) {
for (sector = 0; sector < VROM_SECT_CNT; sector++) {
uint8_t block;
uint8_t used = 0;
const struct vrom_sector_idx_t* sect_hdr
= vrom_get_sector_hdr(sector);
if (sect_hdr->cycles_remain == UINT32_MAX)
/* Already erased */
continue;
if (sect_hdr->cycles_remain == 0)
/* Depleted */
continue;
for (block = 0; block < VROM_SECT_APP_BLOCK_CNT;
block++) {
if (sect_hdr->flags[block]) {
used = 1;
break;
}
}
if (!used) {
/* We can format this */
vrom_format_sector(sect_hdr);
}
}
}
for (sector = 0; sector < VROM_SECT_CNT; sector++) {
uint8_t block;
const struct vrom_sector_idx_t* sect_hdr
= vrom_get_sector_hdr(sector);
if (sect_hdr->cycles_remain == UINT32_MAX) {
/* Unformatted sector. */
if (vrom_format_sector(sect_hdr))
/* Couldn't format, keep looking */
continue;
}
for (block = 0; block < VROM_SECT_APP_BLOCK_CNT; block++) {
if (sect_hdr->flags[block] == UINT16_MAX)
/* Success */
return vrom_get_block(sector, block);
}
}
/* No blocks free, but have we done garbage collection? */
if (!run_gc)
return vrom_find_free(1);
/* If we get here, then we weren't able to find a free block */
return NULL;
}
/*!
* Set flags for a block
*/
static int vrom_set_flags(const struct vrom_data_block_t* block,
uint16_t flags)
{
const struct vrom_sector_idx_t* sector =
vrom_get_sector_hdr(vrom_sector_num(block));
uint8_t block_num = vrom_block_num(block);
/* Compute the new flags settings */
flags = sector->flags[block_num] & ~flags;
/* Write them */
if (FLASH_ProgramHalfWord(
(uint32_t)(&(sector->flags[block_num])),
flags) != FLASH_COMPLETE)
return -EIO;
return 0;
}
/*!
* Mark a block as being obsolete
*/
static int vrom_mark_obsolete(const struct vrom_data_block_t* block)
{
/* Blank out the CRC */
if (FLASH_ProgramWord((uint32_t)(&(block->header.crc32)), 0)
!= FLASH_COMPLETE)
return -EIO;
/* Blank out the ROM ID */
if (FLASH_ProgramByte((uint32_t)(&(block->header.rom)), 0)
!= FLASH_COMPLETE)
return -EIO;
/* Blank out the index */
if (FLASH_ProgramByte((uint32_t)(&(block->header.idx)), 0)
!= FLASH_COMPLETE)
return -EIO;
/* Blank out the size */
if (FLASH_ProgramByte((uint32_t)&(block->header.size), 0)
!= FLASH_COMPLETE)
return -EIO;
/* Blank out the reserved byte */
if (FLASH_ProgramByte((uint32_t)&(block->header.reserved), 0)
!= FLASH_COMPLETE)
return -EIO;
/* Blank out the flags */
return vrom_set_flags(block, -1);
}
/*!
* Write a new block.
*/
static int vrom_write_block(uint8_t rom, uint8_t idx, uint8_t size,
const uint8_t* in)
{
/* Find a new home for the block */
const struct vrom_data_block_t* block = vrom_find_free(0);
struct vrom_data_block_t new_block;
uint8_t* out = (uint8_t*)(block);
uint32_t rem = sizeof(new_block);
int res;
if (!block)
return -ENOSPC;
/* Prepare the new block */
memset(&new_block, 0xff, sizeof(new_block));
new_block.header.rom = rom;
new_block.header.idx = idx;
new_block.header.size = size;
memcpy(new_block.data, in, size);
new_block.header.crc32 = vrom_crc32(&new_block);
/* Start writing out the block */
in = (uint8_t*)(&new_block);
rem = VROM_BLOCK_SZ;
while(rem) {
if (*out != *in) {
if (FLASH_ProgramByte((uint32_t)out, *in)
!= FLASH_COMPLETE)
/* Failed! */
return -EIO;
}
in++;
out++;
rem--;
}
res = vrom_set_flags(block, VROM_SFLAGS_USED);
if (res < 0)
return res;
return size;
}
/*!
* Re-write the given block if needed.
*/
static int vrom_rewrite_block(const struct vrom_data_block_t* block,
uint8_t size, const uint8_t* in)
{
uint8_t obsolete = 0;
uint8_t rom = block->header.rom;
uint8_t idx = block->header.idx;
const uint8_t* cmp_block = block->data;
const uint8_t* cmp_in = in;
uint8_t cmp_sz = size;
int res;
while(cmp_sz) {
if (*cmp_block != *cmp_in) {
obsolete = 1;
break;
}
cmp_sz--;
cmp_block++;
cmp_in++;
}
if (!obsolete)
/* The block is fine, leave it be. */
return size;
/* Mark the block as obsolete */
res = vrom_mark_obsolete(block);
if (res)
return res;
return vrom_write_block(rom, idx, size, in);
}
/*!
* Overwrite the start of a block.
*/
static int vrom_overwrite_block(
const struct vrom_data_block_t* block,
uint8_t offset, uint8_t size, const uint8_t* in)
{
uint8_t data[VROM_DATA_SZ];
uint16_t block_sz = block->header.size;
int res;
if (!offset && (size >= block->header.size))
/* Complete overwrite */
return vrom_rewrite_block(block, size, in);
if (offset) {
/* Overwrite end of block, possible expansion */
block_sz = offset + size;
if (block_sz > VROM_DATA_SZ)
block_sz = VROM_DATA_SZ;
memcpy(data, block->data, offset);
memcpy(&data[offset], in, block_sz - offset);
} else {
/* Overwrite start of block, no size change */
memcpy(data, in, size);
memcpy(&data[size], &(block->data[size]),
block_sz - size);
}
res = vrom_rewrite_block(block, block_sz, data);
if (res < 0)
return res;
return block_sz;
}
/*!
* Write data to the virtual EEPROM.
*/
static int vrom_write_internal(uint8_t rom,
uint16_t offset, uint16_t size, const uint8_t* in)
{
/* Figure out our starting block and offset */
uint8_t block_idx = offset / VROM_DATA_SZ;
uint8_t block_offset = offset % VROM_DATA_SZ;
int count = 0;
/* Locate the first block */
const struct vrom_data_block_t* block = vrom_find(rom, block_idx);
uint8_t block_sz = VROM_DATA_SZ;
if (block_sz > (size + block_offset))
block_sz = size + block_offset;
if (!block) {
/* Create a new block */
uint8_t data[VROM_DATA_SZ];
int res;
memset(data, 0xff, sizeof(data));
memcpy(&data[block_offset], in,
block_sz-block_offset);
res = vrom_write_block(rom, block_idx, block_sz, data);
if (res < 0)
return res;
} else {
/* Overwrite block */
int res = vrom_overwrite_block(block, block_offset,
block_sz, in);
if (res < 0)
return res;
count += block_sz;
}
block_idx++;
size -= block_sz - block_offset;
while(size) {
/* Work out how much data to write */
if (size < VROM_DATA_SZ)
block_sz = size;
else
block_sz = VROM_DATA_SZ;
int res;
/* Is there a block covering this range? */
block = vrom_find(rom, block_idx);
if (block)
res = vrom_overwrite_block(
block, 0, block_sz, in);
else
res = vrom_write_block(rom, block_idx,
block_sz, in);
if (res < 0)
return res;
/* Successful write */
count += res;
size -= res;
in += res;
offset += res;
}
return count;
}
/*!
* Read data from a virtual EEPROM.
* @param rom ROM ID to start reading.
* @param offset Address offset into ROM to start reading.
* @param size Number of bytes to read from ROM.
* @param out Buffer to write ROM content to.
* @returns Number of bytes read from ROM.
* @retval -ENXIO ROM not found
* @retval -ESPIPE Offset past end of ROM.
*/
int vrom_read(uint8_t rom, uint16_t offset, uint16_t size, void* out)
{
/* Figure out our starting block and offset */
uint8_t block_idx = offset / VROM_DATA_SZ;
uint8_t block_offset = offset % VROM_DATA_SZ;
uint8_t block_sz;
int count = 0;
uint8_t* out_ptr = (uint8_t*)out;
/* Locate the first block */
const struct vrom_data_block_t* block = vrom_find(rom, block_idx);
if (!block)
return -ENXIO;
if (block_offset >= block->header.size)
return -ESPIPE;
/* Copy the initial bytes */
block_sz = block->header.size - block_offset;
if (block_sz > size)
block_sz = size;
memcpy(out_ptr, &(block->data[block_offset]), block_sz);
out_ptr += block_sz;
size -= block_sz;
count += block_sz;
if (size) {
/* Look for the next block */
block = vrom_find(rom, ++block_idx);
while(size && block) {
if (block->header.size <= size)
block_sz = block->header.size;
else
block_sz = size;
memcpy(out_ptr, block->data, block_sz);
out_ptr += block_sz;
size -= block_sz;
count += block_sz;
block = vrom_find(rom, ++block_idx);
}
}
return count;
}
/*!
* Write data to a virtual EEPROM.
* @param rom ROM ID to start writing.
* @param offset Address offset into ROM to start writing.
* @param size Number of bytes to write to ROM.
* @param in Buffer to write ROM content from.
* @returns Number of bytes written to ROM.
* @retval -EIO Programming failed
* @retval -ENOSPC No free blocks available
*/
int vrom_write(uint8_t rom, uint16_t offset, uint16_t size,
const void* in)
{
int res;
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_EOP
| FLASH_FLAG_OPERR
| FLASH_FLAG_WRPERR
| FLASH_FLAG_PGAERR
| FLASH_FLAG_PGPERR
| FLASH_FLAG_PGSERR);
res = vrom_write_internal(rom, offset, size, in);
FLASH_Lock();
return res;
}
/*!
* Erase a virtual EEPROM.
* @param rom ROM ID to erase.
* @returns Number of bytes written to ROM.
* @retval -EIO Programming failed
* @retval -ENOSPC No free blocks available
*/
int vrom_erase(uint8_t rom)
{
int sector, block;
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_EOP
| FLASH_FLAG_OPERR
| FLASH_FLAG_WRPERR
| FLASH_FLAG_PGAERR
| FLASH_FLAG_PGPERR
| FLASH_FLAG_PGSERR);
for (sector = 0; sector < VROM_SECT_CNT; sector++) {
const struct vrom_sector_idx_t* sect_hdr
= vrom_get_sector_hdr(sector);
if (sect_hdr->cycles_remain == UINT32_MAX)
/* unformatted */
continue;
for (block = 0; block < VROM_SECT_APP_BLOCK_CNT; block++) {
int res;
const struct vrom_data_block_t* block_ptr;
if (sect_hdr->flags[block] == UINT16_MAX)
/* unformatted */
continue;
if (sect_hdr->flags[block] == 0)
/* obsolete */
continue;
block_ptr = vrom_get_block(sector, block);
/* Verify the content */
if (vrom_crc32(block_ptr)
!= block_ptr->header.crc32)
/* corrupt */
continue;
if (block_ptr->header.rom != rom)
/* different ROM */
continue;
/*
* Block is valid, for the correct ROM. Mark it
* obsolete.
*/
res = vrom_mark_obsolete(block_ptr);
if (res)
return res;
}
}
return 0;
}
+585
View File
@@ -0,0 +1,585 @@
/**
******************************************************************************
* @file system_stm32f4xx.c
* @author MCD Application Team
* @version V1.0.1
* @date 10-July-2012
* @brief CMSIS Cortex-M4 Device Peripheral Access Layer System Source File.
* This file contains the system clock configuration for STM32F4xx devices,
* and is generated by the clock configuration tool
* stm32f4xx_Clock_Configuration_V1.0.1.xls
*
* 1. This file provides two functions and one global variable to be called from
* user application:
* - SystemInit(): Setups the system clock (System clock source, PLL Multiplier
* and Divider factors, AHB/APBx prescalers and Flash settings),
* depending on the configuration made in the clock xls tool.
* This function is called at startup just after reset and
* before branch to main program. This call is made inside
* the "startup_stm32f4xx.s" file.
*
* - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
* by the user application to setup the SysTick
* timer or configure other parameters.
*
* - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
* be called whenever the core clock is changed
* during program execution.
*
* 2. After each device reset the HSI (16 MHz) is used as system clock source.
* Then SystemInit() function is called, in "startup_stm32f4xx.s" file, to
* configure the system clock before to branch to main program.
*
* 3. If the system clock source selected by user fails to startup, the SystemInit()
* function will do nothing and HSI still used as system clock source. User can
* add some code to deal with this issue inside the SetSysClock() function.
*
* 4. The default value of HSE crystal is set to 25MHz, refer to "HSE_VALUE" define
* in "stm32f4xx.h" file. When HSE is used as system clock source, directly or
* through PLL, and you are using different crystal you have to adapt the HSE
* value to your own configuration.
*
* 5. This file configures the system clock as follows:
*=============================================================================
*=============================================================================
* Supported STM32F4xx device revision | Rev A
*-----------------------------------------------------------------------------
* System Clock source | PLL (HSE)
*-----------------------------------------------------------------------------
* SYSCLK(Hz) | 168000000
*-----------------------------------------------------------------------------
* HCLK(Hz) | 168000000
*-----------------------------------------------------------------------------
* AHB Prescaler | 1
*-----------------------------------------------------------------------------
* APB1 Prescaler | 4
*-----------------------------------------------------------------------------
* APB2 Prescaler | 2
*-----------------------------------------------------------------------------
* HSE Frequency(Hz) | 8000000
*-----------------------------------------------------------------------------
* PLL_M | 8
*-----------------------------------------------------------------------------
* PLL_N | 336
*-----------------------------------------------------------------------------
* PLL_P | 2
*-----------------------------------------------------------------------------
* PLL_Q | 7
*-----------------------------------------------------------------------------
* PLLI2S_N | 352
*-----------------------------------------------------------------------------
* PLLI2S_R | 2
*-----------------------------------------------------------------------------
* I2S input clock(Hz) | 176000000
* |
* To achieve the following I2S config: |
* - Master clock output (MCKO): OFF |
* - Frame wide : 16bit |
* - Error % : 0,0000 |
* - Prescaler Odd factor (ODD): 1 |
* - Linear prescaler (DIV) : 14 |
*-----------------------------------------------------------------------------
* VDD(V) | 3,3
*-----------------------------------------------------------------------------
* Main regulator output voltage | Scale1 mode
*-----------------------------------------------------------------------------
* Flash Latency(WS) | 5
*-----------------------------------------------------------------------------
* Prefetch Buffer | OFF
*-----------------------------------------------------------------------------
* Instruction cache | ON
*-----------------------------------------------------------------------------
* Data cache | ON
*-----------------------------------------------------------------------------
* Require 48MHz for USB OTG FS, | Enabled
* SDIO and RNG clock |
*-----------------------------------------------------------------------------
*=============================================================================
******************************************************************************
* @attention
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
* <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
******************************************************************************
*/
/** @addtogroup CMSIS
* @{
*/
/** @addtogroup stm32f4xx_system
* @{
*/
/** @addtogroup STM32F4xx_System_Private_Includes
* @{
*/
#include "stm32f4xx.h"
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_TypesDefinitions
* @{
*/
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_Defines
* @{
*/
/************************* Miscellaneous Configuration ************************/
/*!< Uncomment the following line if you need to use external SRAM mounted
on STM324xG_EVAL board as data memory */
/* #define DATA_IN_ExtSRAM */
/*!< Uncomment the following line if you need to relocate your vector Table in
Internal SRAM. */
/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET 0x00 /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
/******************************************************************************/
/************************* PLL Parameters *************************************/
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
#define PLL_M 8
#define PLL_N 336
/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P 2
/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */
#define PLL_Q 7
/* PLLI2S_VCO = (HSE_VALUE Or HSI_VALUE / PLL_M) * PLLI2S_N
I2SCLK = PLLI2S_VCO / PLLI2S_R */
#define START_I2SCLOCK 0
#define PLLI2S_N 352
#define PLLI2S_R 2
/******************************************************************************/
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_Macros
* @{
*/
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_Variables
* @{
*/
uint32_t SystemCoreClock = 168000000;
__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_FunctionPrototypes
* @{
*/
static void SetSysClock(void);
#ifdef DATA_IN_ExtSRAM
static void SystemInit_ExtMemCtl(void);
#endif /* DATA_IN_ExtSRAM */
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_Functions
* @{
*/
/**
* @brief Setup the microcontroller system
* Initialize the Embedded Flash Interface, the PLL and update the
* SystemFrequency variable.
* @param None
* @retval None
*/
void SystemInit(void)
{
/* FPU settings ------------------------------------------------------------*/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
/* Reset the RCC clock configuration to the default reset state ------------*/
/* Set HSION bit */
RCC->CR |= (uint32_t)0x00000001;
/* Reset CFGR register */
RCC->CFGR = 0x00000000;
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= (uint32_t)0xFEF6FFFF;
/* Reset PLLCFGR register */
RCC->PLLCFGR = 0x24003010;
/* Reset HSEBYP bit */
RCC->CR &= (uint32_t)0xFFFBFFFF;
/* Disable all interrupts */
RCC->CIR = 0x00000000;
#ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM */
/* Configure the System clock source, PLL Multiplier and Divider factors,
AHB/APBx prescalers and Flash settings ----------------------------------*/
SetSysClock();
/* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}
/**
* @brief Update SystemCoreClock variable according to Clock Register Values.
* The SystemCoreClock variable contains the core clock (HCLK), it can
* be used by the user application to setup the SysTick timer or configure
* other parameters.
*
* @note Each time the core clock (HCLK) changes, this function must be called
* to update SystemCoreClock variable value. Otherwise, any configuration
* based on this variable will be incorrect.
*
* @note - The system frequency computed by this function is not the real
* frequency in the chip. It is calculated based on the predefined
* constant and the selected clock source:
*
* - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
*
* - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
*
* - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**)
* or HSI_VALUE(*) multiplied/divided by the PLL factors.
*
* (*) HSI_VALUE is a constant defined in stm32f4xx.h file (default value
* 16 MHz) but the real value may vary depending on the variations
* in voltage and temperature.
*
* (**) HSE_VALUE is a constant defined in stm32f4xx.h file (default value
* 25 MHz), user has to ensure that HSE_VALUE is same as the real
* frequency of the crystal used. Otherwise, this function may
* have wrong result.
*
* - The result of this function could be not correct when using fractional
* value for HSE crystal.
*
* @param None
* @retval None
*/
void SystemCoreClockUpdate(void)
{
uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2;
/* Get SYSCLK source -------------------------------------------------------*/
tmp = RCC->CFGR & RCC_CFGR_SWS;
switch (tmp)
{
case 0x00: /* HSI used as system clock source */
SystemCoreClock = HSI_VALUE;
break;
case 0x04: /* HSE used as system clock source */
SystemCoreClock = HSE_VALUE;
break;
case 0x08: /* PLL used as system clock source */
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
SYSCLK = PLL_VCO / PLL_P
*/
pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22;
pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
if (pllsource != 0)
{
/* HSE used as PLL clock source */
pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
}
else
{
/* HSI used as PLL clock source */
pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
}
pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2;
SystemCoreClock = pllvco/pllp;
break;
default:
SystemCoreClock = HSI_VALUE;
break;
}
/* Compute HCLK frequency --------------------------------------------------*/
/* Get HCLK prescaler */
tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
/* HCLK frequency */
SystemCoreClock >>= tmp;
}
/**
* @brief Configures the System clock source, PLL Multiplier and Divider factors,
* AHB/APBx prescalers and Flash settings
* @Note This function should be called only once the RCC clock configuration
* is reset to the default reset state (done in SystemInit() function).
* @param None
* @retval None
*/
static void SetSysClock(void)
{
/******************************************************************************/
/* PLL (clocked by HSE) used as System clock source */
/******************************************************************************/
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* Enable HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* Select regulator voltage output Scale 1 mode, System frequency up to 168 MHz */
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_VOS;
/* HCLK = SYSCLK / 1*/
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK / 2*/
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
/* PCLK1 = HCLK / 4*/
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
/* Configure the main PLL */
RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
(RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
/* Enable the main PLL */
RCC->CR |= RCC_CR_PLLON;
/* Wait till the main PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/* Configure Flash prefetch, Instruction cache, Data cache and wait state */
FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
/* Select the main PLL as system clock source */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_PLL;
/* Wait till the main PLL is used as system clock source */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL)
{
asm("nop");
}
}
else
{ /* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
}
/******************************************************************************/
/* I2S clock configuration */
/******************************************************************************/
#if START_I2SCLOCK
/* PLLI2S clock used as I2S clock source */
RCC->CFGR &= ~RCC_CFGR_I2SSRC;
/* Configure PLLI2S */
RCC->PLLI2SCFGR = (PLLI2S_N << 6) | (PLLI2S_R << 28);
/* Enable PLLI2S */
RCC->CR |= ((uint32_t)RCC_CR_PLLI2SON);
/* Wait till PLLI2S is ready */
while((RCC->CR & RCC_CR_PLLI2SRDY) == 0)
{
}
#endif
}
/**
* @brief Setup the external memory controller. Called in startup_stm32f4xx.s
* before jump to __main
* @param None
* @retval None
*/
#ifdef DATA_IN_ExtSRAM
/**
* @brief Setup the external memory controller.
* Called in startup_stm32f4xx.s before jump to main.
* This function configures the external SRAM mounted on STM324xG_EVAL board
* This SRAM will be used as program data memory (including heap and stack).
* @param None
* @retval None
*/
void SystemInit_ExtMemCtl(void)
{
/*-- GPIOs Configuration -----------------------------------------------------*/
/*
+-------------------+--------------------+------------------+------------------+
+ SRAM pins assignment +
+-------------------+--------------------+------------------+------------------+
| PD0 <-> FSMC_D2 | PE0 <-> FSMC_NBL0 | PF0 <-> FSMC_A0 | PG0 <-> FSMC_A10 |
| PD1 <-> FSMC_D3 | PE1 <-> FSMC_NBL1 | PF1 <-> FSMC_A1 | PG1 <-> FSMC_A11 |
| PD4 <-> FSMC_NOE | PE3 <-> FSMC_A19 | PF2 <-> FSMC_A2 | PG2 <-> FSMC_A12 |
| PD5 <-> FSMC_NWE | PE4 <-> FSMC_A20 | PF3 <-> FSMC_A3 | PG3 <-> FSMC_A13 |
| PD8 <-> FSMC_D13 | PE7 <-> FSMC_D4 | PF4 <-> FSMC_A4 | PG4 <-> FSMC_A14 |
| PD9 <-> FSMC_D14 | PE8 <-> FSMC_D5 | PF5 <-> FSMC_A5 | PG5 <-> FSMC_A15 |
| PD10 <-> FSMC_D15 | PE9 <-> FSMC_D6 | PF12 <-> FSMC_A6 | PG9 <-> FSMC_NE2 |
| PD11 <-> FSMC_A16 | PE10 <-> FSMC_D7 | PF13 <-> FSMC_A7 |------------------+
| PD12 <-> FSMC_A17 | PE11 <-> FSMC_D8 | PF14 <-> FSMC_A8 |
| PD13 <-> FSMC_A18 | PE12 <-> FSMC_D9 | PF15 <-> FSMC_A9 |
| PD14 <-> FSMC_D0 | PE13 <-> FSMC_D10 |------------------+
| PD15 <-> FSMC_D1 | PE14 <-> FSMC_D11 |
| | PE15 <-> FSMC_D12 |
+-------------------+--------------------+
*/
/* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */
RCC->AHB1ENR = 0x00000078;
/* Connect PDx pins to FSMC Alternate function */
GPIOD->AFR[0] = 0x00cc00cc;
GPIOD->AFR[1] = 0xcc0ccccc;
/* Configure PDx pins in Alternate function mode */
GPIOD->MODER = 0xaaaa0a0a;
/* Configure PDx pins speed to 100 MHz */
GPIOD->OSPEEDR = 0xffff0f0f;
/* Configure PDx pins Output type to push-pull */
GPIOD->OTYPER = 0x00000000;
/* No pull-up, pull-down for PDx pins */
GPIOD->PUPDR = 0x00000000;
/* Connect PEx pins to FSMC Alternate function */
GPIOE->AFR[0] = 0xc00cc0cc;
GPIOE->AFR[1] = 0xcccccccc;
/* Configure PEx pins in Alternate function mode */
GPIOE->MODER = 0xaaaa828a;
/* Configure PEx pins speed to 100 MHz */
GPIOE->OSPEEDR = 0xffffc3cf;
/* Configure PEx pins Output type to push-pull */
GPIOE->OTYPER = 0x00000000;
/* No pull-up, pull-down for PEx pins */
GPIOE->PUPDR = 0x00000000;
/* Connect PFx pins to FSMC Alternate function */
GPIOF->AFR[0] = 0x00cccccc;
GPIOF->AFR[1] = 0xcccc0000;
/* Configure PFx pins in Alternate function mode */
GPIOF->MODER = 0xaa000aaa;
/* Configure PFx pins speed to 100 MHz */
GPIOF->OSPEEDR = 0xff000fff;
/* Configure PFx pins Output type to push-pull */
GPIOF->OTYPER = 0x00000000;
/* No pull-up, pull-down for PFx pins */
GPIOF->PUPDR = 0x00000000;
/* Connect PGx pins to FSMC Alternate function */
GPIOG->AFR[0] = 0x00cccccc;
GPIOG->AFR[1] = 0x000000c0;
/* Configure PGx pins in Alternate function mode */
GPIOG->MODER = 0x00080aaa;
/* Configure PGx pins speed to 100 MHz */
GPIOG->OSPEEDR = 0x000c0fff;
/* Configure PGx pins Output type to push-pull */
GPIOG->OTYPER = 0x00000000;
/* No pull-up, pull-down for PGx pins */
GPIOG->PUPDR = 0x00000000;
/*-- FSMC Configuration ------------------------------------------------------*/
/* Enable the FSMC interface clock */
RCC->AHB3ENR = 0x00000001;
/* Configure and enable Bank1_SRAM2 */
FSMC_Bank1->BTCR[2] = 0x00001015;
FSMC_Bank1->BTCR[3] = 0x00010603;
FSMC_Bank1E->BWTR[2] = 0x0fffffff;
/*
Bank1_SRAM2 is configured as follow:
p.FSMC_AddressSetupTime = 3;
p.FSMC_AddressHoldTime = 0;
p.FSMC_DataSetupTime = 6;
p.FSMC_BusTurnAroundDuration = 1;
p.FSMC_CLKDivision = 0;
p.FSMC_DataLatency = 0;
p.FSMC_AccessMode = FSMC_AccessMode_A;
FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM2;
FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_PSRAM;
FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;
FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p;
FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p;
*/
}
#endif /* DATA_IN_ExtSRAM */
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
+151
View File
@@ -0,0 +1,151 @@
/*!
* Fixed-point tone generator.
*
* The code here implements a simple fixed-point tone generator that uses
* integer arithmetic to generate a sinusoid at a fixed sample rate of
* 16kHz.
*
* To set the initial state of the state machine, you specify a frequency
* and duration using tone_reset. The corresponding C file embeds a
* sinusoid look-up table. The total number of samples is computed for
* the given time and used to initialise 'remain', 'time' is initialised
* to 0, and 'step' gives the amount to increment 'time' by each iteration.
*
* The samples are retrieved by repeatedly calling tone_next. This
* advances 'time' and decrements 'remain'. The tone is complete when
* 'remain' is zero.
*
* Author Stuart Longland <me@vk4msl.id.au>
* Copyright (C) 2015 FreeDV project.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation. This program is
* distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see
* <http://www.gnu.org/licenses/>.
*/
#include "tone.h"
/*! Fixed-point shift factor */
#define TONE_SHIFT (12)
/*! Static compiled quarter-sinusoid. */
static const int16_t partial_sine[] = {
830, 2488, 4140, 5781, 7407, 9014, 10598, 12155,
13681, 15171, 16623, 18031, 19394, 20707, 21967, 23170,
24314, 25395, 26411, 27360, 28238, 29043, 29774, 30429,
31006, 31503, 31919, 32253, 32504, 32672, 32756
};
/*! Length of quarter-sinusoid in samples */
#define TONE_PART_SINE_LEN (sizeof(partial_sine)\
/sizeof(partial_sine[0]))
/*! Total length of sinusoid */
#define TONE_SINE_LEN ((TONE_PART_SINE_LEN*4)+4)
/*!
* Generate a sine from the quarter-waveform.
*/
static int16_t tone_sine(uint8_t sample)
{
/* Key points */
if ((sample % (TONE_SINE_LEN/2)) == 0)
/* Zero crossings */
return 0;
if (sample == TONE_SINE_LEN/4)
/* Maximum */
return INT16_MAX;
if (sample == (3*TONE_SINE_LEN)/4)
/* Minimum */
return INT16_MIN;
if (sample < TONE_SINE_LEN/4)
/* First quarter of sine wave */
return partial_sine[sample-1];
if (sample < (TONE_SINE_LEN/2))
/* Second quarter */
return partial_sine[(TONE_SINE_LEN/2)-sample-1];
if (sample < ((3*TONE_SINE_LEN)/4))
/* Third quarter */
return -partial_sine[(sample-3) % TONE_PART_SINE_LEN];
if (sample < TONE_SINE_LEN)
/* Final quarter */
return -partial_sine[TONE_SINE_LEN-sample-1];
/* We should not get here */
return 0;
}
/*!
* Re-set the tone generator.
*
* @param tone_gen Tone generator to reset.
* @param freq Frequency in Hz, 0 = silence.
* @param duration Duration in milliseconds. 0 to stop.
*/
void tone_reset(
struct tone_gen_t* const tone_gen,
uint16_t freq, uint16_t duration)
{
if (freq)
/* Compute the time step */
tone_gen->step = (((2*freq*TONE_SINE_LEN) << TONE_SHIFT)
/ ((2*TONE_FS) + 1) + 1);
else
/* DC tone == silence */
tone_gen->step = 0;
/* Compute remaining samples */
tone_gen->remain = (uint16_t)(
((uint32_t)(TONE_FS * duration)) / 1000);
/* Initialise the sample counter */
tone_gen->sample = 0;
}
/*!
* Retrieve the next sample from the tone generator.
* @param tone_gen Tone generator to update.
*/
int16_t tone_next(
struct tone_gen_t* const tone_gen)
{
if (!tone_gen)
return 0;
if (!tone_gen->remain)
return 0;
if (!tone_gen->step) {
/* Special case, emit silence */
tone_gen->remain--;
return 0;
}
/* Compute sample index */
uint16_t sample_int = ((tone_gen->sample) >> TONE_SHIFT)
% TONE_SINE_LEN;
/* Advance tone generator state */
tone_gen->sample += tone_gen->step;
tone_gen->remain--;
return tone_sine(sample_int);
}
/*!
* Retrieve the current time in milliseconds.
*/
uint32_t tone_msec(const struct tone_gen_t* const tone_gen)
{
uint64_t ms = tone_gen->sample;
ms *= 1000;
ms /= TONE_FS;
return ms >> TONE_SHIFT;
}
+90
View File
@@ -0,0 +1,90 @@
/*!
* Time-out timer.
*
* This is a simple time-out timer for ensuring a maximum transmission
* time is observed. The time-out timer is configured with a total time
* in "ticks", which get counted down in an interrupt.
*
* When the "warning" level is reached, a flag is repeatedly set permit
* triggering of LEDs/sounds to warn the user that time is nearly up.
*
* Upon timeout, a separate flag is set to indicate timeout has taken
* place.
*
* Author Stuart Longland <me@vk4msl.id.au>
* Copyright (C) 2015 FreeDV project.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation. This program is
* distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see
* <http://www.gnu.org/licenses/>.
*/
#include "tot.h"
/*!
* Reset the time-out timer. This zeroes the counter and event flags.
*/
void tot_reset(struct tot_t * const tot)
{
tot->event = 0;
tot->remaining = 0;
tot->warn_remain = 0;
tot->ticks = 0;
}
/*!
* Start the time-out timer ticking.
*/
void tot_start(struct tot_t * const tot, uint32_t tot_ticks,
uint16_t warn_ticks)
{
tot->event = TOT_EVT_START;
tot->warn_remain = tot_ticks - warn_ticks;
tot->remaining = tot_ticks;
tot->ticks = tot->tick_period;
}
/*!
* Update the time-out timer state.
*/
void tot_update(struct tot_t * const tot)
{
if (!tot->event)
/* We are not active */
return;
if (tot->event & TOT_EVT_DONE)
/* We are done, do not process */
return;
if (tot->ticks)
/* Wait for a tick to pass */
return;
/* One "tick" has passed */
if (!tot->remaining) {
/* Time-out reached, reset all flags except timeout */
tot->event |= TOT_EVT_TIMEOUT | TOT_EVT_DONE;
return;
} else {
tot->remaining--;
}
if (!tot->warn_remain) {
/* Warning period has passed */
tot->event |= TOT_EVT_WARN | TOT_EVT_WARN_NEXT;
tot->warn_remain = tot->remain_warn_ticks;
} else {
tot->warn_remain--;
}
tot->ticks = tot->tick_period;
}
+30
View File
@@ -0,0 +1,30 @@
/*
usart_ut.c
David Rowe May 2019
Unit test for stm32 USART support.
tio is useful to receive the serial strings:
$ tio -m INLCRNL /dev/ttyUSB0
*/
#include <stm32f4xx.h>
#include "stm32f4_usart.h"
void Delay(uint32_t nCount)
{
while(nCount--)
{
}
}
int main(void){
usart_init();
while(1){
usart_puts("Hello, World\n");
Delay(0x3FFFFF);
}
}
+96
View File
@@ -0,0 +1,96 @@
/*---------------------------------------------------------------------------*\
FILE........: usb_vcp_ut.c
AUTHOR......: xenovacivus
DATE CREATED: 31 August 2014
USB Virtual COM Port (VCP) unit test that I found here:
https://github.com/xenovacivus/STM32DiscoveryVCP
Remarkably, it compiled and ran first time, and even the LEDs blink
as advertised, they just happen to match the LEDs on the SM1000!
However the speed was capped at about 130 kB/s. After a lot of
messing around I found suggestions in the comments from a similar
library here:
http://stm32f4-discovery.com/2014/08/library-24-virtual-com-port-vcp-stm32f4xx/
The key was changing APP_RX_DATA_SIZE in usbd_conf.h to 10000. I
guess the previous size of 2048 was constraining the length of USB
packets, and the USB overhead meant slow throughput. I could
achieve a max of 450 kB/s with this change, about 1/3 of the
theoretical 1.5 MB/s max for USB FS (12 Mbit/s).
I used this to test grabbing data from the STM32F4 Discovery:
$ sudo dd if=/dev/ttyACM0 of=/dev/null count=100
4+96 records in
44+1 records out
22615 bytes (23 kB) copied, 0.150884 s, 150 kB/s
However I occasionally see:
$ sudo dd if=/dev/ttyACM0 of=/dev/null count=100
dd: failed to open /dev/ttyACM0: Device or resource busy
Googling found some suggestion that this is due to "modem manager", however I
removed MM and the problem still exists.
\*---------------------------------------------------------------------------*/
#include <stm32f4xx.h>
#include <stm32f4xx_gpio.h>
#include "stm32f4_usb_vcp.h"
#include "sm1000_leds_switches.h"
volatile uint32_t ticker, buf_ticker;
#define N 640*6
short buf[N];
int main(void) {
int i;
for(i=0; i<N; i++)
buf[i] = 0;
sm1000_leds_switches_init();
usb_vcp_init();
SysTick_Config(SystemCoreClock/1000);
while (1) {
/* Blink the discovery red LED at 1Hz */
if (ticker > 500) {
GPIOD->BSRRH = GPIO_Pin_13;
}
if (ticker > 1000) {
ticker = 0;
GPIOD->BSRRL = GPIO_Pin_13;
}
/* Every 40ms send a buffer, simulates 16 bit samples at Fs=96kHz */
if (buf_ticker > 40) {
buf_ticker = 0;
led_pwr(1);
VCP_send_buffer((uint8_t*)buf, sizeof(buf));
led_pwr(0);
}
}
return 0;
}
/*
* Interrupt Handler
*/
void SysTick_Handler(void)
{
ticker++;
buf_ticker++;
}
+192
View File
@@ -0,0 +1,192 @@
#define HSE_VALUE ((uint32_t)8000000) /* STM32 discovery uses a 8Mhz external crystal */
#include "stm32f4xx_conf.h"
#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_exti.h"
#include "usbd_cdc_core.h"
#include "usbd_usr.h"
#include "usbd_desc.h"
#include "usbd_cdc_vcp.h"
#include "usb_dcd_int.h"
volatile uint32_t ticker, downTicker;
/*
* The USB data must be 4 byte aligned if DMA is enabled. This macro handles
* the alignment, if necessary (it's actually magic, but don't tell anyone).
*/
__ALIGN_BEGIN USB_OTG_CORE_HANDLE USB_OTG_dev __ALIGN_END;
void init();
void ColorfulRingOfDeath(void);
/*
* Define prototypes for interrupt handlers here. The conditional "extern"
* ensures the weak declarations from startup_stm32f4xx.c are overridden.
*/
#ifdef __cplusplus
extern "C" {
#endif
void SysTick_Handler(void);
void NMI_Handler(void);
void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void SVC_Handler(void);
void DebugMon_Handler(void);
void PendSV_Handler(void);
void OTG_FS_IRQHandler(void);
void OTG_FS_WKUP_IRQHandler(void);
#ifdef __cplusplus
}
#endif
int main(void)
{
/* Set up the system clocks */
SystemInit();
/* Initialize USB, IO, SysTick, and all those other things you do in the morning */
init();
while (1)
{
/* Blink the orange LED at 1Hz */
if (500 == ticker)
{
GPIOD->BSRRH = GPIO_Pin_13;
}
else if (1000 == ticker)
{
ticker = 0;
GPIOD->BSRRL = GPIO_Pin_13;
}
/* If there's data on the virtual serial port:
* - Echo it back
* - Turn the green LED on for 10ms
*/
uint8_t theByte;
if (VCP_get_char(&theByte))
{
VCP_put_char(theByte);
GPIOD->BSRRL = GPIO_Pin_12;
downTicker = 10;
}
if (0 == downTicker)
{
GPIOD->BSRRH = GPIO_Pin_12;
}
}
return 0;
}
void init()
{
/* STM32F4 discovery LEDs */
GPIO_InitTypeDef LED_Config;
/* Always remember to turn on the peripheral clock... If not, you may be up till 3am debugging... */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
LED_Config.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15;
LED_Config.GPIO_Mode = GPIO_Mode_OUT;
LED_Config.GPIO_OType = GPIO_OType_PP;
LED_Config.GPIO_Speed = GPIO_Speed_25MHz;
LED_Config.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &LED_Config);
/* Setup SysTick or CROD! */
if (SysTick_Config(SystemCoreClock / 1000))
{
ColorfulRingOfDeath();
}
/* Setup USB */
USBD_Init(&USB_OTG_dev,
USB_OTG_FS_CORE_ID,
&USR_desc,
&USBD_CDC_cb,
&USR_cb);
return;
}
/*
* Call this to indicate a failure. Blinks the STM32F4 discovery LEDs
* in sequence. At 168Mhz, the blinking will be very fast - about 5 Hz.
* Keep that in mind when debugging, knowing the clock speed might help
* with debugging.
*/
void ColorfulRingOfDeath(void)
{
uint16_t ring = 1;
while (1)
{
uint32_t count = 0;
while (count++ < 500000);
GPIOD->BSRRH = (ring << 12);
ring = ring << 1;
if (ring >= 1<<4)
{
ring = 1;
}
GPIOD->BSRRL = (ring << 12);
}
}
/*
* Interrupt Handlers
*/
void SysTick_Handler(void)
{
ticker++;
if (downTicker > 0)
{
downTicker--;
}
}
void NMI_Handler(void) {}
void HardFault_Handler(void) { ColorfulRingOfDeath(); }
void MemManage_Handler(void) { ColorfulRingOfDeath(); }
void BusFault_Handler(void) { ColorfulRingOfDeath(); }
void UsageFault_Handler(void){ ColorfulRingOfDeath(); }
void SVC_Handler(void) {}
void DebugMon_Handler(void) {}
void PendSV_Handler(void) {}
void OTG_FS_IRQHandler(void)
{
USBD_OTG_ISR_Handler (&USB_OTG_dev);
}
void OTG_FS_WKUP_IRQHandler(void)
{
if(USB_OTG_dev.cfg.low_power)
{
*(uint32_t *)(0xE000ED10) &= 0xFFFFFFF9 ;
SystemInit();
USB_OTG_UngateClock(&USB_OTG_dev);
}
EXTI_ClearITPendingBit(EXTI_Line18);
}