mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-06-25 11:52:53 +10:00
updated ui added new features
This commit is contained in:
+85
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+98
@@ -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);
|
||||
}
|
||||
Vendored
+175
@@ -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));
|
||||
}
|
||||
Vendored
+67
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
+1476
File diff suppressed because it is too large
Load Diff
+62
@@ -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
@@ -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>© 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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>© 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****/
|
||||
Vendored
+151
@@ -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;
|
||||
}
|
||||
Vendored
+90
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user