Compare commits
No commits in common. 'funcgen' and 'master' have entirely different histories.
@ -1,294 +0,0 @@ |
||||
#include "stm8s.h" |
||||
#include "fncgen.h" |
||||
|
||||
/** Get 14-bit LSB from a 28-bit value */ |
||||
#define FREQ_LSB(reg) (u16)(0x3FFF & (reg)) |
||||
|
||||
/** Get 14-bit MSB from a 28-bit value */ |
||||
#define FREQ_MSB(reg) (u16)(0x3FFF & ((reg)>>14)) |
||||
|
||||
/** Mask applied to the FG CREG to clear any previous waveform preset */ |
||||
#define FG_WFM_MASK (u16)(FG_OPBITEN | FG_DIV2 | FG_MODE) |
||||
|
||||
/**
|
||||
* Send 16 bits |
||||
* @param word |
||||
*/ |
||||
static void FG_SPI_Send16(uint16_t word) |
||||
{ |
||||
while(!SPI_GetFlagStatus(SPI_FLAG_TXE)); |
||||
SPI_SendData((u8)(word >> 8)); |
||||
while(!SPI_GetFlagStatus(SPI_FLAG_TXE)); |
||||
SPI_SendData((u8)(word)); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Write a single word over SPI to a FG |
||||
* @param inst - FG instance |
||||
* @param word - word to write (16 bits) |
||||
*/ |
||||
static void FG_WriteWord(FG_Instance *inst, uint16_t word) |
||||
{ |
||||
// NSS down
|
||||
inst->NSS_GPIO->ODR &= ~inst->NSS_PIN; |
||||
// Data
|
||||
FG_SPI_Send16(word); |
||||
// Wait for write to complete
|
||||
while(SPI_GetFlagStatus(SPI_FLAG_BSY)); |
||||
// NSS up
|
||||
inst->NSS_GPIO->ODR |= inst->NSS_PIN; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Manually start a FG data frame |
||||
* This function may be used for broadcasting register |
||||
* changes to multiple devices (ie. FG_RESET release |
||||
* for phase synchronisation) |
||||
* |
||||
* @param inst - FG instance |
||||
*/ |
||||
void FG_JoinBroadcast(FG_Instance *inst) |
||||
{ |
||||
// NSS down
|
||||
inst->NSS_GPIO->ODR &= ~inst->NSS_PIN; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Manually end a FG data frame |
||||
* This function is used to terminate a broadcast session. |
||||
* |
||||
* @param inst - FG instance |
||||
*/ |
||||
void FG_LeaveBroadcast(FG_Instance *inst) |
||||
{ |
||||
// Wait for write to complete
|
||||
while(SPI_GetFlagStatus(SPI_FLAG_BSY)); |
||||
// NSS up
|
||||
inst->NSS_GPIO->ODR |= inst->NSS_PIN; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Flush the CREG instance variable to the device |
||||
* @param inst - FG instance |
||||
*/ |
||||
static inline void FG_WriteCREG(FG_Instance *inst) |
||||
{ |
||||
FG_WriteWord(inst, inst->CREG); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Set frequency in a bank register |
||||
* Change is immediately applied if the bank is active. |
||||
* |
||||
* @param inst - FG instance |
||||
* @param bank - bank number |
||||
* @param regval - new frequency register value (28 bits) |
||||
*/ |
||||
void FG_SetFreq(FG_Instance *inst, FG_FreqBank bank, u32 regval) |
||||
{ |
||||
u16 word; |
||||
|
||||
// Ensure B28 is set
|
||||
if (!(inst->CREG & FG_B28)) { |
||||
inst->CREG |= FG_B28; |
||||
FG_WriteCREG(inst); |
||||
} |
||||
|
||||
word = (bank == FREQ0 ? FG_ADDR_FREQ0 : FG_ADDR_FREQ1); |
||||
FG_WriteWord(inst, word | FREQ_LSB(regval)); // 14 bits LSB
|
||||
FG_WriteWord(inst, word | FREQ_MSB(regval)); // 14 bits MSB
|
||||
} |
||||
|
||||
/**
|
||||
* @brief Set frequency MSB in a bank register |
||||
* Change is immediately applied if the bank is active. |
||||
* |
||||
* @param inst - FG instance |
||||
* @param bank - frequency bank to change |
||||
* @param lsb14 - upper 14 bits of the frequency register |
||||
*/ |
||||
void FG_SetFreqMSB(FG_Instance *inst, FG_FreqBank bank, u16 msb14) |
||||
{ |
||||
u16 word; |
||||
|
||||
// Ensure B28 is cleared
|
||||
if ((inst->CREG & (FG_B28 | FG_HLB)) != FG_HLB) { |
||||
inst->CREG &= ~FG_B28; |
||||
inst->CREG |= FG_HLB; |
||||
FG_WriteCREG(inst); |
||||
} |
||||
|
||||
word = (bank == FREQ0 ? FG_ADDR_FREQ0 : FG_ADDR_FREQ1); |
||||
FG_WriteWord(inst, word | (u16)(msb14 & 0x3FFF)); // 14 bits LSB
|
||||
} |
||||
|
||||
/**
|
||||
* @brief Set frequency LSB in a bank register |
||||
* Change is immediately applied if the bank is active. |
||||
* |
||||
* @param inst - FG instance |
||||
* @param bank - frequency bank to change |
||||
* @param lsb14 - lower 14 bits of the frequency register |
||||
*/ |
||||
void FG_SetFreqLSB(FG_Instance *inst, FG_FreqBank bank, u16 lsb14) |
||||
{ |
||||
u16 word; |
||||
|
||||
// Ensure B28 is cleared
|
||||
if ((inst->CREG & (FG_B28 | FG_HLB)) != 0) { |
||||
inst->CREG &= ~FG_B28; |
||||
inst->CREG &= ~FG_HLB; |
||||
FG_WriteCREG(inst); |
||||
} |
||||
|
||||
word = (bank == FREQ0 ? FG_ADDR_FREQ0 : FG_ADDR_FREQ1); |
||||
FG_WriteWord(inst, word | (u16)(lsb14 & 0x3FFF)); // 14 bits LSB
|
||||
} |
||||
|
||||
/**
|
||||
* @brief Select active frequency bank (for FSK) |
||||
* @param inst - FG instance |
||||
* @param bank - bank number |
||||
*/ |
||||
void FG_FreqSwitch(FG_Instance *inst, FG_FreqBank bank) |
||||
{ |
||||
if (bank == FREQ0) { |
||||
inst->CREG &= ~FG_FSELECT; |
||||
} else { |
||||
inst->CREG |= FG_FSELECT; |
||||
} |
||||
FG_WriteCREG(inst); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Set phase in a bank register |
||||
* Change is immediately applied if the bank is active. |
||||
* |
||||
* @param inst - FG instance |
||||
* @param bank - bank number |
||||
* @param regval - new phase register value (12 bits) |
||||
*/ |
||||
void FG_SetPhase(FG_Instance *inst, FG_PhaseBank bank, u16 regval) |
||||
{ |
||||
u16 word = (bank == PHASE0 ? FG_ADDR_PHASE0 : FG_ADDR_PHASE1); |
||||
FG_WriteWord(inst, word | (u16) (regval & 0xFFF)); // 12 bits LSB
|
||||
} |
||||
|
||||
/**
|
||||
* @brief Select active phase register (for PSK) |
||||
* @param inst - FG instance |
||||
* @param bank - bank number |
||||
*/ |
||||
void FG_PhaseSwitch(FG_Instance *inst, FG_PhaseBank bank) |
||||
{ |
||||
if (bank == PHASE0) { |
||||
inst->CREG &= ~FG_PSELECT; |
||||
} else { |
||||
inst->CREG |= FG_PSELECT; |
||||
} |
||||
FG_WriteCREG(inst); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Select a function generator waveform preset |
||||
* @param inst - FG instance |
||||
* @param wfm - waveform preset (enum) |
||||
*/ |
||||
void FG_SetWaveform(FG_Instance *inst, FG_Waveform wfm) |
||||
{ |
||||
inst->CREG &= ~FG_WFM_MASK; |
||||
inst->CREG |= wfm; |
||||
FG_WriteCREG(inst); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Suspend a function generator |
||||
* Stops the counter, keeping the output constant |
||||
* |
||||
* @param inst - FG instance |
||||
*/ |
||||
void FG_Suspend(FG_Instance *inst) |
||||
{ |
||||
inst->CREG |= FG_SLEEP1; |
||||
FG_WriteCREG(inst); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Resume a function generator from suspend state * |
||||
* @param inst - FG instance |
||||
*/ |
||||
void FG_Resume(FG_Instance *inst) |
||||
{ |
||||
inst->CREG &= ~FG_SLEEP1; |
||||
FG_WriteCREG(inst); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Enable or disable a function generator |
||||
* When the FG is disabled, the output goes to a midpoint and |
||||
* counting registers are reset. |
||||
* |
||||
* @param inst - FG instance |
||||
* @param enable - enable status |
||||
*/ |
||||
void FG_Cmd(FG_Instance *inst, FunctionalState enable) |
||||
{ |
||||
if (enable == ENABLE) { |
||||
inst->CREG &= ~FG_RESET; |
||||
} else { |
||||
inst->CREG |= FG_RESET; |
||||
} |
||||
FG_WriteCREG(inst); |
||||
} |
||||
|
||||
void FG_Reset(FG_Instance *inst) |
||||
{ |
||||
// Stop, enable fullsize freq writes
|
||||
inst->CREG = FG_B28 | FG_RESET; |
||||
FG_WriteCREG(inst); |
||||
|
||||
FG_SetFreq(inst, FREQ0, HZ_REG(500)); |
||||
FG_SetFreq(inst, FREQ1, 0); |
||||
FG_SetPhase(inst, PHASE0, 0); |
||||
FG_SetPhase(inst, PHASE1, 0); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Configure a function generator |
||||
* Sets up the GPIO pin and resets the target. |
||||
* SPI must already be configured! |
||||
* |
||||
* @param inst - FG instance |
||||
* @param NSS_GPIO - port with the slave select pin |
||||
* @param NSS_PIN - slave slect pin position |
||||
*/ |
||||
void FG_Init(FG_Instance *inst, GPIO_TypeDef *NSS_GPIO, GPIO_Pin_TypeDef NSS_PIN) |
||||
{ |
||||
GPIO_Init(NSS_GPIO, NSS_PIN, GPIO_MODE_OUT_PP_HIGH_FAST); |
||||
|
||||
inst->NSS_GPIO = NSS_GPIO; |
||||
inst->NSS_PIN = NSS_PIN; |
||||
|
||||
FG_Reset(inst); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Configure SPI for talking to FG devices |
||||
* Sets up GPIOs and inits the SPI peripheral |
||||
*/ |
||||
void FG_SPI_Init(void) |
||||
{ |
||||
// MOSI, SCK
|
||||
GPIO_Init(GPIOC, GPIO_PIN_5 | GPIO_PIN_6, GPIO_MODE_OUT_PP_HIGH_FAST); |
||||
|
||||
SPI_Init(SPI_FIRSTBIT_MSB, |
||||
SPI_BAUDRATEPRESCALER_2, |
||||
SPI_MODE_MASTER, |
||||
SPI_CLOCKPOLARITY_HIGH, |
||||
SPI_CLOCKPHASE_1EDGE, |
||||
SPI_DATADIRECTION_1LINE_TX, |
||||
SPI_NSS_SOFT, |
||||
0); |
||||
|
||||
SPI_Cmd(ENABLE); |
||||
} |
@ -1,82 +0,0 @@ |
||||
//
|
||||
// Created by MightyPork on 2017/02/17.
|
||||
//
|
||||
|
||||
#ifndef STM8S_FNCGEN_H |
||||
#define STM8S_FNCGEN_H |
||||
|
||||
// See the C file for doxygen
|
||||
|
||||
#include "stm8s.h" |
||||
|
||||
// Freq write - addr + 14 bits
|
||||
// B28 and HLB allow sequential write, or repeated update of half-registers
|
||||
// In sequential write, the half-registers are written in the order LSB, MSB
|
||||
#define FG_ADDR_CR (u16)0x0000 |
||||
#define FG_ADDR_FREQ0 (u16)0x4000 |
||||
#define FG_ADDR_FREQ1 (u16)0x8000 |
||||
|
||||
// Phase write - addr + 12 bits
|
||||
#define FG_ADDR_PHASE0 (u16)0xC000 |
||||
#define FG_ADDR_PHASE1 (u16)0xE000 |
||||
|
||||
#define FG_B28 (u16)0x2000 // Enable sequential write of the freq registers
|
||||
#define FG_HLB (u16)0x1000 // Nonsequential write, register select (0-LSB, 1-MSB)
|
||||
#define FG_FSELECT (u16)0x0800 // Select active frequency reg
|
||||
#define FG_PSELECT (u16)0x0400 // Select active phase reg
|
||||
#define FG_RESET (u16)0x0100 // Reset counter and keep output at midpoint
|
||||
#define FG_SLEEP1 (u16)0x0080 // Suspend
|
||||
#define FG_SLEEP12 (u16)0x0040 // Shut down ADC (when using square output)
|
||||
#define FG_OPBITEN (u16)0x0020 // Output counter MSb (0=SIN/TRI,1=SQUARE)
|
||||
#define FG_DIV2 (u16)0x0008 // Divide counter MSb by 2
|
||||
#define FG_MODE (u16)0x0002 // If ADC enabled, 0 = SIN, 1 = TRI
|
||||
|
||||
/** Convert frequency in Hz to a register value (for 25 MHz MCLK) */ |
||||
#define HZ_REG(hz) (u32)((float)hz * 10.73741824f) |
||||
|
||||
/** Function generator instance object */ |
||||
typedef struct { |
||||
uint16_t CREG; |
||||
GPIO_TypeDef *NSS_GPIO; |
||||
GPIO_Pin_TypeDef NSS_PIN; |
||||
} FG_Instance; |
||||
|
||||
/** FG frequency bank number */ |
||||
typedef enum { |
||||
FREQ0 = 0, |
||||
FREQ1 = 1 |
||||
} FG_FreqBank; |
||||
|
||||
/** FG phase bank number */ |
||||
typedef enum { |
||||
PHASE0 = 0, |
||||
PHASE1 = 1 |
||||
} FG_PhaseBank; |
||||
|
||||
/**
|
||||
* Function generator waveform presets |
||||
*/ |
||||
typedef enum { |
||||
WFM_SINE = (u16) 0, |
||||
WFM_TRIANGLE = (u16) FG_MODE, |
||||
WFM_SQUARE = (u16) (FG_OPBITEN | FG_DIV2), |
||||
WFM_SQUARE_DIV2 = (u16) FG_OPBITEN |
||||
} FG_Waveform; |
||||
|
||||
void FG_JoinBroadcast(FG_Instance *inst); |
||||
void FG_LeaveBroadcast(FG_Instance *inst); |
||||
void FG_SetFreq(FG_Instance *inst, FG_FreqBank bank, u32 regval); |
||||
void FG_SetFreqMSB(FG_Instance *inst, FG_FreqBank bank, u16 msb14); |
||||
void FG_SetFreqLSB(FG_Instance *inst, FG_FreqBank bank, u16 lsb14); |
||||
void FG_FreqSwitch(FG_Instance *inst, FG_FreqBank bank); |
||||
void FG_SetPhase(FG_Instance *inst, FG_PhaseBank bank, u16 regval); |
||||
void FG_PhaseSwitch(FG_Instance *inst, FG_PhaseBank bank); |
||||
void FG_SetWaveform(FG_Instance *inst, FG_Waveform wfm); |
||||
void FG_Suspend(FG_Instance *inst); |
||||
void FG_Resume(FG_Instance *inst); |
||||
void FG_Cmd(FG_Instance *inst, FunctionalState enable); |
||||
void FG_Reset(FG_Instance *inst); |
||||
void FG_Init(FG_Instance *inst, GPIO_TypeDef *NSS_GPIO, GPIO_Pin_TypeDef NSS_PIN); |
||||
void FG_SPI_Init(void); |
||||
|
||||
#endif //STM8S_FNCGEN_H
|
@ -1,31 +1,83 @@ |
||||
#include "stm8s.h" |
||||
#include <stdio.h> |
||||
#include "bootstrap.h" |
||||
#include "fncgen.h" |
||||
|
||||
FG_Instance fgi; |
||||
#define LEVEL_TOP 400 |
||||
#define LEVEL_MAX 370 |
||||
|
||||
volatile uint16_t level = 200; |
||||
|
||||
/**
|
||||
* Set PWM level |
||||
*/ |
||||
void PWM_Write(void) |
||||
{ |
||||
uint16_t tmp = level; |
||||
if (tmp > LEVEL_MAX) tmp = LEVEL_MAX; |
||||
TIM1_SetCompare4(tmp); |
||||
} |
||||
|
||||
void PWM_Cmd(FunctionalState fs) |
||||
{ |
||||
TIM1_CtrlPWMOutputs(fs); |
||||
} |
||||
|
||||
/**
|
||||
* Set up the PWM generation |
||||
*/ |
||||
void PWM_Setup() |
||||
{ |
||||
// open drain, fast
|
||||
GPIOC->DDR |= GPIO_PIN_4; // out
|
||||
//GPIOC->CR1 &= ~GPIO_PIN_4; // open drain
|
||||
GPIOC->CR2 |= GPIO_PIN_4; // fast
|
||||
|
||||
TIM1_TimeBaseInit(0, TIM1_COUNTERMODE_UP, LEVEL_TOP, 0); |
||||
|
||||
TIM1_OC4Init(TIM1_OCMODE_PWM1, |
||||
TIM1_OUTPUTSTATE_ENABLE, |
||||
level, |
||||
TIM1_OCPOLARITY_HIGH, |
||||
TIM1_OCIDLESTATE_SET); |
||||
|
||||
TIM1_Cmd(ENABLE); |
||||
} |
||||
|
||||
/**
|
||||
* Set up the analog input |
||||
*/ |
||||
void AIN_Setup() |
||||
{ |
||||
ADC1_ConversionConfig(ADC1_CONVERSIONMODE_CONTINUOUS, |
||||
ADC1_CHANNEL_3, |
||||
ADC1_ALIGN_RIGHT); |
||||
ADC1_Cmd(ENABLE); |
||||
ADC1_StartConversion(); |
||||
} |
||||
|
||||
void main(void) |
||||
{ |
||||
u16 t; |
||||
bool x = FALSE; |
||||
SimpleInit(); |
||||
uint16_t cnt = 0; |
||||
uint16_t conv; |
||||
|
||||
FG_SPI_Init(); |
||||
SimpleInit(); |
||||
PWM_Setup(); |
||||
AIN_Setup(); |
||||
|
||||
FG_Init(&fgi, GPIOC, GPIO_PIN_4); |
||||
FG_SetFreq(&fgi, FREQ0, HZ_REG(4000)); |
||||
FG_SetFreq(&fgi, FREQ1, HZ_REG(2500)); |
||||
FG_SetWaveform(&fgi, WFM_SINE); |
||||
FG_Cmd(&fgi, ENABLE); |
||||
// Go
|
||||
PWM_Cmd(ENABLE); |
||||
|
||||
t = ms_now(); |
||||
while (1) { |
||||
if (ms_loop_elapsed(&t, 100)) { |
||||
if (cnt++ == 65535) { |
||||
cnt = 0; |
||||
LED_Toggle(); |
||||
} |
||||
|
||||
// Switching freq banks - connect a speaker to hear 2-tone beeping!
|
||||
x = !x; |
||||
FG_FreqSwitch(&fgi, x); |
||||
// adjust level
|
||||
if (ADC1_GetFlagStatus(ADC1_FLAG_EOC)) { |
||||
conv = ADC1_GetConversionValue(); |
||||
level = conv; |
||||
PWM_Write(); |
||||
} |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue