Skip to content

Commit

Permalink
optimize and switch to single-sample processing
Browse files Browse the repository at this point in the history
  • Loading branch information
trentgill committed Jul 11, 2024
1 parent bd91256 commit fdad476
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 130 deletions.
67 changes: 30 additions & 37 deletions lib/sfold.c

Large diffs are not rendered by default.

110 changes: 48 additions & 62 deletions ll/adc.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include "adc.h"

#include <stdio.h>
// #include <stm32f7xx_hal.h> // HAL_Delay()

#include "interrupts.h"
#include "../lib/caw.h"
Expand All @@ -10,13 +9,9 @@ static ADC_HandleTypeDef AdcHandle;
static ADC_HandleTypeDef AdcHandle2;
static ADC_ChannelConfTypeDef sConfig;

#define ADC_CHANNELS 3
#define ADC_BUFFERS 2
static volatile uint16_t adc_raw[ADC_CHANNELS*ADC_BUFFERS*2]; // 6 channels,
static volatile uint16_t* adc_raw2 = &adc_raw[ADC_CHANNELS*ADC_BUFFERS*1]; // second 3 channels
static volatile uint16_t adc_direct[2][6];


static void start_conversion(void);
static void next_conversion(void);

void ADC_Init(void){
AdcHandle.Instance = ADC1;
Expand All @@ -25,15 +20,15 @@ void ADC_Init(void){
AdcHandle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;
AdcHandle.Init.Resolution = ADC_RESOLUTION_12B;
AdcHandle.Init.ScanConvMode = ADC_SCAN_ENABLE;
AdcHandle.Init.ContinuousConvMode = ENABLE; // i think disable
AdcHandle.Init.NbrOfConversion = 3; // scan all 5 chans on ADC1
AdcHandle.Init.DiscontinuousConvMode = DISABLE; // i think enable
AdcHandle.Init.ContinuousConvMode = ENABLE;
AdcHandle.Init.NbrOfConversion = 3;
AdcHandle.Init.DiscontinuousConvMode = DISABLE;
AdcHandle.Init.NbrOfDiscConversion = 3;
AdcHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
AdcHandle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
AdcHandle.Init.DMAContinuousRequests = ENABLE;
AdcHandle.Init.DMAContinuousRequests = DISABLE;
AdcHandle.Init.EOCSelection = ADC_EOC_SEQ_CONV;

if( HAL_ADC_Init( &AdcHandle ) != HAL_OK ){
Expand All @@ -58,23 +53,21 @@ void ADC_Init(void){
if( HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK )
Caw_printf("HAL_ADC_ConfigChannel failed\n");



AdcHandle2.Instance = ADC2;
HAL_ADC_DeInit(&AdcHandle2);

AdcHandle2.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;
AdcHandle2.Init.Resolution = ADC_RESOLUTION_12B;
AdcHandle2.Init.ScanConvMode = ADC_SCAN_ENABLE;
AdcHandle2.Init.ContinuousConvMode = ENABLE; // i think disable
AdcHandle2.Init.NbrOfConversion = 3; // scan all 5 chans on ADC1
AdcHandle2.Init.DiscontinuousConvMode = DISABLE; // i think enable
AdcHandle2.Init.ScanConvMode = ADC_SCAN_ENABLE; // enable this to allow use of the internal MUX
AdcHandle2.Init.ContinuousConvMode = ENABLE; // enable means "one conversion" is the whole sequence
AdcHandle2.Init.NbrOfConversion = 3; // scan 3 chans on ADC
AdcHandle2.Init.DiscontinuousConvMode = DISABLE; // only used if doing a fancy irregular channel sequence
AdcHandle2.Init.NbrOfDiscConversion = 3;
AdcHandle2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
AdcHandle2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
AdcHandle2.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
AdcHandle2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
AdcHandle2.Init.DMAContinuousRequests = ENABLE;
AdcHandle2.Init.DMAContinuousRequests = DISABLE; // ENABLE this along with DMA circular buffer mode for continuous
AdcHandle2.Init.EOCSelection = ADC_EOC_SEQ_CONV;

if( HAL_ADC_Init( &AdcHandle2 ) != HAL_OK ){
Expand All @@ -95,34 +88,35 @@ void ADC_Init(void){
Caw_printf("HAL_ADC_ConfigChannel failed\n");


start_conversion();
next_conversion();
}

static void start_conversion(void){
static int double_buffer = 0;
static void next_conversion(void){
if( HAL_ADC_Start_DMA( &AdcHandle
, (uint32_t*)adc_raw
, ADC_CHANNELS*ADC_BUFFERS
, (uint32_t*)&adc_direct[double_buffer][0]
, 3
) != HAL_OK ){
Caw_printf("HAL_ADC_Start_DMA failed, retrying..\n");
// HAL_Delay(10);
if( HAL_ADC_Start_DMA( &AdcHandle
, (uint32_t*)adc_raw
, ADC_CHANNELS*ADC_BUFFERS
, (uint32_t*)&adc_direct[double_buffer][0]
, 3
) != HAL_OK ){
Caw_printf("HAL_ADC_Start_DMA failed again, ignoring\n");
return;
}
}

if( HAL_ADC_Start_DMA( &AdcHandle2
, (uint32_t*)adc_raw2
, ADC_CHANNELS*ADC_BUFFERS
, (uint32_t*)&adc_direct[double_buffer][3]
, 3
) != HAL_OK ){
Caw_printf("HAL_ADC_Start_DMA failed, retrying..\n");
// HAL_Delay(10);
if( HAL_ADC_Start_DMA( &AdcHandle2
, (uint32_t*)adc_raw2
, ADC_CHANNELS*ADC_BUFFERS
, (uint32_t*)&adc_direct[double_buffer][3]
, 3
) != HAL_OK ){
Caw_printf("HAL_ADC_Start_DMA failed again, ignoring\n");
return;
Expand Down Expand Up @@ -159,7 +153,7 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc){
hdma_adc.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_adc.Init.Mode = DMA_CIRCULAR;
hdma_adc.Init.Mode = DMA_NORMAL;
hdma_adc.Init.Priority = DMA_PRIORITY_HIGH;
hdma_adc.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
hdma_adc.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
Expand Down Expand Up @@ -197,7 +191,7 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc){
hdma_adc2.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc2.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc2.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_adc2.Init.Mode = DMA_CIRCULAR;
hdma_adc2.Init.Mode = DMA_NORMAL;
hdma_adc2.Init.Priority = DMA_PRIORITY_HIGH;
hdma_adc2.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
hdma_adc2.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
Expand All @@ -214,17 +208,30 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc){
}
}

static int read_half[2] = {0,0};
uint16_t ADC_get(int chan){ // inversion & bipolar shaping
// 1,2,3,1,2,3,4,5,6,4,5,6
if(chan<3){
chan += read_half[0] * ADC_CHANNELS;
} else {
chan -= ADC_CHANNELS; // 3-5 down to 0-2
chan += read_half[1] * ADC_CHANNELS; // +0 or +3 -> 0-2 / 3-5
chan += ADC_CHANNELS * 2; // += 6 -> 6-8 / 9-11
// return a pointer to an array that can be accesed directly with enum names
uint16_t* ADC_get_buffer(void){
return (uint16_t*)adc_direct[double_buffer];
}

uint16_t ADC_get(int chan){
return (uint16_t)adc_direct[double_buffer][chan];
}

// call this at the start of your tight audio callback
// this will setup the ADC_get() function to point to the valid data
static int completed = 0;
uint16_t* ADC_next_frame(void){
// check we completed the last read (raise warning if not, help with configuring speeds)
if(completed==0){
Caw_printf("ADC: dma wasn't done\n\r");
}
return adc_raw[chan];
// flip read/write heads
double_buffer ^= 1;

// start next cycle
next_conversion();

return ADC_get_buffer();
}


Expand All @@ -237,30 +244,9 @@ void DMA2_Stream2_IRQHandler(void){
HAL_DMA_IRQHandler(AdcHandle2.DMA_Handle);
}

static int counter = 0;
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc){
counter++;
if(hadc == &AdcHandle){
read_half[0] = 0;
} else if(hadc == &AdcHandle2){
read_half[1] = 0;
}
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc){
counter++;
if(hadc == &AdcHandle){
read_half[0] = 1;
} else if(hadc == &AdcHandle2){
read_half[1] = 1;
}
completed++;
}
void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc){
Caw_printf("adc error\n\r");
}

// debug / timing / optimization
int ADC_get_count(void){
int c = counter;
counter = 0;
return c;
}
3 changes: 3 additions & 0 deletions ll/adc.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc);
void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc);

int ADC_get_count(void);

// collect next frame, and return last received frame
uint16_t* ADC_next_frame(void);
61 changes: 32 additions & 29 deletions ll/adda.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,47 @@ static uint32_t stepper[16] = {0};

static uint32_t vals[4] = {0}; // direct set values for non-folds

static void write_to_dac_buffer(uint32_t* dest, int channel, int sample, int src);
static void write_to_dac_buffer(uint32_t* dest, int channel, int src);

// single sample mode. assume bsize == 1
void ADDA_BlockProcess(uint32_t* dest, int bsize){
uint16_t* adc = ADC_next_frame();

uint32_t samp = 0;

// smooth & stepped modulation
for(int i=0; i<bsize; i++){ // samples
for(int j=0; j<4; j++){ // channels
// samp is a 12bit value, right-justified in u32
samp = vals[j]; // override with set value
for(int j=0; j<4; j++){ // channels
// samp is a 12bit value, right-justified in u32
samp = vals[j]; // override with set value

write_to_dac_buffer(dest, j, i, samp);
}
write_to_dac_buffer(dest, j, samp);
}

const float i12f = 1.f / (float)0xfff;

// TODO move this inside the bsize loop once we have per-sample ADC
sfold_set_fold(i12f * (float)ADC_get(ADC_Fold));
sfold_set_rotate(i12f * (float)ADC_get(ADC_Rotate));
sfold_set_density(i12f * (float)ADC_get(ADC_Density));
sfold_set_offset(i12f * (float)ADC_get(ADC_Offset));
sfold_set_id(i12f * (float)ADC_get(ADC_ID));

for(int i=0; i<bsize; i++){ // samples
for(int j=4; j<16; j++){ // channels
// samp is a 12bit value, right-justified in u32
uint32_t samp = stepper[j];
stepper[j] += j+1;
stepper[j] &= 0xfff;

float sf = sfold(j-4);

write_to_dac_buffer(dest, DAC_get_channel_id(j-4)
, i, (int)(sf*(float)0xfff));
// write_to_dac_buffer(dest, j, i, samp);
}
// sfold_set_fold(i12f * (float)ADC_get(ADC_Fold));
// sfold_set_rotate(i12f * (float)ADC_get(ADC_Rotate));
// sfold_set_density(i12f * (float)ADC_get(ADC_Density));
// sfold_set_offset(i12f * (float)ADC_get(ADC_Offset));
// sfold_set_id(i12f * (float)ADC_get(ADC_ID));

sfold_set_fold(i12f * (float)adc[ADC_Fold]);
sfold_set_rotate(i12f * (float)adc[ADC_Rotate]);
sfold_set_density(i12f * (float)adc[ADC_Density]);
sfold_set_offset(i12f * (float)adc[ADC_Offset]);
sfold_set_id(i12f * (float)adc[ADC_ID]);

for(int j=4; j<16; j++){ // channels
// samp is a 12bit value, right-justified in u32
// uint32_t samp = stepper[j];
// stepper[j] += j+1;
// stepper[j] &= 0xfff;

float sf = sfold(j-4);

write_to_dac_buffer(dest, DAC_get_channel_id(j-4)
, (int)(sf*(float)0xfff));
}
}

Expand All @@ -62,9 +66,8 @@ void ADDA_set_val(int ch, uint32_t val){
// really just need to unroll the loop
// honestly this is probably neglible on the f722 chip
// really just wanted to abstract this away as it was confusing to get right
static void write_to_dac_buffer(uint32_t* dest, int channel, int sample, int src){
static void write_to_dac_buffer(uint32_t* dest, int channel, int src){
int jj = (channel & 7) << 1; // bottom 3 bits, x2. 0/8 follow each other
int jx = channel >> 3; // 1 if an upper set. upper 8 are 1 step ahead of lower
int ix = sample * 16; // for each sample, step forward by 16 samples (there are 16 channels)
dest[jj+jx+ix] = (jj<<11) | (src & 0xfff); // hard cut values to 12bit range
dest[jj+jx] = (jj<<11) | (src & 0xfff); // hard cut values to 12bit range
}
5 changes: 3 additions & 2 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ int main(void){
stepped_init();
sfold_init(12);

DAC_Init(32, 16); // 32 samples per block, 16 channels
// DAC_Init(32, 16); // 32 samples per block, 16 channels
DAC_Init(1, 16); // DISABLE BLOCK PROCESSING, single sample! minimal latency
DAC_Start();

uint32_t last_tick = HAL_GetTick();
Expand Down Expand Up @@ -103,7 +104,7 @@ int main(void){
a >>= 12; // divide by 4096
lights_xset(stepped_ix());
// Caw_printf("%i\n\r",a);
Caw_printf("%i\n\r",ADC_get_count());
// Caw_printf("%i\n\r",ADC_get_count());
}
for(int i=0; i<6; i++){
// ADDA_set_val(i, ADC_get(i));
Expand Down
55 changes: 55 additions & 0 deletions util/lut_expo.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
header = [[
// THIS FILE IS AUTOGENERATED //
// DO NOT EDIT THIS MANUALLY //
#pragma once
const float lut_expo_256[256] = {
]]

footer = "\n};\n"

-- expects values 0..255
-- outputs must be 0.f~1.f
function expo(i)
local c = i / 255 -- convert 0,255 into 0,1
-- return 2 ^ (10 * (c-1)) -- expo approximation (obvs could do real expo if we want?)
return 1 - math.sqrt(1 - c^2) -- expo as a quarter circle for max smoothness
end

function logo(i)
local c = i / 255 -- convert 0,255 into 0,1
-- return 1 - (2 ^ (-10 * c)) -- log approximation (obvs could do real expo if we want?)
return math.sqrt(2*c - c^2) -- log as a quarter circle for max smoothness
end

function build_table()
s = header
t = {}
for i=0,255 do
t[i+1] = expo(i)
end
print(t[1])
print(t[2])
print(t[3])
print(t[4])
print(t[127])
print(t[128])
print(t[255])
print(t[256])
s = s .. table.concat(t, ", ")
return s .. footer
end

local out_file = arg[1]

-- build_table()

do
f = io.open(out_file, 'w')
f:write(build_table())
f:close()
end

-- example usage:
-- lua scripts/lin2exp.lua lib/lin2exp.h
Loading

0 comments on commit fdad476

Please sign in to comment.