multithreaded app
This commit is contained in:
parent
c2e37543ff
commit
71ccbeffe6
7
RpiLedBars/.vscode/launch.json
vendored
7
RpiLedBars/.vscode/launch.json
vendored
@ -11,7 +11,7 @@
|
|||||||
"program": "${workspaceFolder}/bin/pixled",
|
"program": "${workspaceFolder}/bin/pixled",
|
||||||
"args": [
|
"args": [
|
||||||
"-n",
|
"-n",
|
||||||
"5",
|
"60",
|
||||||
// "-t"
|
// "-t"
|
||||||
],
|
],
|
||||||
"stopAtEntry": false,
|
"stopAtEntry": false,
|
||||||
@ -27,7 +27,10 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"miDebuggerPath": "${workspaceFolder}/sgdb.sh",
|
"miDebuggerPath": "${workspaceFolder}/sgdb.sh",
|
||||||
"preLaunchTask": "${defaultBuildTask}"
|
"preLaunchTask": "${defaultBuildTask}",
|
||||||
|
"logging": {
|
||||||
|
"engineLogging": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -1,24 +1,24 @@
|
|||||||
CC=gcc
|
CC := gcc
|
||||||
CFLAGS=-Wall -g -D_DEBUG
|
CFLAGS := -Wall -g -D_DEBUG
|
||||||
LDFLAGS=-lwiringPi #-lpthread
|
LDFLAGS := -lwiringPi -lasound -lfftw3 -lpthread -lm
|
||||||
SRCDIR=src
|
|
||||||
OBJDIR=obj
|
|
||||||
BINDIR=bin
|
|
||||||
SRC=$(notdir $(wildcard $(SRCDIR)/*.c))
|
|
||||||
OBJ=$(SRC:.c=.o)
|
|
||||||
BIN=pixled
|
|
||||||
|
|
||||||
all: $(addprefix $(BINDIR)/, $(BIN))
|
SRC := src
|
||||||
|
OBJ := obj
|
||||||
|
BIN := bin/pixled
|
||||||
|
|
||||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c
|
# SOURCES := $(wildcard $(SRC)/*.c)
|
||||||
if [ ! -d $(OBJDIR) ]; then mkdir "$(OBJDIR)"; fi
|
SOURCES := $(shell find $(SRC) -type f -name "*.c")
|
||||||
$(CC) -c -o $@ $< $(CFLAGS)
|
OBJECTS := $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SOURCES))
|
||||||
|
|
||||||
$(BINDIR)/$(BIN) : $(addprefix $(OBJDIR)/, $(OBJ))
|
all: $(BIN)
|
||||||
if [ ! -d "$(BINDIR)" ]; then mkdir "$(BINDIR)"; fi
|
|
||||||
|
$(OBJ)/%.o: $(SRC)/%.c
|
||||||
|
if [ ! -d "$(dir $@)" ]; then mkdir -p "$(dir $@)"; fi
|
||||||
|
$(CC) -I$(SRC) -c $< -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(BIN) : $(OBJECTS)
|
||||||
|
if [ ! -d "$(dir $(BIN))" ]; then mkdir -p "$(dir $(BIN))"; fi
|
||||||
$(CC) -o $@ $^ $(LDFLAGS)
|
$(CC) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BINDIR)/* $(OBJDIR)/*
|
rm -rf $(OBJ)
|
||||||
if [ -d $(OBJDIR) ]; then rmdir "$(OBJDIR)"; fi
|
|
||||||
if [ -d "$(BINDIR)" ]; then rmdir "$(BINDIR)"; fi
|
|
25
RpiLedBars/cava_config
Normal file
25
RpiLedBars/cava_config
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
[general]
|
||||||
|
framerate = 60
|
||||||
|
autosens = 0
|
||||||
|
sensitivity = 200
|
||||||
|
bars = 128
|
||||||
|
|
||||||
|
[input]
|
||||||
|
method = alsa
|
||||||
|
source = plughw:1
|
||||||
|
|
||||||
|
[output]
|
||||||
|
method = raw
|
||||||
|
channels = mono
|
||||||
|
mono_option = left
|
||||||
|
raw_target = /tmp/cava_output
|
||||||
|
data_format = ascii
|
||||||
|
ascii_max_range=65535
|
||||||
|
bit_format = 16bit
|
||||||
|
|
||||||
|
[smoothing]
|
||||||
|
integral = 0
|
||||||
|
monstercat = 1
|
||||||
|
waves = 0
|
||||||
|
gravity = 0
|
||||||
|
|
25
RpiLedBars/cava_test
Normal file
25
RpiLedBars/cava_test
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
[general]
|
||||||
|
framerate = 60
|
||||||
|
autosens = 0
|
||||||
|
sensitivity = 200
|
||||||
|
bars = 20
|
||||||
|
|
||||||
|
[input]
|
||||||
|
method = alsa
|
||||||
|
source = plughw:1
|
||||||
|
|
||||||
|
[output]
|
||||||
|
method = raw
|
||||||
|
channels = mono
|
||||||
|
mono_option = left
|
||||||
|
;raw_target = /tmp/cava_output
|
||||||
|
data_format = ascii
|
||||||
|
ascii_max_range=65535
|
||||||
|
bit_format = 16bit
|
||||||
|
|
||||||
|
[smoothing]
|
||||||
|
integral = 0
|
||||||
|
monstercat = 0
|
||||||
|
waves = 0
|
||||||
|
gravity = 0
|
||||||
|
|
BIN
RpiLedBars/file.wav
Normal file
BIN
RpiLedBars/file.wav
Normal file
Binary file not shown.
5
RpiLedBars/install.sh
Executable file
5
RpiLedBars/install.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
# make
|
||||||
|
cp ./bin/pixled bin/service_pixled
|
||||||
|
sudo -s bash -c "cp pixled.service /etc/systemd/system; systemctl daemon-reload"
|
||||||
|
# sudo -s bash -c "systemctl enable pixled"
|
||||||
|
sudo -s bash -c "systemctl restart pixled"
|
12
RpiLedBars/pixled.service
Normal file
12
RpiLedBars/pixled.service
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=pixled
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/home/pi/LedBars/RpiLedBars/bin/service_pixled -n 60
|
||||||
|
Restart=always
|
||||||
|
User=root
|
||||||
|
Group=root
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
50
RpiLedBars/res/.asoundrc
Normal file
50
RpiLedBars/res/.asoundrc
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#The below 2 sections are commented, they control the default sound card to use
|
||||||
|
#This set up is for a Pi with an I2S microphone attached using the guide
|
||||||
|
#from adafruit at
|
||||||
|
# https://learn.adafruit.com/adafruit-i2s-mems-microphone-breakout
|
||||||
|
#Uncomment and the I2S will be your default card (assuming same setup)
|
||||||
|
#but you won't get audio playback because both recording and playback will be
|
||||||
|
#defaulted
|
||||||
|
#TODO - Figure out how to set default for recording separately
|
||||||
|
#To adjust use aplay -l to work out the devices you have and their card number
|
||||||
|
#For recording devices use arecord -l
|
||||||
|
|
||||||
|
#pcm.!default {
|
||||||
|
# type hw
|
||||||
|
# card 1
|
||||||
|
#}
|
||||||
|
|
||||||
|
#ctl.!default {
|
||||||
|
# type hw
|
||||||
|
# card 1
|
||||||
|
#}
|
||||||
|
|
||||||
|
#This section makes a reference to your I2S hardware, adjust the card name
|
||||||
|
# to what is shown in arecord -l after card x: before the name in []
|
||||||
|
#You may have to adjust channel count also but stick with default first
|
||||||
|
pcm.dmic_hw {
|
||||||
|
type hw
|
||||||
|
card sndrpii2scard
|
||||||
|
channels 1
|
||||||
|
format S32_LE
|
||||||
|
}
|
||||||
|
|
||||||
|
#This is the software volume control, it links to the hardware above and after
|
||||||
|
# saving the .asoundrc file you can type alsamixer, press F6 to select
|
||||||
|
# your I2S mic then F4 to set the recording volume and arrow up and down
|
||||||
|
# to adjust the volume
|
||||||
|
# After adjusting the volume - go for 50 percent at first, you can do
|
||||||
|
# something like
|
||||||
|
# arecord -D dmic_sv -c2 -r 48000 -f S32_LE -t wav -V mono -v myfile.wav
|
||||||
|
pcm.dmic_sv {
|
||||||
|
type softvol
|
||||||
|
slave.pcm dmic_hw
|
||||||
|
control {
|
||||||
|
name "Boost Capture Volume"
|
||||||
|
card sndrpii2scard
|
||||||
|
}
|
||||||
|
min_dB -3.0
|
||||||
|
max_dB 30.0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
18
RpiLedBars/res/install_i2s_mems_mic
Normal file
18
RpiLedBars/res/install_i2s_mems_mic
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
https://makersportal.com/blog/recording-stereo-audio-on-a-raspberry-pi
|
||||||
|
https://learn.adafruit.com/adafruit-i2s-mems-microphone-breakout/raspberry-pi-wiring-test
|
||||||
|
|
||||||
|
sudo pip3 install --upgrade adafruit-python-shell
|
||||||
|
cd /tmp
|
||||||
|
sudo wget https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/i2smic.py
|
||||||
|
sudo python3 i2smic.py
|
||||||
|
|
||||||
|
# plug microphone
|
||||||
|
sudo reboot
|
||||||
|
arecord -l
|
||||||
|
arecord -D plughw:1 -c1 -r 48000 -f S32_LE -t wav -V mono -v file.wav
|
||||||
|
|
||||||
|
# Control record volume
|
||||||
|
cp res/.asoundrc ~/.asoundrc
|
||||||
|
|
||||||
|
# alsa API
|
||||||
|
http://www.equalarea.com/paul/alsa-audio.html
|
@ -80,6 +80,7 @@ void gpio_set(int pin, int mode, int pull) {
|
|||||||
gpio_mode(pin, mode);
|
gpio_mode(pin, mode);
|
||||||
gpio_pull(pin, pull);
|
gpio_pull(pin, pull);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set I/P pullup or pulldown
|
// Set I/P pullup or pulldown
|
||||||
void gpio_pull(int pin, int pull) {
|
void gpio_pull(int pin, int pull) {
|
||||||
volatile uint32_t *reg = REG32(gpio_regs, GPIO_GPPUDCLK0) + pin / 32;
|
volatile uint32_t *reg = REG32(gpio_regs, GPIO_GPPUDCLK0) + pin / 32;
|
@ -32,17 +32,13 @@
|
|||||||
|
|
||||||
#include "rpi_pixleds.h"
|
#include "rpi_pixleds.h"
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "rpi_artnet.h"
|
|
||||||
#include "rpi_smi_defs.h"
|
#include "rpi_smi_defs.h"
|
||||||
|
|
||||||
// Structures for mapped I/O devices, and non-volatile memory
|
// Structures for mapped I/O devices, and non-volatile memory
|
||||||
@ -103,7 +99,7 @@ int chan_num;
|
|||||||
// chan_num++;
|
// chan_num++;
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// signal(SIGINT, terminate);
|
// signal(SIGINT, terminate_smi);
|
||||||
// map_devices();
|
// map_devices();
|
||||||
// init_smi(LED_NCHANS > 8 ? SMI_16_BITS : SMI_8_BITS, SMI_TIMING);
|
// init_smi(LED_NCHANS > 8 ? SMI_16_BITS : SMI_8_BITS, SMI_TIMING);
|
||||||
// map_uncached_mem(&vc_mem, VC_MEM_SIZE);
|
// map_uncached_mem(&vc_mem, VC_MEM_SIZE);
|
||||||
@ -163,7 +159,7 @@ int chan_num;
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// terminate(0);
|
// terminate_smi(0);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Convert RGB text string into integer data, for given channel
|
// Convert RGB text string into integer data, for given channel
|
||||||
@ -240,7 +236,7 @@ int hexdig(char c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Map GPIO, DMA and SMI registers into virtual mem (user space)
|
// Map GPIO, DMA and SMI registers into virtual mem (user space)
|
||||||
// If any of these fail, program will be terminated
|
// If any of these fail, program will be terminate_smid
|
||||||
void map_devices(void) {
|
void map_devices(void) {
|
||||||
map_periph(&gpio_regs, (void *)GPIO_BASE, PAGE_SIZE);
|
map_periph(&gpio_regs, (void *)GPIO_BASE, PAGE_SIZE);
|
||||||
map_periph(&dma_regs, (void *)DMA_BASE, PAGE_SIZE);
|
map_periph(&dma_regs, (void *)DMA_BASE, PAGE_SIZE);
|
||||||
@ -251,11 +247,11 @@ void map_devices(void) {
|
|||||||
// Catastrophic failure in initial setup
|
// Catastrophic failure in initial setup
|
||||||
void fail(char *s) {
|
void fail(char *s) {
|
||||||
printf(s);
|
printf(s);
|
||||||
terminate(0);
|
terminate_smi(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free memory segments and exit
|
// Free memory segments and exit
|
||||||
void terminate(int sig) {
|
void terminate_smi(int sig) {
|
||||||
int i;
|
int i;
|
||||||
printf("Closing\n");
|
printf("Closing\n");
|
||||||
if (gpio_regs.virt) {
|
if (gpio_regs.virt) {
|
||||||
@ -274,7 +270,8 @@ void terminate(int sig) {
|
|||||||
|
|
||||||
// Initialise SMI, given data width, time step, and setup/hold/strobe counts
|
// Initialise SMI, given data width, time step, and setup/hold/strobe counts
|
||||||
// Step value is in nanoseconds: even numbers, 2 to 30
|
// Step value is in nanoseconds: even numbers, 2 to 30
|
||||||
void init_smi(int width, int ns, int setup, int strobe, int hold) {
|
void init_smi(int ledChan, int ns, int setup, int strobe, int hold) {
|
||||||
|
int width = ledChan > 8 ? SMI_16_BITS : SMI_8_BITS;
|
||||||
int i, divi = ns / 2;
|
int i, divi = ns / 2;
|
||||||
|
|
||||||
smi_cs = (SMI_CS_REG *)REG32(smi_regs, SMI_CS);
|
smi_cs = (SMI_CS_REG *)REG32(smi_regs, SMI_CS);
|
@ -41,11 +41,6 @@
|
|||||||
#define TX_BUFF_SIZE(n) (TX_BUFF_LEN(n) * sizeof(TXDATA_T))
|
#define TX_BUFF_SIZE(n) (TX_BUFF_LEN(n) * sizeof(TXDATA_T))
|
||||||
#define VC_MEM_SIZE (PAGE_SIZE + TX_BUFF_SIZE(CHAN_MAXLEDS))
|
#define VC_MEM_SIZE (PAGE_SIZE + TX_BUFF_SIZE(CHAN_MAXLEDS))
|
||||||
|
|
||||||
#if TX_TEST
|
|
||||||
// Data for simple transmission test
|
|
||||||
TXDATA_T tx_test_data[] = {1, 2, 3, 4, 5, 6, 7, 0};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void test_leds();
|
void test_leds();
|
||||||
int str_rgb(char *s, int rgbs[][LED_NCHANS], int chan);
|
int str_rgb(char *s, int rgbs[][LED_NCHANS], int chan);
|
||||||
void set_color(uint32_t rgb, TXDATA_T *txd);
|
void set_color(uint32_t rgb, TXDATA_T *txd);
|
||||||
@ -54,8 +49,8 @@ void swap_bytes(void *data, int len);
|
|||||||
int hexdig(char c);
|
int hexdig(char c);
|
||||||
void map_devices(void);
|
void map_devices(void);
|
||||||
void fail(char *s);
|
void fail(char *s);
|
||||||
void terminate(int sig);
|
void terminate_smi(int sig);
|
||||||
void init_smi(int width, int ns, int setup, int hold, int strobe);
|
void init_smi(int ledChan, int ns, int setup, int hold, int strobe);
|
||||||
void setup_smi_dma(MEM_MAP *mp, int nsamp, TXDATA_T **txdata);
|
void setup_smi_dma(MEM_MAP *mp, int nsamp, TXDATA_T **txdata);
|
||||||
void start_smi(MEM_MAP *mp);
|
void start_smi(MEM_MAP *mp);
|
||||||
|
|
@ -29,9 +29,6 @@
|
|||||||
#define SMI_18_BITS 2
|
#define SMI_18_BITS 2
|
||||||
#define SMI_9_BITS 3
|
#define SMI_9_BITS 3
|
||||||
|
|
||||||
// DMA request
|
|
||||||
#define DMA_SMI_DREQ 4
|
|
||||||
|
|
||||||
// Union of 32-bit value with register bitfields
|
// Union of 32-bit value with register bitfields
|
||||||
#define REG_DEF(name, fields) \
|
#define REG_DEF(name, fields) \
|
||||||
typedef union { \
|
typedef union { \
|
159
RpiLedBars/res/src/cava/rpi_microphone.c
Normal file
159
RpiLedBars/res/src/cava/rpi_microphone.c
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
#include "rpi_microphone.h"
|
||||||
|
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define CHANNELS_COUNT 1
|
||||||
|
#define SAMPLE_RATE 44100
|
||||||
|
|
||||||
|
snd_pcm_t *capture_handle;
|
||||||
|
|
||||||
|
int write_to_fftw_input_buffers(int16_t frames, int16_t buf[frames * 2], audio_data_t *audio);
|
||||||
|
|
||||||
|
void *microphone_listen(void *arg) {
|
||||||
|
audio_data_t *audio = (audio_data_t *)arg;
|
||||||
|
microphone_setup(audio);
|
||||||
|
while (1) {
|
||||||
|
microphone_exec(audio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void microphone_setup(audio_data_t *audio) {
|
||||||
|
int err;
|
||||||
|
snd_pcm_hw_params_t *hw_params;
|
||||||
|
snd_pcm_uframes_t frames = audio->FFTtreblebufferSize;
|
||||||
|
unsigned int sampleRate = SAMPLE_RATE;
|
||||||
|
|
||||||
|
if ((err = snd_pcm_open(&capture_handle, audio->source, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
|
||||||
|
fprintf(stderr, "cannot open audio device %s (%s)\n", audio->source, snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
|
||||||
|
fprintf(stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0) {
|
||||||
|
fprintf(stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_set_access(capture_handle, hw_params,
|
||||||
|
SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
|
||||||
|
fprintf(stderr, "cannot set access type (%s)\n", snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
|
||||||
|
fprintf(stderr, "cannot set sample format (%s)\n", snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, CHANNELS_COUNT)) < 0) {
|
||||||
|
fprintf(stderr, "cannot set channel count (%s)\n", snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &sampleRate, NULL)) < 0) {
|
||||||
|
fprintf(stderr, "cannot set sample rate (%s)\n", snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_set_period_size_near(capture_handle, hw_params, &frames, NULL)) <
|
||||||
|
0) {
|
||||||
|
fprintf(stderr, "cannot set period size (%s)\n", snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0) {
|
||||||
|
fprintf(stderr, "cannot set parameters (%s)\n", snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_pcm_hw_params_get_rate(hw_params, &audio->rate, NULL);
|
||||||
|
|
||||||
|
snd_pcm_hw_params_free(hw_params);
|
||||||
|
|
||||||
|
if ((err = snd_pcm_prepare(capture_handle)) < 0) {
|
||||||
|
fprintf(stderr, "cannot prepare audio interface for use (%s)\n", snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void microphone_exec(audio_data_t *audio) {
|
||||||
|
int err;
|
||||||
|
snd_pcm_uframes_t buffer_size;
|
||||||
|
snd_pcm_uframes_t period_size;
|
||||||
|
snd_pcm_uframes_t frames = audio->FFTtreblebufferSize;
|
||||||
|
|
||||||
|
snd_pcm_get_params(capture_handle, &buffer_size, &period_size);
|
||||||
|
int16_t buf[period_size];
|
||||||
|
frames = period_size / 2;
|
||||||
|
|
||||||
|
err = snd_pcm_readi(capture_handle, buf, frames);
|
||||||
|
|
||||||
|
if (err == -EPIPE) {
|
||||||
|
/* EPIPE means overrun */
|
||||||
|
fprintf(stderr, "overrun occurred\n");
|
||||||
|
snd_pcm_prepare(capture_handle);
|
||||||
|
} else if (err < 0) {
|
||||||
|
fprintf(stderr, "error from read: %s\n", snd_strerror(err));
|
||||||
|
} else if (err != (int)frames) {
|
||||||
|
fprintf(stderr, "short read, read %d %d frames\n", err, (int)frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&audio->lock);
|
||||||
|
write_to_fftw_input_buffers(frames, buf, audio);
|
||||||
|
pthread_mutex_unlock(&audio->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_output_buffers(audio_data_t *data) {
|
||||||
|
memset(data->in_bass_l, 0, sizeof(double) * data->FFTbassbufferSize);
|
||||||
|
memset(data->in_mid_l, 0, sizeof(double) * data->FFTmidbufferSize);
|
||||||
|
memset(data->in_treble_l, 0, sizeof(double) * data->FFTtreblebufferSize);
|
||||||
|
memset(data->in_bass_l_raw, 0, sizeof(double) * data->FFTbassbufferSize);
|
||||||
|
memset(data->in_mid_l_raw, 0, sizeof(double) * data->FFTmidbufferSize);
|
||||||
|
memset(data->in_treble_l_raw, 0, sizeof(double) * data->FFTtreblebufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
int write_to_fftw_input_buffers(int16_t frames, int16_t buf[frames * 2], audio_data_t *audio) {
|
||||||
|
if (frames == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (uint16_t n = audio->FFTbassbufferSize; n > frames; n = n - frames) {
|
||||||
|
for (uint16_t i = 1; i <= frames; i++) {
|
||||||
|
audio->in_bass_l_raw[n - i] = audio->in_bass_l_raw[n - i - frames];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint16_t n = audio->FFTmidbufferSize; n > frames; n = n - frames) {
|
||||||
|
for (uint16_t i = 1; i <= frames; i++) {
|
||||||
|
audio->in_mid_l_raw[n - i] = audio->in_mid_l_raw[n - i - frames];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint16_t n = audio->FFTtreblebufferSize; n > frames; n = n - frames) {
|
||||||
|
for (uint16_t i = 1; i <= frames; i++) {
|
||||||
|
audio->in_treble_l_raw[n - i] = audio->in_treble_l_raw[n - i - frames];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint16_t n = frames - 1;
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < frames; i++) {
|
||||||
|
audio->in_bass_l_raw[n] = buf[i * 2];
|
||||||
|
audio->in_mid_l_raw[n] = audio->in_bass_l_raw[n];
|
||||||
|
audio->in_treble_l_raw[n] = audio->in_bass_l_raw[n];
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hann Window
|
||||||
|
for (int i = 0; i < audio->FFTbassbufferSize; i++) {
|
||||||
|
audio->in_bass_l[i] = audio->bass_multiplier[i] * audio->in_bass_l_raw[i];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < audio->FFTmidbufferSize; i++) {
|
||||||
|
audio->in_mid_l[i] = audio->mid_multiplier[i] * audio->in_mid_l_raw[i];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < audio->FFTtreblebufferSize; i++) {
|
||||||
|
audio->in_treble_l[i] = audio->treble_multiplier[i] * audio->in_treble_l_raw[i];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
42
RpiLedBars/res/src/cava/rpi_microphone.h
Normal file
42
RpiLedBars/res/src/cava/rpi_microphone.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#if !defined(__RPI_MICROPHONE_H__)
|
||||||
|
#define __RPI_MICROPHONE_H__
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int FFTbassbufferSize;
|
||||||
|
int FFTmidbufferSize;
|
||||||
|
int FFTtreblebufferSize;
|
||||||
|
int bass_index;
|
||||||
|
int mid_index;
|
||||||
|
int treble_index;
|
||||||
|
double *bass_multiplier;
|
||||||
|
double *mid_multiplier;
|
||||||
|
double *treble_multiplier;
|
||||||
|
double *in_bass_l_raw;
|
||||||
|
double *in_mid_l_raw;
|
||||||
|
double *in_treble_l_raw;
|
||||||
|
double *in_bass_l;
|
||||||
|
double *in_mid_l;
|
||||||
|
double *in_treble_l;
|
||||||
|
int format;
|
||||||
|
unsigned int rate;
|
||||||
|
char *source; // alsa device, fifo path or pulse source
|
||||||
|
int im; // input mode alsa, fifo or pulse
|
||||||
|
unsigned int channels;
|
||||||
|
bool left, right, average;
|
||||||
|
int terminate; // shared variable used to terminate audio thread
|
||||||
|
char error_message[1024];
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
} audio_data_t;
|
||||||
|
|
||||||
|
void *microphone_listen(void *arg);
|
||||||
|
|
||||||
|
void microphone_setup(audio_data_t *audio);
|
||||||
|
|
||||||
|
void microphone_exec(audio_data_t *audio);
|
||||||
|
|
||||||
|
void reset_output_buffers(audio_data_t *data);
|
||||||
|
|
||||||
|
#endif /* __RPI_MICROPHONE_H__ */
|
447
RpiLedBars/res/src/cava/rpi_spectrum.c
Normal file
447
RpiLedBars/res/src/cava/rpi_spectrum.c
Normal file
@ -0,0 +1,447 @@
|
|||||||
|
#include "rpi_spectrum.h"
|
||||||
|
|
||||||
|
#include <fftw3.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "rpi_microphone.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int framerate;
|
||||||
|
int number_of_bars;
|
||||||
|
int lower_cut_off, upper_cut_off;
|
||||||
|
} param_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
double sens;
|
||||||
|
double gravity;
|
||||||
|
double integral;
|
||||||
|
double userEQ[128];
|
||||||
|
} config_t;
|
||||||
|
|
||||||
|
param_t param = {
|
||||||
|
.framerate = 60, .number_of_bars = 128, .lower_cut_off = 50, .upper_cut_off = 10000};
|
||||||
|
config_t conf = {.sens = 1, .gravity = 0, .integral = 0, .userEQ = {1}};
|
||||||
|
|
||||||
|
pthread_t microphoneListener;
|
||||||
|
|
||||||
|
audio_data_t audio;
|
||||||
|
|
||||||
|
fftw_complex *out_bass_l;
|
||||||
|
fftw_plan p_bass_l;
|
||||||
|
fftw_complex *out_mid_l;
|
||||||
|
fftw_plan p_mid_l;
|
||||||
|
fftw_complex *out_treble_l;
|
||||||
|
fftw_plan p_treble_l;
|
||||||
|
|
||||||
|
// input: init
|
||||||
|
int *bars_left;
|
||||||
|
double *temp_l;
|
||||||
|
|
||||||
|
int bass_cut_off = 150;
|
||||||
|
int treble_cut_off = 2500;
|
||||||
|
|
||||||
|
void init_plan(int bufferSize, double **in, double **in_raw, fftw_complex **out, fftw_plan *p);
|
||||||
|
|
||||||
|
void spectrum_start_bg_worker();
|
||||||
|
|
||||||
|
void spectrum_stop_bg_worker();
|
||||||
|
|
||||||
|
void spectrum_setup(char *audio_source) {
|
||||||
|
for (size_t i = 0; i < 128; i++) {
|
||||||
|
conf.userEQ[i] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&audio, 0, sizeof(audio));
|
||||||
|
|
||||||
|
audio.source = malloc(1 + strlen(audio_source));
|
||||||
|
strcpy(audio.source, audio_source);
|
||||||
|
|
||||||
|
audio.format = -1;
|
||||||
|
audio.rate = 0;
|
||||||
|
audio.FFTbassbufferSize = 4096;
|
||||||
|
audio.FFTmidbufferSize = 2048;
|
||||||
|
audio.FFTtreblebufferSize = 1024;
|
||||||
|
audio.terminate = 0;
|
||||||
|
audio.channels = 1;
|
||||||
|
audio.average = false;
|
||||||
|
audio.left = true;
|
||||||
|
audio.right = false;
|
||||||
|
audio.bass_index = 0;
|
||||||
|
audio.mid_index = 0;
|
||||||
|
audio.treble_index = 0;
|
||||||
|
audio.bass_multiplier = (double *)malloc(audio.FFTbassbufferSize * sizeof(double));
|
||||||
|
audio.mid_multiplier = (double *)malloc(audio.FFTmidbufferSize * sizeof(double));
|
||||||
|
audio.treble_multiplier = (double *)malloc(audio.FFTtreblebufferSize * sizeof(double));
|
||||||
|
|
||||||
|
temp_l = (double *)malloc((audio.FFTbassbufferSize / 2 + 1) * sizeof(double));
|
||||||
|
|
||||||
|
bars_left = (int *)malloc(256 * sizeof(int));
|
||||||
|
|
||||||
|
for (int i = 0; i < audio.FFTbassbufferSize; i++) {
|
||||||
|
audio.bass_multiplier[i] = 0.5 * (1 - cos(2 * M_PI * i / (audio.FFTbassbufferSize - 1)));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < audio.FFTmidbufferSize; i++) {
|
||||||
|
audio.mid_multiplier[i] = 0.5 * (1 - cos(2 * M_PI * i / (audio.FFTmidbufferSize - 1)));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < audio.FFTtreblebufferSize; i++) {
|
||||||
|
audio.treble_multiplier[i] = 0.5 * (1 - cos(2 * M_PI * i / (audio.FFTtreblebufferSize - 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// BASS
|
||||||
|
// audio.FFTbassbufferSize = audio.rate / 20; // audio.FFTbassbufferSize;
|
||||||
|
// audio.in_bass_l = fftw_alloc_real(audio.FFTbassbufferSize);
|
||||||
|
// audio.in_bass_l_raw = fftw_alloc_real(audio.FFTbassbufferSize);
|
||||||
|
|
||||||
|
// out_bass_l = fftw_alloc_complex(audio.FFTbassbufferSize / 2 + 1);
|
||||||
|
// memset(out_bass_l, 0, (audio.FFTbassbufferSize / 2 + 1) * sizeof(fftw_complex));
|
||||||
|
|
||||||
|
// p_bass_l =
|
||||||
|
// fftw_plan_dft_r2c_1d(audio.FFTbassbufferSize, audio.in_bass_l, out_bass_l, FFTW_MEASURE);
|
||||||
|
|
||||||
|
init_plan(audio.FFTbassbufferSize, &audio.in_bass_l, &audio.in_bass_l_raw, &out_bass_l,
|
||||||
|
&p_bass_l);
|
||||||
|
|
||||||
|
// MID
|
||||||
|
// audio.FFTmidbufferSize = audio.rate / bass_cut_off; // audio.FFTbassbufferSize;
|
||||||
|
// audio.in_mid_l = fftw_alloc_real(audio.FFTmidbufferSize);
|
||||||
|
// audio.in_mid_l_raw = fftw_alloc_real(audio.FFTmidbufferSize);
|
||||||
|
|
||||||
|
// out_mid_l = fftw_alloc_complex(audio.FFTmidbufferSize / 2 + 1);
|
||||||
|
// memset(out_mid_l, 0, (audio.FFTmidbufferSize / 2 + 1) * sizeof(fftw_complex));
|
||||||
|
|
||||||
|
// p_mid_l = fftw_plan_dft_r2c_1d(audio.FFTmidbufferSize, audio.in_mid_l, out_mid_l,
|
||||||
|
// FFTW_MEASURE);
|
||||||
|
|
||||||
|
init_plan(audio.FFTmidbufferSize, &audio.in_mid_l, &audio.in_mid_l_raw, &out_mid_l, &p_mid_l);
|
||||||
|
|
||||||
|
// TRIEBLE
|
||||||
|
// audio.FFTtreblebufferSize = audio.rate / treble_cut_off; // audio.FFTbassbufferSize;
|
||||||
|
// audio.in_treble_l = fftw_alloc_real(audio.FFTtreblebufferSize);
|
||||||
|
// audio.in_treble_l_raw = fftw_alloc_real(audio.FFTtreblebufferSize);
|
||||||
|
|
||||||
|
// out_treble_l = fftw_alloc_complex(audio.FFTtreblebufferSize / 2 + 1);
|
||||||
|
// memset(out_treble_l, 0, (audio.FFTtreblebufferSize / 2 + 1) * sizeof(fftw_complex));
|
||||||
|
|
||||||
|
// p_treble_l = fftw_plan_dft_r2c_1d(audio.FFTtreblebufferSize, audio.in_treble_l,
|
||||||
|
// out_treble_l,
|
||||||
|
// FFTW_MEASURE);
|
||||||
|
|
||||||
|
init_plan(audio.FFTtreblebufferSize, &audio.in_treble_l, &audio.in_treble_l_raw, &out_treble_l,
|
||||||
|
&p_treble_l);
|
||||||
|
|
||||||
|
reset_output_buffers(&audio);
|
||||||
|
|
||||||
|
spectrum_start_bg_worker();
|
||||||
|
}
|
||||||
|
|
||||||
|
void spectrum_start_bg_worker() {
|
||||||
|
pthread_mutex_init(&audio.lock, NULL);
|
||||||
|
if (pthread_create(µphoneListener, NULL, microphone_listen, (void *)&audio) < 0) {
|
||||||
|
perror("pthread_create");
|
||||||
|
}
|
||||||
|
|
||||||
|
int timeout_counter = 0;
|
||||||
|
while (audio.rate == 0) {
|
||||||
|
usleep(2000);
|
||||||
|
++timeout_counter;
|
||||||
|
if (timeout_counter > 2000) {
|
||||||
|
fprintf(stderr, "could not get rate and/or format, problems with audio thread? "
|
||||||
|
"quiting...\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void spectrum_stop_bg_worker() {
|
||||||
|
if (pthread_cancel(microphoneListener) != 0) {
|
||||||
|
perror("pthread_cancel");
|
||||||
|
}
|
||||||
|
if (pthread_join(microphoneListener, NULL) != 0) {
|
||||||
|
perror("pthread_join");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void spectrum_execute() {
|
||||||
|
int bars[256];
|
||||||
|
int bars_mem[256];
|
||||||
|
int bars_last[256];
|
||||||
|
int previous_frame[256];
|
||||||
|
int fall[256];
|
||||||
|
float bars_peak[256];
|
||||||
|
|
||||||
|
int height, lines, width, remainder, fp;
|
||||||
|
|
||||||
|
bool reloadConf = false;
|
||||||
|
|
||||||
|
for (int n = 0; n < 256; n++) {
|
||||||
|
bars_last[n] = 0;
|
||||||
|
previous_frame[n] = 0;
|
||||||
|
fall[n] = 0;
|
||||||
|
bars_peak[n] = 0;
|
||||||
|
bars_mem[n] = 0;
|
||||||
|
bars[n] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
width = 256;
|
||||||
|
height = UINT16_MAX;
|
||||||
|
|
||||||
|
// getting numbers of bars
|
||||||
|
int number_of_bars = param.number_of_bars;
|
||||||
|
|
||||||
|
if (number_of_bars > 256)
|
||||||
|
number_of_bars = 256; // cant have more than 256 bars
|
||||||
|
|
||||||
|
// process [smoothing]: calculate gravity
|
||||||
|
float g = conf.gravity * ((float)height / 2160) * pow((60 / (float)param.framerate), 2.5);
|
||||||
|
|
||||||
|
// calculate integral value, must be reduced with height
|
||||||
|
double integral = conf.integral;
|
||||||
|
if (height > 320)
|
||||||
|
integral = conf.integral * 1 / sqrt((log10((float)height / 10)));
|
||||||
|
|
||||||
|
// process: calculate cutoff frequencies and eq
|
||||||
|
|
||||||
|
double userEQ_keys_to_bars_ratio;
|
||||||
|
|
||||||
|
if (number_of_bars > 0) {
|
||||||
|
userEQ_keys_to_bars_ratio = (double)(((double)(number_of_bars < 128 ? number_of_bars : 128)) /
|
||||||
|
((double)number_of_bars));
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate frequency constant (used to distribute bars across the frequency band)
|
||||||
|
double frequency_constant = log10((float)param.lower_cut_off / (float)param.upper_cut_off) /
|
||||||
|
(1 / ((float)number_of_bars + 1) - 1);
|
||||||
|
|
||||||
|
float cut_off_frequency[256];
|
||||||
|
float upper_cut_off_frequency[256];
|
||||||
|
float relative_cut_off[256];
|
||||||
|
double center_frequencies[256];
|
||||||
|
int FFTbuffer_lower_cut_off[256];
|
||||||
|
int FFTbuffer_upper_cut_off[256];
|
||||||
|
double eq[256];
|
||||||
|
|
||||||
|
int bass_cut_off_bar = -1;
|
||||||
|
int treble_cut_off_bar = -1;
|
||||||
|
bool first_bar = true;
|
||||||
|
int first_treble_bar = 0;
|
||||||
|
int bar_buffer[number_of_bars + 1];
|
||||||
|
|
||||||
|
for (int n = 0; n < number_of_bars + 1; n++) {
|
||||||
|
double bar_distribution_coefficient = frequency_constant * (-1);
|
||||||
|
bar_distribution_coefficient +=
|
||||||
|
((float)n + 1) / ((float)number_of_bars + 1) * frequency_constant;
|
||||||
|
cut_off_frequency[n] = param.upper_cut_off * pow(10, bar_distribution_coefficient);
|
||||||
|
|
||||||
|
if (n > 0) {
|
||||||
|
if (cut_off_frequency[n - 1] >= cut_off_frequency[n] &&
|
||||||
|
cut_off_frequency[n - 1] > bass_cut_off)
|
||||||
|
cut_off_frequency[n] =
|
||||||
|
cut_off_frequency[n - 1] + (cut_off_frequency[n - 1] - cut_off_frequency[n - 2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
relative_cut_off[n] = cut_off_frequency[n] / (audio.rate / 2);
|
||||||
|
// remember nyquist!, per my calculations this should be rate/2
|
||||||
|
// and nyquist freq in M/2 but testing shows it is not...
|
||||||
|
// or maybe the nq freq is in M/4
|
||||||
|
|
||||||
|
eq[n] = pow(cut_off_frequency[n], 1);
|
||||||
|
|
||||||
|
// the numbers that come out of the FFT are very high
|
||||||
|
// the EQ is used to "normalize" them by dividing with this very huge number
|
||||||
|
eq[n] *= (float)height / pow(2, 28);
|
||||||
|
|
||||||
|
eq[n] *= conf.userEQ[(int)floor(((double)n) * userEQ_keys_to_bars_ratio)];
|
||||||
|
|
||||||
|
eq[n] /= log2(audio.FFTbassbufferSize);
|
||||||
|
|
||||||
|
if (cut_off_frequency[n] < bass_cut_off) {
|
||||||
|
// BASS
|
||||||
|
bar_buffer[n] = 1;
|
||||||
|
FFTbuffer_lower_cut_off[n] = relative_cut_off[n] * (audio.FFTbassbufferSize / 2);
|
||||||
|
bass_cut_off_bar++;
|
||||||
|
treble_cut_off_bar++;
|
||||||
|
if (bass_cut_off_bar > 0)
|
||||||
|
first_bar = false;
|
||||||
|
|
||||||
|
eq[n] *= log2(audio.FFTbassbufferSize);
|
||||||
|
} else if (cut_off_frequency[n] > bass_cut_off && cut_off_frequency[n] < treble_cut_off) {
|
||||||
|
// MID
|
||||||
|
bar_buffer[n] = 2;
|
||||||
|
FFTbuffer_lower_cut_off[n] = relative_cut_off[n] * (audio.FFTmidbufferSize / 2);
|
||||||
|
treble_cut_off_bar++;
|
||||||
|
if ((treble_cut_off_bar - bass_cut_off_bar) == 1) {
|
||||||
|
first_bar = true;
|
||||||
|
FFTbuffer_upper_cut_off[n - 1] = relative_cut_off[n] * (audio.FFTbassbufferSize / 2);
|
||||||
|
} else {
|
||||||
|
first_bar = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
eq[n] *= log2(audio.FFTmidbufferSize);
|
||||||
|
} else {
|
||||||
|
// TREBLE
|
||||||
|
bar_buffer[n] = 3;
|
||||||
|
FFTbuffer_lower_cut_off[n] = relative_cut_off[n] * (audio.FFTtreblebufferSize / 2);
|
||||||
|
first_treble_bar++;
|
||||||
|
if (first_treble_bar == 1) {
|
||||||
|
first_bar = true;
|
||||||
|
FFTbuffer_upper_cut_off[n - 1] = relative_cut_off[n] * (audio.FFTmidbufferSize / 2);
|
||||||
|
} else {
|
||||||
|
first_bar = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
eq[n] *= log2(audio.FFTtreblebufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n > 0) {
|
||||||
|
if (!first_bar) {
|
||||||
|
FFTbuffer_upper_cut_off[n - 1] = FFTbuffer_lower_cut_off[n] - 1;
|
||||||
|
|
||||||
|
// pushing the spectrum up if the exponential function gets "clumped" in the
|
||||||
|
// bass and caluclating new cut off frequencies
|
||||||
|
if (FFTbuffer_lower_cut_off[n] <= FFTbuffer_lower_cut_off[n - 1]) {
|
||||||
|
|
||||||
|
FFTbuffer_lower_cut_off[n] = FFTbuffer_lower_cut_off[n - 1] + 1;
|
||||||
|
FFTbuffer_upper_cut_off[n - 1] = FFTbuffer_lower_cut_off[n] - 1;
|
||||||
|
|
||||||
|
if (bar_buffer[n] == 1)
|
||||||
|
relative_cut_off[n] =
|
||||||
|
(float)(FFTbuffer_lower_cut_off[n]) / ((float)audio.FFTbassbufferSize / 2);
|
||||||
|
else if (bar_buffer[n] == 2)
|
||||||
|
relative_cut_off[n] =
|
||||||
|
(float)(FFTbuffer_lower_cut_off[n]) / ((float)audio.FFTmidbufferSize / 2);
|
||||||
|
else if (bar_buffer[n] == 3)
|
||||||
|
relative_cut_off[n] =
|
||||||
|
(float)(FFTbuffer_lower_cut_off[n]) / ((float)audio.FFTtreblebufferSize / 2);
|
||||||
|
|
||||||
|
cut_off_frequency[n] = relative_cut_off[n] * ((float)audio.rate / 2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (FFTbuffer_upper_cut_off[n - 1] <= FFTbuffer_lower_cut_off[n - 1])
|
||||||
|
FFTbuffer_upper_cut_off[n - 1] = FFTbuffer_lower_cut_off[n - 1] + 1;
|
||||||
|
}
|
||||||
|
upper_cut_off_frequency[n - 1] =
|
||||||
|
cut_off_frequency[n]; // high_relative_cut_off * ((float)audio.rate / 2);
|
||||||
|
center_frequencies[n - 1] =
|
||||||
|
pow((cut_off_frequency[n - 1] * upper_cut_off_frequency[n - 1]), 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// process: execute FFT and sort frequency bands
|
||||||
|
pthread_mutex_lock(&audio.lock);
|
||||||
|
fftw_execute(p_bass_l);
|
||||||
|
fftw_execute(p_mid_l);
|
||||||
|
fftw_execute(p_treble_l);
|
||||||
|
pthread_mutex_unlock(&audio.lock);
|
||||||
|
|
||||||
|
// process: separate frequency bands
|
||||||
|
for (int n = 0; n < number_of_bars; n++) {
|
||||||
|
|
||||||
|
temp_l[n] = 0;
|
||||||
|
|
||||||
|
// process: add upp FFT values within bands
|
||||||
|
for (int i = FFTbuffer_lower_cut_off[n]; i <= FFTbuffer_upper_cut_off[n]; i++) {
|
||||||
|
if (n <= bass_cut_off_bar) {
|
||||||
|
|
||||||
|
temp_l[n] += hypot(out_bass_l[i][0], out_bass_l[i][1]);
|
||||||
|
|
||||||
|
} else if (n > bass_cut_off_bar && n <= treble_cut_off_bar) {
|
||||||
|
|
||||||
|
temp_l[n] += hypot(out_mid_l[i][0], out_mid_l[i][1]);
|
||||||
|
|
||||||
|
} else if (n > treble_cut_off_bar) {
|
||||||
|
|
||||||
|
temp_l[n] += hypot(out_treble_l[i][0], out_treble_l[i][1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getting average multiply with sens and eq
|
||||||
|
temp_l[n] /= FFTbuffer_upper_cut_off[n] - FFTbuffer_lower_cut_off[n] + 1;
|
||||||
|
temp_l[n] *= conf.sens * eq[n];
|
||||||
|
|
||||||
|
bars_left[n] = temp_l[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
// process [filter]
|
||||||
|
|
||||||
|
// if (p.monstercat) {
|
||||||
|
// if (p.stereo) {
|
||||||
|
// bars_left = monstercat_filter(bars_left, number_of_bars / 2, p.waves, p.monstercat);
|
||||||
|
// bars_right = monstercat_filter(bars_right, number_of_bars / 2, p.waves, p.monstercat);
|
||||||
|
// } else {
|
||||||
|
// bars_left = monstercat_filter(bars_left, number_of_bars, p.waves, p.monstercat);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// processing signal
|
||||||
|
|
||||||
|
// bool senselow = true;
|
||||||
|
|
||||||
|
for (int n = 0; n < number_of_bars; n++) {
|
||||||
|
// // mirroring stereo channels
|
||||||
|
// if (p.stereo) {
|
||||||
|
// if (n < number_of_bars / 2) {
|
||||||
|
// bars[n] = bars_left[number_of_bars / 2 - n - 1];
|
||||||
|
// } else {
|
||||||
|
// bars[n] = bars_right[n - number_of_bars / 2];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// } else {
|
||||||
|
bars[n] = bars_left[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
// process [smoothing]: falloff
|
||||||
|
// if (g > 0) {
|
||||||
|
// if (bars[n] < bars_last[n]) {
|
||||||
|
// bars[n] = bars_peak[n] - (g * fall[n] * fall[n]);
|
||||||
|
// if (bars[n] < 0)
|
||||||
|
// bars[n] = 0;
|
||||||
|
// fall[n]++;
|
||||||
|
// } else {
|
||||||
|
// bars_peak[n] = bars[n];
|
||||||
|
// fall[n] = 0;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// bars_last[n] = bars[n];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// process [smoothing]: integral
|
||||||
|
// if (p.integral > 0) {
|
||||||
|
// bars[n] = bars_mem[n] * integral + bars[n];
|
||||||
|
// bars_mem[n] = bars[n];
|
||||||
|
|
||||||
|
// int diff = height - bars[n];
|
||||||
|
// if (diff < 0)
|
||||||
|
// diff = 0;
|
||||||
|
// double div = 1 / (diff + 1);
|
||||||
|
// // bars[n] = bars[n] - pow(div, 10) * (height + 1);
|
||||||
|
// bars_mem[n] = bars_mem[n] * (1 - div / 20);
|
||||||
|
// }
|
||||||
|
|
||||||
|
memcpy(previous_frame, bars, 256 * sizeof(int));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < number_of_bars; i++) {
|
||||||
|
printf("%5d, ", bars[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int *spectrum_display_bars() { return bars_left; }
|
||||||
|
|
||||||
|
void init_plan(int bufferSize, double **in, double **in_raw, fftw_complex **out, fftw_plan *p) {
|
||||||
|
*in = fftw_alloc_real(bufferSize);
|
||||||
|
*in_raw = fftw_alloc_real(bufferSize);
|
||||||
|
|
||||||
|
*out = fftw_alloc_complex(bufferSize / 2 + 1);
|
||||||
|
memset(*out, 0, (bufferSize / 2 + 1) * sizeof(fftw_complex));
|
||||||
|
|
||||||
|
*p = fftw_plan_dft_r2c_1d(bufferSize, *in, *out, FFTW_MEASURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compute_param() {}
|
||||||
|
|
||||||
|
void compute_config() {}
|
14
RpiLedBars/res/src/cava/rpi_spectrum.h
Normal file
14
RpiLedBars/res/src/cava/rpi_spectrum.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#if !defined(__RPI_SPECTRUM_H__)
|
||||||
|
#define __RPI_SPECTRUM_H__
|
||||||
|
|
||||||
|
void spectrum_setup(char *audio_source);
|
||||||
|
|
||||||
|
void spectrum_start_bg_worker();
|
||||||
|
|
||||||
|
void spectrum_stop_bg_worker();
|
||||||
|
|
||||||
|
void spectrum_execute();
|
||||||
|
|
||||||
|
int *spectrum_get_bars();
|
||||||
|
|
||||||
|
#endif /* __RPI_SPECTRUM_H__ */
|
15
RpiLedBars/res/src/output/raw.c
Normal file
15
RpiLedBars/res/src/output/raw.c
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include <math.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int16_t buf_16;
|
||||||
|
int8_t buf_8;
|
||||||
|
|
||||||
|
int buffer[200];
|
||||||
|
|
||||||
|
int print_raw_out(int bars_count, int fd, int is_binary, int bit_format, int ascii_range,
|
||||||
|
char bar_delim, char frame_delim, int const f[200]) {
|
||||||
|
memcpy(buffer, f, sizeof(int) * bars_count);
|
||||||
|
return 0;
|
||||||
|
}
|
2
RpiLedBars/res/src/output/raw.h
Normal file
2
RpiLedBars/res/src/output/raw.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
int print_raw_out(int bars_count, int fd, int is_binary, int bit_format, int ascii_range,
|
||||||
|
char bar_delim, char frame_delim, int const f[200]);
|
93
RpiLedBars/res/src/tmp/rpi_cava.c
Normal file
93
RpiLedBars/res/src/tmp/rpi_cava.c
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#include "rpi_cava.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pid_t cavaPid;
|
||||||
|
pthread_t fifoReader;
|
||||||
|
int cavaFifo;
|
||||||
|
uint16_t buffer[20];
|
||||||
|
|
||||||
|
static void *fifo_to_buffer(void *arg);
|
||||||
|
|
||||||
|
void setup_cava() {}
|
||||||
|
|
||||||
|
void close_cava() { stop_cava_bg_worker(); }
|
||||||
|
|
||||||
|
void start_cava_bg_worker() {
|
||||||
|
if ((cavaPid = fork()) == -1) {
|
||||||
|
perror("fork");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cavaPid == 0) {
|
||||||
|
/* Child process*/
|
||||||
|
char *args[] = {"/usr/local/bin/cava", "-p", "/home/pi/LedBars/RpiLedBars/cava_config", NULL};
|
||||||
|
|
||||||
|
if (execv(args[0], args) != 0) {
|
||||||
|
perror("execv");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
sleep(1);
|
||||||
|
if ((cavaFifo = open("/tmp/cava_output", O_RDONLY)) < 0) {
|
||||||
|
perror("open");
|
||||||
|
close_cava();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_create(&fifoReader, NULL, fifo_to_buffer, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop_cava_bg_worker() {
|
||||||
|
if (pthread_cancel(fifoReader) != 0) {
|
||||||
|
perror("pthread_cancel");
|
||||||
|
}
|
||||||
|
if (pthread_join(fifoReader, NULL) != 0) {
|
||||||
|
perror("pthread_join");
|
||||||
|
}
|
||||||
|
close(cavaFifo);
|
||||||
|
kill(cavaPid, SIGTERM);
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_cava_buffer(uint16_t buffer_dst[20]) {
|
||||||
|
memcpy(buffer_dst, buffer, 40);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *fifo_to_buffer(void *arg) {
|
||||||
|
while (1) {
|
||||||
|
int totalRead = 0;
|
||||||
|
while (totalRead < 40) {
|
||||||
|
int nread;
|
||||||
|
nread = read(cavaFifo, ((uint8_t *)buffer) + totalRead, 40 - totalRead);
|
||||||
|
if (nread >= 0) {
|
||||||
|
totalRead += nread;
|
||||||
|
} else {
|
||||||
|
if (errno != EAGAIN) {
|
||||||
|
perror("read");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("data[%d] : ", totalRead);
|
||||||
|
for (size_t i = 0; i < 20; i++) {
|
||||||
|
printf("%5u;", buffer[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
16
RpiLedBars/res/src/tmp/rpi_cava.h
Normal file
16
RpiLedBars/res/src/tmp/rpi_cava.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#if !defined(__RPI_CAVA_H__)
|
||||||
|
#define __RPI_CAVA_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void setup_cava();
|
||||||
|
|
||||||
|
int get_cava_buffer(uint16_t buffer_dst[20]);
|
||||||
|
|
||||||
|
void close_cava();
|
||||||
|
|
||||||
|
void start_cava_bg_worker();
|
||||||
|
|
||||||
|
void stop_cava_bg_worker();
|
||||||
|
|
||||||
|
#endif /* __RPI_CAVA_H__ */
|
103
RpiLedBars/res/src/tmp/rpi_microphone.c
Normal file
103
RpiLedBars/res/src/tmp/rpi_microphone.c
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#include "rpi_microphone.h"
|
||||||
|
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
|
#include <fftw3.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define CHANNELS_COUNT 1
|
||||||
|
#define SAMPLE_RATE 44100
|
||||||
|
|
||||||
|
snd_pcm_t *capture_handle;
|
||||||
|
char const *deviceName = "plughw:1";
|
||||||
|
|
||||||
|
void setup_microphone() {
|
||||||
|
int err;
|
||||||
|
snd_pcm_hw_params_t *hw_params;
|
||||||
|
unsigned int rate = 44100;
|
||||||
|
|
||||||
|
if ((err = snd_pcm_open(&capture_handle, deviceName, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
|
||||||
|
fprintf(stderr, "cannot open audio device %s (%s)\n", deviceName, snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
|
||||||
|
fprintf(stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0) {
|
||||||
|
fprintf(stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_set_access(capture_handle, hw_params,
|
||||||
|
SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
|
||||||
|
fprintf(stderr, "cannot set access type (%s)\n", snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
|
||||||
|
fprintf(stderr, "cannot set sample format (%s)\n", snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &rate, 0)) < 0) {
|
||||||
|
fprintf(stderr, "cannot set sample rate (%s)\n", snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, 2)) < 0) {
|
||||||
|
fprintf(stderr, "cannot set channel count (%s)\n", snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0) {
|
||||||
|
fprintf(stderr, "cannot set parameters (%s)\n", snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_pcm_hw_params_free(hw_params);
|
||||||
|
|
||||||
|
if ((err = snd_pcm_prepare(capture_handle)) < 0) {
|
||||||
|
fprintf(stderr, "cannot prepare audio interface for use (%s)\n", snd_strerror(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BUFFERSIZE 1024
|
||||||
|
short buf[BUFFERSIZE];
|
||||||
|
|
||||||
|
void read_microphone() {
|
||||||
|
int err;
|
||||||
|
short max = SHRT_MIN;
|
||||||
|
|
||||||
|
if ((err = snd_pcm_readi(capture_handle, buf, BUFFERSIZE)) != BUFFERSIZE) {
|
||||||
|
fprintf(stderr, "read from audio interface failed (%s)\n", snd_strerror(err));
|
||||||
|
} else {
|
||||||
|
for (size_t i = 0; i < BUFFERSIZE; ++i) {
|
||||||
|
if (buf[i] > max) {
|
||||||
|
max = buf[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("%d, ", max);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fft() {
|
||||||
|
// fftw_complex *in, *out;
|
||||||
|
// fftw_plan my_plan;
|
||||||
|
// in = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * N);
|
||||||
|
// out = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * N);
|
||||||
|
// my_plan = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
|
||||||
|
// fftw_execute(my_plan); /* repeat as needed */
|
||||||
|
// fftw_destroy_plan(my_plan);
|
||||||
|
// fftw_free(in);
|
||||||
|
// fftw_free(out);
|
||||||
|
// return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_microphone() { snd_pcm_close(capture_handle); }
|
10
RpiLedBars/res/src/tmp/rpi_microphone.h
Normal file
10
RpiLedBars/res/src/tmp/rpi_microphone.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#if !defined(__RPI_MICROPHONE_H__)
|
||||||
|
#define __RPI_MICROPHONE_H__
|
||||||
|
|
||||||
|
void setup_microphone();
|
||||||
|
|
||||||
|
void read_microphone();
|
||||||
|
|
||||||
|
void close_microphone();
|
||||||
|
|
||||||
|
#endif /* __RPI_MICROPHONE_H__ */
|
@ -1 +0,0 @@
|
|||||||
#include "artnet.h"
|
|
@ -1,4 +0,0 @@
|
|||||||
#if !defined(__ARTNET_H__)
|
|
||||||
#define __ARTNET_H__
|
|
||||||
|
|
||||||
#endif // __ARTNET_H__
|
|
49
RpiLedBars/src/drivers/common.c
Normal file
49
RpiLedBars/src/drivers/common.c
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// Use mmap to obtain virtual address, given physical
|
||||||
|
void *map_periph(MEM_MAP *mp, void *phys, int size) {
|
||||||
|
mp->phys = phys;
|
||||||
|
mp->size = PAGE_ROUNDUP(size);
|
||||||
|
mp->bus = (void *)((uint32_t)phys - PHYS_REG_BASE + BUS_REG_BASE);
|
||||||
|
mp->virt = map_segment(phys, mp->size);
|
||||||
|
return (mp->virt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free mapped peripheral or memory
|
||||||
|
void unmap_periph_mem(MEM_MAP *mp) {
|
||||||
|
if (mp) {
|
||||||
|
unmap_segment(mp->virt, mp->size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- VIRTUAL MEMORY -----
|
||||||
|
|
||||||
|
// Get virtual memory segment for peripheral regs or physical mem
|
||||||
|
void *map_segment(void *addr, int size) {
|
||||||
|
int fd;
|
||||||
|
void *mem;
|
||||||
|
|
||||||
|
size = PAGE_ROUNDUP(size);
|
||||||
|
if ((fd = open("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC)) < 0)
|
||||||
|
fprintf(stderr, "Error: can't open /dev/mem, run using sudo\n");
|
||||||
|
mem = mmap(0, size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, (uint32_t)addr);
|
||||||
|
close(fd);
|
||||||
|
#if DEBUG
|
||||||
|
printf("Map %p -> %p\n", (void *)addr, mem);
|
||||||
|
#endif
|
||||||
|
if (mem == MAP_FAILED)
|
||||||
|
fprintf(stderr, "Error: can't map memory\n");
|
||||||
|
return (mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free mapped memory
|
||||||
|
void unmap_segment(void *mem, int size) {
|
||||||
|
if (mem)
|
||||||
|
munmap(mem, PAGE_ROUNDUP(size));
|
||||||
|
}
|
50
RpiLedBars/src/drivers/common.h
Normal file
50
RpiLedBars/src/drivers/common.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#if !defined(__COMMON_H__)
|
||||||
|
#define __COMMON_H__
|
||||||
|
|
||||||
|
// Location of peripheral registers in physical memory
|
||||||
|
#define PHYS_REG_BASE PI_23_REG_BASE
|
||||||
|
#define PI_01_REG_BASE 0x20000000 // Pi Zero or 1
|
||||||
|
#define PI_23_REG_BASE 0x3F000000 // Pi 2 or 3
|
||||||
|
#define PI_4_REG_BASE 0xFE000000 // Pi 4
|
||||||
|
|
||||||
|
#define CLOCK_HZ 250000000 // Pi 2 - 4
|
||||||
|
//#define CLOCK_HZ 400000000 // Pi Zero
|
||||||
|
|
||||||
|
// Location of peripheral registers in bus memory
|
||||||
|
#define BUS_REG_BASE 0x7E000000
|
||||||
|
|
||||||
|
// Get virtual 8 and 32-bit pointers to register
|
||||||
|
#define REG8(m, x) ((volatile uint8_t *)((uint32_t)(m.virt) + (uint32_t)(x)))
|
||||||
|
#define REG32(m, x) ((volatile uint32_t *)((uint32_t)(m.virt) + (uint32_t)(x)))
|
||||||
|
// Get bus address of register
|
||||||
|
#define REG_BUS_ADDR(m, x) ((uint32_t)(m.bus) + (uint32_t)(x))
|
||||||
|
// Convert uncached memory virtual address to bus address
|
||||||
|
#define MEM_BUS_ADDR(mp, a) ((uint32_t)a - (uint32_t)mp->virt + (uint32_t)mp->bus)
|
||||||
|
// Convert bus address to physical address (for mmap)
|
||||||
|
#define BUS_PHYS_ADDR(a) ((void *)((uint32_t)(a) & ~0xC0000000))
|
||||||
|
|
||||||
|
// Size of memory page
|
||||||
|
#define PAGE_SIZE 0x1000
|
||||||
|
// Round up to nearest page
|
||||||
|
#define PAGE_ROUNDUP(n) ((n) % PAGE_SIZE == 0 ? (n) : ((n) + PAGE_SIZE) & ~(PAGE_SIZE - 1))
|
||||||
|
|
||||||
|
// Structure for mapped peripheral or memory
|
||||||
|
typedef struct {
|
||||||
|
int fd, // File descriptor
|
||||||
|
h, // Memory handle
|
||||||
|
size; // Memory size
|
||||||
|
void *bus, // Bus address
|
||||||
|
*virt, // Virtual address
|
||||||
|
*phys; // Physical address
|
||||||
|
} MEM_MAP;
|
||||||
|
|
||||||
|
// Use mmap to obtain virtual address, given physical
|
||||||
|
void *map_periph(MEM_MAP *mp, void *phys, int size);
|
||||||
|
|
||||||
|
// Free mapped peripheral or memory
|
||||||
|
void unmap_periph_mem(MEM_MAP *mp);
|
||||||
|
|
||||||
|
void *map_segment(void *addr, int size);
|
||||||
|
void unmap_segment(void *addr, int size);
|
||||||
|
|
||||||
|
#endif // __COMMON_H__
|
90
RpiLedBars/src/drivers/dma/rpi_dma.c
Normal file
90
RpiLedBars/src/drivers/dma/rpi_dma.c
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#include "rpi_dma.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "rpi_videocore.h"
|
||||||
|
|
||||||
|
// DMA channels and data requests
|
||||||
|
#define DMA_SMI_DREQ 4
|
||||||
|
#define DMA_PWM_DREQ 5
|
||||||
|
#define DMA_SPI_TX_DREQ 6
|
||||||
|
#define DMA_SPI_RX_DREQ 7
|
||||||
|
#define DMA_BASE (PHYS_REG_BASE + 0x007000)
|
||||||
|
// DMA register addresses offset by 0x100 * chan_num
|
||||||
|
#define DMA_CS 0x00
|
||||||
|
#define DMA_CONBLK_AD 0x04
|
||||||
|
#define DMA_TI 0x08
|
||||||
|
#define DMA_SRCE_AD 0x0c
|
||||||
|
#define DMA_DEST_AD 0x10
|
||||||
|
#define DMA_TXFR_LEN 0x14
|
||||||
|
#define DMA_STRIDE 0x18
|
||||||
|
#define DMA_NEXTCONBK 0x1c
|
||||||
|
#define DMA_DEBUG 0x20
|
||||||
|
#define DMA_REG(ch, r) ((r) == DMA_ENABLE ? DMA_ENABLE : (ch)*0x100 + (r))
|
||||||
|
#define DMA_ENABLE 0xff0
|
||||||
|
// DMA register values
|
||||||
|
#define DMA_WAIT_RESP (1 << 3)
|
||||||
|
#define DMA_CB_DEST_INC (1 << 4)
|
||||||
|
#define DMA_DEST_DREQ (1 << 6)
|
||||||
|
#define DMA_CB_SRCE_INC (1 << 8)
|
||||||
|
#define DMA_SRCE_DREQ (1 << 10)
|
||||||
|
#define DMA_PRIORITY(n) ((n) << 16)
|
||||||
|
|
||||||
|
// Virtual memory pointers to acceess GPIO, DMA and PWM from user space
|
||||||
|
MEM_MAP dma_regs;
|
||||||
|
|
||||||
|
char *dma_regstrs[] = {"DMA CS", "CB_AD", "TI", "SRCE_AD", "DEST_AD",
|
||||||
|
"TFR_LEN", "STRIDE", "NEXT_CB", "DEBUG", ""};
|
||||||
|
|
||||||
|
void dma_setup(MEM_MAP *mp, int chan, int nsamp, uint8_t **txdata, int offset, uint32_t dest_ad) {
|
||||||
|
map_periph(&dma_regs, (void *)DMA_BASE, PAGE_SIZE);
|
||||||
|
|
||||||
|
DMA_CB *cbs = mp->virt;
|
||||||
|
|
||||||
|
*txdata = (uint8_t *)(cbs + offset);
|
||||||
|
enable_dma(chan);
|
||||||
|
cbs[0].ti = DMA_DEST_DREQ | (DMA_SMI_DREQ << 16) | DMA_CB_SRCE_INC | DMA_WAIT_RESP;
|
||||||
|
cbs[0].tfr_len = nsamp;
|
||||||
|
cbs[0].srce_ad = MEM_BUS_ADDR(mp, *txdata);
|
||||||
|
cbs[0].dest_ad = dest_ad;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dma_close() { unmap_periph_mem(&dma_regs); }
|
||||||
|
|
||||||
|
// Enable and reset DMA
|
||||||
|
void enable_dma(int chan) {
|
||||||
|
*REG32(dma_regs, DMA_ENABLE) |= (1 << chan);
|
||||||
|
*REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 << 31;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start DMA, given first control block
|
||||||
|
void start_dma(MEM_MAP *mp, int chan, DMA_CB *cbp, uint32_t csval) {
|
||||||
|
*REG32(dma_regs, DMA_REG(chan, DMA_CONBLK_AD)) = MEM_BUS_ADDR(mp, cbp);
|
||||||
|
*REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 2; // Clear 'end' flag
|
||||||
|
*REG32(dma_regs, DMA_REG(chan, DMA_DEBUG)) = 7; // Clear error bits
|
||||||
|
*REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 | csval; // Start DMA
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return remaining transfer length
|
||||||
|
uint32_t dma_transfer_len(int chan) { return (*REG32(dma_regs, DMA_REG(chan, DMA_TXFR_LEN))); }
|
||||||
|
|
||||||
|
// Check if DMA is active
|
||||||
|
uint32_t dma_active(int chan) { return ((*REG32(dma_regs, DMA_REG(chan, DMA_CS))) & 1); }
|
||||||
|
|
||||||
|
// Halt current DMA operation by resetting controller
|
||||||
|
void stop_dma(int chan) {
|
||||||
|
if (dma_regs.virt)
|
||||||
|
*REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 << 31;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display DMA registers
|
||||||
|
void disp_dma(int chan) {
|
||||||
|
volatile uint32_t *p = REG32(dma_regs, DMA_REG(chan, DMA_CS));
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (dma_regstrs[i][0]) {
|
||||||
|
printf("%-7s %08X ", dma_regstrs[i++], *p++);
|
||||||
|
if (i % 5 == 0 || dma_regstrs[i][0] == 0)
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
33
RpiLedBars/src/drivers/dma/rpi_dma.h
Normal file
33
RpiLedBars/src/drivers/dma/rpi_dma.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#if !defined(__RPI_DMA_H__)
|
||||||
|
#define __RPI_DMA_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
#define DMA_CHAN_A 10
|
||||||
|
#define DMA_CHAN_B 11
|
||||||
|
|
||||||
|
// DMA control block (must be 32-byte aligned)
|
||||||
|
typedef struct {
|
||||||
|
uint32_t ti, // Transfer info
|
||||||
|
srce_ad, // Source address
|
||||||
|
dest_ad, // Destination address
|
||||||
|
tfr_len, // Transfer length
|
||||||
|
stride, // Transfer stride
|
||||||
|
next_cb, // Next control block
|
||||||
|
debug, // Debug register, zero in control block
|
||||||
|
unused;
|
||||||
|
} DMA_CB __attribute__((aligned(32)));
|
||||||
|
|
||||||
|
void dma_setup(MEM_MAP *mp, int chan, int nsamp, uint8_t **txdata, int offset, uint32_t dest_ad);
|
||||||
|
void dma_close();
|
||||||
|
|
||||||
|
void enable_dma(int chan);
|
||||||
|
void start_dma(MEM_MAP *mp, int chan, DMA_CB *cbp, uint32_t csval);
|
||||||
|
uint32_t dma_transfer_len(int chan);
|
||||||
|
uint32_t dma_active(int chan);
|
||||||
|
void stop_dma(int chan);
|
||||||
|
void disp_dma(int chan);
|
||||||
|
|
||||||
|
#endif // __RPI_DMA_H__
|
125
RpiLedBars/src/drivers/dma/rpi_videocore.c
Normal file
125
RpiLedBars/src/drivers/dma/rpi_videocore.c
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
#include "rpi_videocore.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// Mailbox command/response structure
|
||||||
|
typedef struct {
|
||||||
|
uint32_t len, // Overall length (bytes)
|
||||||
|
req, // Zero for request, 1<<31 for response
|
||||||
|
tag, // Command number
|
||||||
|
blen, // Buffer length (bytes)
|
||||||
|
dlen; // Data length (bytes)
|
||||||
|
uint32_t uints[32 - 5]; // Data (108 bytes maximum)
|
||||||
|
} VC_MSG __attribute__((aligned(16)));
|
||||||
|
|
||||||
|
void disp_vc_msg(VC_MSG *msgp);
|
||||||
|
|
||||||
|
int open_mbox(void);
|
||||||
|
void close_mbox(int fd);
|
||||||
|
uint32_t msg_mbox(int fd, VC_MSG *msgp);
|
||||||
|
void *map_uncached_mem(MEM_MAP *mp, int size);
|
||||||
|
|
||||||
|
void videocore_setup(MEM_MAP *mp, int size) { map_uncached_mem(mp, size); }
|
||||||
|
void videocore_close(MEM_MAP *mp) {
|
||||||
|
unmap_periph_mem(mp);
|
||||||
|
if (mp->fd) {
|
||||||
|
unlock_vc_mem(mp->fd, mp->h);
|
||||||
|
free_vc_mem(mp->fd, mp->h);
|
||||||
|
close_mbox(mp->fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate uncached memory, get bus & phys addresses
|
||||||
|
void *map_uncached_mem(MEM_MAP *mp, int size) {
|
||||||
|
void *ret;
|
||||||
|
mp->size = PAGE_ROUNDUP(size);
|
||||||
|
mp->fd = open_mbox();
|
||||||
|
ret = (mp->h = alloc_vc_mem(mp->fd, mp->size, DMA_MEM_FLAGS)) > 0 &&
|
||||||
|
(mp->bus = lock_vc_mem(mp->fd, mp->h)) != 0 &&
|
||||||
|
(mp->virt = map_segment(BUS_PHYS_ADDR(mp->bus), mp->size)) != 0
|
||||||
|
? mp->virt
|
||||||
|
: 0;
|
||||||
|
printf("VC mem handle %u, phys %p, virt %p\n", mp->h, mp->bus, mp->virt);
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate memory on PAGE_SIZE boundary, return handle
|
||||||
|
uint32_t alloc_vc_mem(int fd, uint32_t size, VC_ALLOC_FLAGS flags) {
|
||||||
|
VC_MSG msg = {
|
||||||
|
.tag = 0x3000c, .blen = 12, .dlen = 12, .uints = {PAGE_ROUNDUP(size), PAGE_SIZE, flags}};
|
||||||
|
return (msg_mbox(fd, &msg));
|
||||||
|
}
|
||||||
|
// Lock allocated memory, return bus address
|
||||||
|
void *lock_vc_mem(int fd, int h) {
|
||||||
|
VC_MSG msg = {.tag = 0x3000d, .blen = 4, .dlen = 4, .uints = {h}};
|
||||||
|
return (h ? (void *)msg_mbox(fd, &msg) : 0);
|
||||||
|
}
|
||||||
|
// Unlock allocated memory
|
||||||
|
uint32_t unlock_vc_mem(int fd, int h) {
|
||||||
|
VC_MSG msg = {.tag = 0x3000e, .blen = 4, .dlen = 4, .uints = {h}};
|
||||||
|
return (h ? msg_mbox(fd, &msg) : 0);
|
||||||
|
}
|
||||||
|
// Free memory
|
||||||
|
uint32_t free_vc_mem(int fd, int h) {
|
||||||
|
VC_MSG msg = {.tag = 0x3000f, .blen = 4, .dlen = 4, .uints = {h}};
|
||||||
|
return (h ? msg_mbox(fd, &msg) : 0);
|
||||||
|
}
|
||||||
|
uint32_t set_vc_clock(int fd, int id, uint32_t freq) {
|
||||||
|
VC_MSG msg1 = {.tag = 0x38001, .blen = 8, .dlen = 8, .uints = {id, 1}};
|
||||||
|
VC_MSG msg2 = {.tag = 0x38002, .blen = 12, .dlen = 12, .uints = {id, freq, 0}};
|
||||||
|
msg_mbox(fd, &msg1);
|
||||||
|
disp_vc_msg(&msg1);
|
||||||
|
msg_mbox(fd, &msg2);
|
||||||
|
disp_vc_msg(&msg2);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display mailbox message
|
||||||
|
void disp_vc_msg(VC_MSG *msgp) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
printf("VC msg len=%X, req=%X, tag=%X, blen=%x, dlen=%x, data ", msgp->len, msgp->req, msgp->tag,
|
||||||
|
msgp->blen, msgp->dlen);
|
||||||
|
for (i = 0; i < msgp->blen / 4; i++)
|
||||||
|
printf("%08X ", msgp->uints[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open mailbox interface, return file descriptor
|
||||||
|
int open_mbox(void) {
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if ((fd = open("/dev/vcio", 0)) < 0)
|
||||||
|
fprintf(stderr, "Error: can't open VC mailbox\n");
|
||||||
|
return (fd);
|
||||||
|
}
|
||||||
|
// Close mailbox interface
|
||||||
|
void close_mbox(int fd) {
|
||||||
|
if (fd >= 0)
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send message to mailbox, return first response int, 0 if error
|
||||||
|
uint32_t msg_mbox(int fd, VC_MSG *msgp) {
|
||||||
|
uint32_t ret = 0, i;
|
||||||
|
|
||||||
|
for (i = msgp->dlen / 4; i <= msgp->blen / 4; i += 4)
|
||||||
|
msgp->uints[i++] = 0;
|
||||||
|
msgp->len = (msgp->blen + 6) * 4;
|
||||||
|
msgp->req = 0;
|
||||||
|
if (ioctl(fd, _IOWR(100, 0, void *), msgp) < 0)
|
||||||
|
printf("VC IOCTL failed\n");
|
||||||
|
else if ((msgp->req & 0x80000000) == 0)
|
||||||
|
printf("VC IOCTL error\n");
|
||||||
|
else if (msgp->req == 0x80000001)
|
||||||
|
printf("VC IOCTL partial error\n");
|
||||||
|
else
|
||||||
|
ret = msgp->uints[0];
|
||||||
|
#if DEBUG
|
||||||
|
disp_vc_msg(msgp);
|
||||||
|
#endif
|
||||||
|
return (ret);
|
||||||
|
}
|
33
RpiLedBars/src/drivers/dma/rpi_videocore.h
Normal file
33
RpiLedBars/src/drivers/dma/rpi_videocore.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#if !defined(__RPI_VIDEOCORE_H__)
|
||||||
|
#define __RPI_VIDEOCORE_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
// Videocore mailbox memory allocation flags, see:
|
||||||
|
// https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
|
||||||
|
typedef enum {
|
||||||
|
MEM_FLAG_DISCARDABLE = 1 << 0, // can be resized to 0 at any time. Use for cached data
|
||||||
|
MEM_FLAG_NORMAL = 0 << 2, // normal allocating alias. Don't use from ARM
|
||||||
|
MEM_FLAG_DIRECT = 1 << 2, // 0xC alias uncached
|
||||||
|
MEM_FLAG_COHERENT = 2 << 2, // 0x8 alias. Non-allocating in L2 but coherent
|
||||||
|
MEM_FLAG_ZERO = 1 << 4, // initialise buffer to all zeros
|
||||||
|
MEM_FLAG_NO_INIT = 1 << 5, // don't initialise (default is initialise to all ones)
|
||||||
|
MEM_FLAG_HINT_PERMALOCK = 1 << 6, // Likely to be locked for long periods of time
|
||||||
|
MEM_FLAG_L1_NONALLOCATING = (MEM_FLAG_DIRECT | MEM_FLAG_COHERENT) // Allocating in L2
|
||||||
|
} VC_ALLOC_FLAGS;
|
||||||
|
|
||||||
|
// VC flags for unchached DMA memory
|
||||||
|
#define DMA_MEM_FLAGS (MEM_FLAG_DIRECT | MEM_FLAG_ZERO)
|
||||||
|
|
||||||
|
void videocore_setup(MEM_MAP *mp, int size);
|
||||||
|
void videocore_close(MEM_MAP *mp);
|
||||||
|
|
||||||
|
uint32_t alloc_vc_mem(int fd, uint32_t size, VC_ALLOC_FLAGS flags);
|
||||||
|
void *lock_vc_mem(int fd, int h);
|
||||||
|
uint32_t unlock_vc_mem(int fd, int h);
|
||||||
|
uint32_t free_vc_mem(int fd, int h);
|
||||||
|
uint32_t set_vc_clock(int fd, int id, uint32_t freq);
|
||||||
|
|
||||||
|
#endif // __RPI_VIDEOCORE_H__
|
75
RpiLedBars/src/drivers/gpio/rpi_gpio.c
Normal file
75
RpiLedBars/src/drivers/gpio/rpi_gpio.c
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#include "rpi_gpio.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
// GPIO register definitions
|
||||||
|
#define GPIO_BASE (PHYS_REG_BASE + 0x200000)
|
||||||
|
#define GPIO_MODE0 0x00
|
||||||
|
#define GPIO_SET0 0x1c
|
||||||
|
#define GPIO_CLR0 0x28
|
||||||
|
#define GPIO_LEV0 0x34
|
||||||
|
#define GPIO_GPPUD 0x94
|
||||||
|
#define GPIO_GPPUDCLK0 0x98
|
||||||
|
|
||||||
|
#define GPIO_MODE_STRS "IN", "OUT", "ALT5", "ALT4", "ALT0", "ALT1", "ALT2", "ALT3"
|
||||||
|
|
||||||
|
// Virtual memory pointers to acceess GPIO, DMA and PWM from user space
|
||||||
|
MEM_MAP gpio_regs;
|
||||||
|
|
||||||
|
char *gpio_mode_strs[] = {GPIO_MODE_STRS};
|
||||||
|
|
||||||
|
// definitions
|
||||||
|
|
||||||
|
void gpio_setup() { map_periph(&gpio_regs, (void *)GPIO_BASE, PAGE_SIZE); }
|
||||||
|
|
||||||
|
void gpio_close() { unmap_periph_mem(&gpio_regs); }
|
||||||
|
|
||||||
|
// Set input or output with pullups
|
||||||
|
void gpio_set(int pin, int mode, int pull) {
|
||||||
|
gpio_mode(pin, mode);
|
||||||
|
gpio_pull(pin, pull);
|
||||||
|
}
|
||||||
|
// Set I/P pullup or pulldown
|
||||||
|
void gpio_pull(int pin, int pull) {
|
||||||
|
volatile uint32_t *reg = REG32(gpio_regs, GPIO_GPPUDCLK0) + pin / 32;
|
||||||
|
|
||||||
|
*REG32(gpio_regs, GPIO_GPPUD) = pull;
|
||||||
|
usleep(2);
|
||||||
|
*reg = pin << (pin % 32);
|
||||||
|
usleep(2);
|
||||||
|
*REG32(gpio_regs, GPIO_GPPUD) = 0;
|
||||||
|
*reg = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set input or output
|
||||||
|
void gpio_mode(int pin, int mode) {
|
||||||
|
if (gpio_regs.virt) {
|
||||||
|
volatile uint32_t *reg = REG32(gpio_regs, GPIO_MODE0) + pin / 10, shift = (pin % 10) * 3;
|
||||||
|
*reg = (*reg & ~(7 << shift)) | (mode << shift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set an O/P pin
|
||||||
|
void gpio_out(int pin, int val) {
|
||||||
|
volatile uint32_t *reg = REG32(gpio_regs, val ? GPIO_SET0 : GPIO_CLR0) + pin / 32;
|
||||||
|
*reg = 1 << (pin % 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get an I/P pin value
|
||||||
|
uint8_t gpio_in(int pin) {
|
||||||
|
volatile uint32_t *reg = REG32(gpio_regs, GPIO_LEV0) + pin / 32;
|
||||||
|
return (((*reg) >> (pin % 32)) & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display the values in a GPIO mode register
|
||||||
|
void disp_mode_vals(uint32_t mode) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 10; i++)
|
||||||
|
printf("%u:%-4s ", i, gpio_mode_strs[(mode >> (i * 3)) & 7]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
26
RpiLedBars/src/drivers/gpio/rpi_gpio.h
Normal file
26
RpiLedBars/src/drivers/gpio/rpi_gpio.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#if !defined(__RPI_GPIO_H__)
|
||||||
|
#define __RPI_GPIO_H__
|
||||||
|
|
||||||
|
// GPIO I/O definitions
|
||||||
|
#define GPIO_IN 0
|
||||||
|
#define GPIO_OUT 1
|
||||||
|
#define GPIO_ALT0 4
|
||||||
|
#define GPIO_ALT1 5
|
||||||
|
#define GPIO_ALT2 6
|
||||||
|
#define GPIO_ALT3 7
|
||||||
|
#define GPIO_ALT4 3
|
||||||
|
#define GPIO_ALT5 2
|
||||||
|
|
||||||
|
#define GPIO_NOPULL 0
|
||||||
|
#define GPIO_PULLDN 1
|
||||||
|
#define GPIO_PULLUP 2
|
||||||
|
|
||||||
|
void gpio_setup();
|
||||||
|
|
||||||
|
void gpio_close();
|
||||||
|
|
||||||
|
void gpio_pull(int pin, int pull);
|
||||||
|
|
||||||
|
void gpio_mode(int pin, int mode);
|
||||||
|
|
||||||
|
#endif // __RPI_GPIO_H__
|
132
RpiLedBars/src/drivers/leddriver/rpi_leddriver.c
Normal file
132
RpiLedBars/src/drivers/leddriver/rpi_leddriver.c
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#include "rpi_leddriver.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
#include "../dma/rpi_dma.h"
|
||||||
|
#include "../dma/rpi_videocore.h"
|
||||||
|
#include "../gpio/rpi_gpio.h"
|
||||||
|
#include "../smi/rpi_smi.h"
|
||||||
|
|
||||||
|
#if PHYS_REG_BASE == PI_4_REG_BASE // Timings for RPi v4 (1.5 GHz)
|
||||||
|
#define SMI_TIMING 10, 15, 30, 15 // 400 ns cycle time
|
||||||
|
#else // Timings for RPi v0-3 (1 GHz)
|
||||||
|
#define SMI_TIMING 10, 10, 20, 10 // 400 ns cycle time
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TX_TEST 0 // If non-zero, use dummy Tx data
|
||||||
|
#define LED_NBITS 24 // Number of data bits per LED
|
||||||
|
#define LED_PREBITS 4 // Number of zero bits before LED data
|
||||||
|
#define LED_POSTBITS 4 // Number of zero bits after LED data
|
||||||
|
#define BIT_NPULSES 3 // Number of O/P pulses per LED bit
|
||||||
|
|
||||||
|
// Length of data for 1 row (1 LED on each channel)
|
||||||
|
#define LED_DLEN (LED_NBITS * BIT_NPULSES)
|
||||||
|
|
||||||
|
// Transmit data type, 8 or 16 bits
|
||||||
|
#if LED_NCHANS > 8
|
||||||
|
#define TXDATA_T uint16_t
|
||||||
|
#else
|
||||||
|
#define TXDATA_T uint8_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Ofset into Tx data buffer, given LED number in chan
|
||||||
|
#define LED_TX_OSET(n) (LED_PREBITS + (LED_DLEN * (n)))
|
||||||
|
|
||||||
|
// Size of data buffers & NV memory, given number of LEDs per chan
|
||||||
|
#define TX_BUFF_LEN(n) (LED_TX_OSET(n) + LED_POSTBITS)
|
||||||
|
#define TX_BUFF_SIZE(n) (TX_BUFF_LEN(n) * sizeof(TXDATA_T))
|
||||||
|
#define VC_MEM_SIZE (PAGE_SIZE + TX_BUFF_SIZE(CHAN_MAXLEDS))
|
||||||
|
|
||||||
|
/* Global */
|
||||||
|
MEM_MAP vc_mem;
|
||||||
|
TXDATA_T *txdata;
|
||||||
|
TXDATA_T tx_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)];
|
||||||
|
|
||||||
|
void swap_bytes();
|
||||||
|
|
||||||
|
void leddriver_setup() {
|
||||||
|
gpio_setup();
|
||||||
|
videocore_setup(&vc_mem, VC_MEM_SIZE);
|
||||||
|
smi_setup(LED_NCHANS, SMI_TIMING, &vc_mem, TX_BUFF_LEN(CHAN_MAXLEDS), &txdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
void leddriver_close() {
|
||||||
|
printf("Closing\n");
|
||||||
|
videocore_close(&vc_mem);
|
||||||
|
smi_close(LED_NCHANS);
|
||||||
|
gpio_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_color(uint32_t rgb, int index) {
|
||||||
|
int msk;
|
||||||
|
TXDATA_T *txd = &(tx_buffer[LED_TX_OSET(index)]);
|
||||||
|
|
||||||
|
// For each bit of the 24-bit RGB values..
|
||||||
|
for (size_t n = 0; n < LED_NBITS; n++) {
|
||||||
|
// Mask to convert RGB to GRB, M.S bit first
|
||||||
|
msk = n == 0 ? 0x800000 : n == 8 ? 0x8000 : n == 16 ? 0x80 : msk >> 1;
|
||||||
|
// 1st byte or word is a high pulse on all lines
|
||||||
|
txd[0] = (TXDATA_T)0xffff;
|
||||||
|
// 2nd has high or low bits from data
|
||||||
|
// 3rd is a low pulse
|
||||||
|
txd[1] = txd[2] = 0;
|
||||||
|
if (rgb & msk) {
|
||||||
|
txd[1] = (TXDATA_T)0xffff;
|
||||||
|
}
|
||||||
|
txd += BIT_NPULSES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Tx data for 8 or 16 chans, 1 LED per chan, given 1 RGB val per chan
|
||||||
|
// Logic 1 is 0.8us high, 0.4 us low, logic 0 is 0.4us high, 0.8us low
|
||||||
|
void rgb_txdata(int *rgbs, int index) {
|
||||||
|
int i, n, msk;
|
||||||
|
TXDATA_T *txd = &(tx_buffer[LED_TX_OSET(index)]);
|
||||||
|
|
||||||
|
// For each bit of the 24-bit RGB values..
|
||||||
|
for (n = 0; n < LED_NBITS; n++) {
|
||||||
|
// Mask to convert RGB to GRB, M.S bit first
|
||||||
|
msk = n == 0 ? 0x800000 : n == 8 ? 0x8000 : n == 16 ? 0x80 : msk >> 1;
|
||||||
|
// 1st byte or word is a high pulse on all lines
|
||||||
|
txd[0] = (TXDATA_T)0xffff;
|
||||||
|
// 2nd has high or low bits from data
|
||||||
|
// 3rd is a low pulse
|
||||||
|
txd[1] = txd[2] = 0;
|
||||||
|
for (i = 0; i < LED_NCHANS; i++) {
|
||||||
|
if (rgbs[i] & msk)
|
||||||
|
txd[1] |= (1 << i);
|
||||||
|
}
|
||||||
|
txd += BIT_NPULSES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void leddriver_refresh() {
|
||||||
|
#if LED_NCHANS <= 8
|
||||||
|
swap_bytes();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (dma_active(DMA_CHAN_A)) {
|
||||||
|
usleep(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(txdata, tx_buffer, TX_BUFF_SIZE(CHAN_MAXLEDS));
|
||||||
|
enable_dma(DMA_CHAN_A);
|
||||||
|
start_smi(&vc_mem);
|
||||||
|
usleep(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap adjacent bytes in transmit data
|
||||||
|
void swap_bytes() {
|
||||||
|
uint16_t *wp = (uint16_t *)tx_buffer;
|
||||||
|
int len = TX_BUFF_SIZE(CHAN_MAXLEDS);
|
||||||
|
|
||||||
|
len = (len + 1) / 2;
|
||||||
|
while (len-- > 0) {
|
||||||
|
*wp = __builtin_bswap16(*wp);
|
||||||
|
wp++;
|
||||||
|
}
|
||||||
|
}
|
19
RpiLedBars/src/drivers/leddriver/rpi_leddriver.h
Normal file
19
RpiLedBars/src/drivers/leddriver/rpi_leddriver.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#if !defined(__RPI_LEDDRIVER_H__)
|
||||||
|
#define __RPI_LEDDRIVER_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define CHAN_MAXLEDS 6 * 60 // Maximum number of LEDs per channel
|
||||||
|
#define LED_NCHANS 8 // Number of LED channels (8 or 16)
|
||||||
|
|
||||||
|
void leddriver_setup();
|
||||||
|
|
||||||
|
void leddriver_close();
|
||||||
|
|
||||||
|
void set_color(uint32_t rgb, int index);
|
||||||
|
|
||||||
|
void rgb_txdata(int *rgbs, int index);
|
||||||
|
|
||||||
|
void leddriver_refresh();
|
||||||
|
|
||||||
|
#endif // __RPI_LEDDRIVER_H__
|
223
RpiLedBars/src/drivers/smi/rpi_smi.c
Normal file
223
RpiLedBars/src/drivers/smi/rpi_smi.c
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
#include "rpi_smi.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "../dma/rpi_dma.h"
|
||||||
|
#include "../gpio/rpi_gpio.h"
|
||||||
|
|
||||||
|
// GPIO first pin
|
||||||
|
#define SMI_SD0_PIN 8
|
||||||
|
|
||||||
|
// Data widths
|
||||||
|
#define SMI_8_BITS 0
|
||||||
|
#define SMI_16_BITS 1
|
||||||
|
#define SMI_18_BITS 2
|
||||||
|
#define SMI_9_BITS 3
|
||||||
|
|
||||||
|
// Clock registers and values
|
||||||
|
#define CLK_BASE (PHYS_REG_BASE + 0x101000)
|
||||||
|
// #define CLK_PWM_CTL 0xa0
|
||||||
|
// #define CLK_PWM_DIV 0xa4
|
||||||
|
#define CLK_SMI_CTL 0xb0
|
||||||
|
#define CLK_SMI_DIV 0xb4
|
||||||
|
#define CLK_PASSWD 0x5a000000
|
||||||
|
#define PWM_CLOCK_ID 0xa
|
||||||
|
|
||||||
|
// DMA request threshold
|
||||||
|
#define REQUEST_THRESH 2
|
||||||
|
|
||||||
|
// Register definitions
|
||||||
|
#define SMI_BASE (PHYS_REG_BASE + 0x600000)
|
||||||
|
#define SMI_CS 0x00 // Control & status
|
||||||
|
#define SMI_L 0x04 // Transfer length
|
||||||
|
#define SMI_A 0x08 // Address
|
||||||
|
#define SMI_D 0x0c // Data
|
||||||
|
#define SMI_DSR0 0x10 // Read settings device 0
|
||||||
|
#define SMI_DSW0 0x14 // Write settings device 0
|
||||||
|
#define SMI_DSR1 0x18 // Read settings device 1
|
||||||
|
#define SMI_DSW1 0x1c // Write settings device 1
|
||||||
|
#define SMI_DSR2 0x20 // Read settings device 2
|
||||||
|
#define SMI_DSW2 0x24 // Write settings device 2
|
||||||
|
#define SMI_DSR3 0x28 // Read settings device 3
|
||||||
|
#define SMI_DSW3 0x2c // Write settings device 3
|
||||||
|
#define SMI_DMC 0x30 // DMA control
|
||||||
|
#define SMI_DCS 0x34 // Direct control/status
|
||||||
|
#define SMI_DCA 0x38 // Direct address
|
||||||
|
#define SMI_DCD 0x3c // Direct data
|
||||||
|
#define SMI_FD 0x40 // FIFO debug
|
||||||
|
#define SMI_REGLEN (SMI_FD * 4)
|
||||||
|
|
||||||
|
// Union of 32-bit value with register bitfields
|
||||||
|
#define REG_DEF(name, fields) \
|
||||||
|
typedef union { \
|
||||||
|
struct { \
|
||||||
|
volatile uint32_t fields; \
|
||||||
|
}; \
|
||||||
|
volatile uint32_t value; \
|
||||||
|
} name
|
||||||
|
|
||||||
|
// Control and status register
|
||||||
|
#define SMI_CS_FIELDS \
|
||||||
|
enable: \
|
||||||
|
1, done : 1, active : 1, start : 1, clear : 1, write : 1, _x1 : 2, teen : 1, intd : 1, intt : 1, \
|
||||||
|
intr : 1, pvmode : 1, seterr : 1, pxldat : 1, edreq : 1, _x2 : 8, _x3 : 1, aferr : 1, \
|
||||||
|
txw : 1, rxr : 1, txd : 1, rxd : 1, txe : 1, rxf : 1
|
||||||
|
REG_DEF(SMI_CS_REG, SMI_CS_FIELDS);
|
||||||
|
|
||||||
|
// Data length register
|
||||||
|
#define SMI_L_FIELDS \
|
||||||
|
len: \
|
||||||
|
32
|
||||||
|
REG_DEF(SMI_L_REG, SMI_L_FIELDS);
|
||||||
|
|
||||||
|
// Address & device number
|
||||||
|
#define SMI_A_FIELDS \
|
||||||
|
addr: \
|
||||||
|
6, _x1 : 2, dev : 2
|
||||||
|
REG_DEF(SMI_A_REG, SMI_A_FIELDS);
|
||||||
|
|
||||||
|
// Data FIFO
|
||||||
|
#define SMI_D_FIELDS \
|
||||||
|
data: \
|
||||||
|
32
|
||||||
|
REG_DEF(SMI_D_REG, SMI_D_FIELDS);
|
||||||
|
|
||||||
|
// DMA control register
|
||||||
|
#define SMI_DMC_FIELDS \
|
||||||
|
reqw: \
|
||||||
|
6, reqr : 6, panicw : 6, panicr : 6, dmap : 1, _x1 : 3, dmaen : 1
|
||||||
|
REG_DEF(SMI_DMC_REG, SMI_DMC_FIELDS);
|
||||||
|
|
||||||
|
// Device settings: read (1 of 4)
|
||||||
|
#define SMI_DSR_FIELDS \
|
||||||
|
rstrobe: \
|
||||||
|
7, rdreq : 1, rpace : 7, rpaceall : 1, rhold : 6, fsetup : 1, mode68 : 1, rsetup : 6, rwidth : 2
|
||||||
|
REG_DEF(SMI_DSR_REG, SMI_DSR_FIELDS);
|
||||||
|
|
||||||
|
// Device settings: write (1 of 4)
|
||||||
|
#define SMI_DSW_FIELDS \
|
||||||
|
wstrobe: \
|
||||||
|
7, wdreq : 1, wpace : 7, wpaceall : 1, whold : 6, wswap : 1, wformat : 1, wsetup : 6, wwidth : 2
|
||||||
|
REG_DEF(SMI_DSW_REG, SMI_DSW_FIELDS);
|
||||||
|
|
||||||
|
// Direct control register
|
||||||
|
#define SMI_DCS_FIELDS \
|
||||||
|
enable: \
|
||||||
|
1, start : 1, done : 1, write : 1
|
||||||
|
REG_DEF(SMI_DCS_REG, SMI_DCS_FIELDS);
|
||||||
|
|
||||||
|
// Direct control address & device number
|
||||||
|
#define SMI_DCA_FIELDS \
|
||||||
|
addr: \
|
||||||
|
6, _x1 : 2, dev : 2
|
||||||
|
REG_DEF(SMI_DCA_REG, SMI_DCA_FIELDS);
|
||||||
|
|
||||||
|
// Direct control data
|
||||||
|
#define SMI_DCD_FIELDS \
|
||||||
|
data: \
|
||||||
|
32
|
||||||
|
REG_DEF(SMI_DCD_REG, SMI_DCD_FIELDS);
|
||||||
|
|
||||||
|
// Debug register
|
||||||
|
#define SMI_FLVL_FIELDS \
|
||||||
|
fcnt: \
|
||||||
|
6, _x1 : 2, flvl : 6
|
||||||
|
REG_DEF(SMI_FLVL_REG, SMI_FLVL_FIELDS);
|
||||||
|
|
||||||
|
// Pointers to SMI registers
|
||||||
|
volatile SMI_CS_REG *smi_cs;
|
||||||
|
volatile SMI_L_REG *smi_l;
|
||||||
|
volatile SMI_A_REG *smi_a;
|
||||||
|
volatile SMI_D_REG *smi_d;
|
||||||
|
volatile SMI_DMC_REG *smi_dmc;
|
||||||
|
volatile SMI_DSR_REG *smi_dsr;
|
||||||
|
volatile SMI_DSW_REG *smi_dsw;
|
||||||
|
volatile SMI_DCS_REG *smi_dcs;
|
||||||
|
volatile SMI_DCA_REG *smi_dca;
|
||||||
|
volatile SMI_DCD_REG *smi_dcd;
|
||||||
|
|
||||||
|
MEM_MAP smi_regs, clk_regs;
|
||||||
|
|
||||||
|
void setup_smi_dma(MEM_MAP *mp, int nsamp, uint8_t **txdata, int len);
|
||||||
|
|
||||||
|
void smi_setup(int channels, int ns, int setup, int strobe, int hold, MEM_MAP *mp, int nsamp,
|
||||||
|
uint8_t **txdata) {
|
||||||
|
map_periph(&smi_regs, (void *)SMI_BASE, PAGE_SIZE);
|
||||||
|
map_periph(&clk_regs, (void *)CLK_BASE, PAGE_SIZE);
|
||||||
|
|
||||||
|
int width = channels > 8 ? SMI_16_BITS : SMI_8_BITS;
|
||||||
|
int i, divi = ns / 2;
|
||||||
|
|
||||||
|
smi_cs = (SMI_CS_REG *)REG32(smi_regs, SMI_CS);
|
||||||
|
smi_l = (SMI_L_REG *)REG32(smi_regs, SMI_L);
|
||||||
|
smi_a = (SMI_A_REG *)REG32(smi_regs, SMI_A);
|
||||||
|
smi_d = (SMI_D_REG *)REG32(smi_regs, SMI_D);
|
||||||
|
smi_dmc = (SMI_DMC_REG *)REG32(smi_regs, SMI_DMC);
|
||||||
|
smi_dsr = (SMI_DSR_REG *)REG32(smi_regs, SMI_DSR0);
|
||||||
|
smi_dsw = (SMI_DSW_REG *)REG32(smi_regs, SMI_DSW0);
|
||||||
|
smi_dcs = (SMI_DCS_REG *)REG32(smi_regs, SMI_DCS);
|
||||||
|
smi_dca = (SMI_DCA_REG *)REG32(smi_regs, SMI_DCA);
|
||||||
|
smi_dcd = (SMI_DCD_REG *)REG32(smi_regs, SMI_DCD);
|
||||||
|
smi_cs->value = smi_l->value = smi_a->value = 0;
|
||||||
|
smi_dsr->value = smi_dsw->value = smi_dcs->value = smi_dca->value = 0;
|
||||||
|
if (*REG32(clk_regs, CLK_SMI_DIV) != divi << 12) {
|
||||||
|
*REG32(clk_regs, CLK_SMI_CTL) = CLK_PASSWD | (1 << 5);
|
||||||
|
usleep(10);
|
||||||
|
while (*REG32(clk_regs, CLK_SMI_CTL) & (1 << 7))
|
||||||
|
;
|
||||||
|
usleep(10);
|
||||||
|
*REG32(clk_regs, CLK_SMI_DIV) = CLK_PASSWD | (divi << 12);
|
||||||
|
usleep(10);
|
||||||
|
*REG32(clk_regs, CLK_SMI_CTL) = CLK_PASSWD | 6 | (1 << 4);
|
||||||
|
usleep(10);
|
||||||
|
while ((*REG32(clk_regs, CLK_SMI_CTL) & (1 << 7)) == 0)
|
||||||
|
;
|
||||||
|
usleep(100);
|
||||||
|
}
|
||||||
|
if (smi_cs->seterr)
|
||||||
|
smi_cs->seterr = 1;
|
||||||
|
smi_dsr->rsetup = smi_dsw->wsetup = setup;
|
||||||
|
smi_dsr->rstrobe = smi_dsw->wstrobe = strobe;
|
||||||
|
smi_dsr->rhold = smi_dsw->whold = hold;
|
||||||
|
smi_dmc->panicr = smi_dmc->panicw = 8;
|
||||||
|
smi_dmc->reqr = smi_dmc->reqw = REQUEST_THRESH;
|
||||||
|
smi_dsr->rwidth = smi_dsw->wwidth = width;
|
||||||
|
for (i = 0; i < channels; i++)
|
||||||
|
gpio_mode(SMI_SD0_PIN + i, GPIO_ALT1);
|
||||||
|
|
||||||
|
setup_smi_dma(mp, nsamp, txdata, width + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void smi_close(int channels) {
|
||||||
|
for (size_t i = 0; i < channels; ++i)
|
||||||
|
gpio_mode(SMI_SD0_PIN + i, GPIO_IN);
|
||||||
|
if (smi_regs.virt) {
|
||||||
|
*REG32(smi_regs, SMI_CS) = 0;
|
||||||
|
}
|
||||||
|
stop_dma(DMA_CHAN_A);
|
||||||
|
unmap_periph_mem(&clk_regs);
|
||||||
|
unmap_periph_mem(&smi_regs);
|
||||||
|
dma_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start SMI DMA transfers
|
||||||
|
void start_smi(MEM_MAP *mp) {
|
||||||
|
DMA_CB *cbs = mp->virt;
|
||||||
|
|
||||||
|
start_dma(mp, DMA_CHAN_A, &cbs[0], 0);
|
||||||
|
smi_cs->start = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
|
||||||
|
// Set up SMI transfers using DMA
|
||||||
|
void setup_smi_dma(MEM_MAP *mp, int nsamp, uint8_t **txdata, int len) {
|
||||||
|
smi_dmc->dmaen = 1;
|
||||||
|
smi_cs->enable = 1;
|
||||||
|
smi_cs->clear = 1;
|
||||||
|
smi_cs->pxldat = 1;
|
||||||
|
smi_l->len = nsamp * len;
|
||||||
|
smi_cs->write = 1;
|
||||||
|
|
||||||
|
dma_setup(mp, DMA_CHAN_A, nsamp, txdata, len, REG_BUS_ADDR(smi_regs, SMI_D));
|
||||||
|
}
|
14
RpiLedBars/src/drivers/smi/rpi_smi.h
Normal file
14
RpiLedBars/src/drivers/smi/rpi_smi.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#if !defined(__RPI_SMI_H__)
|
||||||
|
#define __RPI_SMI_H__
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void smi_setup(int channels, int ns, int setup, int strobe, int hold, MEM_MAP *mp, int nsamp,
|
||||||
|
uint8_t **txdata);
|
||||||
|
|
||||||
|
void smi_close(int channels);
|
||||||
|
|
||||||
|
void start_smi(MEM_MAP *mp);
|
||||||
|
|
||||||
|
#endif // __RPI_SMI_H__
|
@ -1,147 +1,133 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <pthread.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#ifdef _WIN32
|
#include <time.h>
|
||||||
#include <Windows.h>
|
|
||||||
#else
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "rpi_artnet.h"
|
#include "drivers/leddriver/rpi_leddriver.h"
|
||||||
#include "rpi_pixleds.h"
|
#include "drivers/selector/rpi_selector.h"
|
||||||
#include "rpi_smi_defs.h"
|
|
||||||
|
|
||||||
#include "rpi_selector.h"
|
#include "rpi_midi_controller.h"
|
||||||
|
#include "tasks/artnet/rpi_artnet.h"
|
||||||
|
#include "tasks/cava/rpi_cava.h"
|
||||||
|
|
||||||
/* Command-line parameters */
|
/* Command-line parameters */
|
||||||
bool IsTestMode = false;
|
bool IsTestMode = false;
|
||||||
int chanLedCount = 0;
|
int chanLedCount = 0;
|
||||||
|
|
||||||
/* Global */
|
pthread_t *bgTasks[4] = {NULL};
|
||||||
MEM_MAP vc_mem;
|
|
||||||
TXDATA_T *txdata;
|
|
||||||
|
|
||||||
void parseCommandLineArgs(int argc, char const *argv[]);
|
void parseCommandLineArgs(int argc, char const *argv[]);
|
||||||
|
|
||||||
|
void terminate(int sig);
|
||||||
|
|
||||||
void execute_test_mode();
|
void execute_test_mode();
|
||||||
|
|
||||||
void execute_artnet_mode();
|
void execute_artnet_mode();
|
||||||
|
|
||||||
void execute_autonomous_mode();
|
void execute_autonomous_mode();
|
||||||
|
|
||||||
void execute_autonomous2_mode();
|
void execute_manual_mode();
|
||||||
|
|
||||||
|
void adjust_loop(struct timespec const *loopStart);
|
||||||
|
|
||||||
int main(int argc, char const *argv[]) {
|
int main(int argc, char const *argv[]) {
|
||||||
int previousMode = 0;
|
int previousMode = -1;
|
||||||
|
|
||||||
// setup
|
// setup
|
||||||
parseCommandLineArgs(argc, argv);
|
parseCommandLineArgs(argc, argv);
|
||||||
|
|
||||||
signal(SIGINT, terminate);
|
signal(SIGINT, terminate);
|
||||||
|
|
||||||
|
struct sched_param sp;
|
||||||
|
sp.sched_priority = 32;
|
||||||
|
if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) {
|
||||||
|
fprintf(stderr, "WARNING: Failed to set stepper thread to real-time priority\n");
|
||||||
|
}
|
||||||
|
|
||||||
setup_selector();
|
setup_selector();
|
||||||
|
|
||||||
// setup led
|
// // setup led
|
||||||
map_devices();
|
// map_devices();
|
||||||
init_smi(LED_NCHANS > 8 ? SMI_16_BITS : SMI_8_BITS, SMI_TIMING);
|
// init_smi(LED_NCHANS, SMI_TIMING);
|
||||||
map_uncached_mem(&vc_mem, VC_MEM_SIZE);
|
// map_uncached_mem(&vc_mem, VC_MEM_SIZE);
|
||||||
|
|
||||||
setup_smi_dma(&vc_mem, TX_BUFF_LEN(chanLedCount), &txdata);
|
// setup_smi_dma(&vc_mem, TX_BUFF_LEN(chanLedCount), &txdata);
|
||||||
|
|
||||||
|
leddriver_setup();
|
||||||
|
|
||||||
|
setup_midi_controller();
|
||||||
artnet_init();
|
artnet_init();
|
||||||
|
setup_cava();
|
||||||
|
|
||||||
// loop
|
// loop
|
||||||
while (1) {
|
while (1) {
|
||||||
|
struct timespec loopStart;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &loopStart);
|
||||||
int mode = get_selector_position();
|
int mode = get_selector_position();
|
||||||
|
execute_midi_controller();
|
||||||
|
|
||||||
if (mode != previousMode) {
|
if (mode != -1) {
|
||||||
|
if (mode != previousMode) {
|
||||||
#if defined(_DEBUG)
|
#if defined(_DEBUG)
|
||||||
print_selector();
|
printf("swtching to mode : %d\n", mode);
|
||||||
#endif
|
#endif
|
||||||
}
|
/* stop previous bg task */
|
||||||
|
switch (previousMode) {
|
||||||
|
case 1:
|
||||||
|
stop_artnet_bg_worker();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
stop_cava_bg_worker();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch (mode) {
|
/* start new bg task */
|
||||||
case 0:
|
switch (mode) {
|
||||||
// mode test
|
case 1:
|
||||||
execute_test_mode();
|
start_artnet_bg_worker();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 2:
|
||||||
// artnet mode
|
start_cava_bg_worker();
|
||||||
execute_artnet_mode();
|
break;
|
||||||
break;
|
default:
|
||||||
case 2:
|
break;
|
||||||
execute_autonomous_mode();
|
}
|
||||||
break;
|
} else {
|
||||||
case 3:
|
switch (mode) {
|
||||||
// autonomous mode 2
|
case 0:
|
||||||
execute_autonomous2_mode();
|
// mode test
|
||||||
break;
|
execute_test_mode();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
// artnet mode
|
||||||
|
execute_artnet_mode();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
execute_autonomous_mode();
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
// manual mode
|
||||||
|
execute_manual_mode();
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
printf("error in selector \n");
|
if (mode != previousMode) {
|
||||||
break;
|
fprintf(stderr, "Error in selector\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previousMode = mode;
|
||||||
}
|
}
|
||||||
previousMode = mode;
|
adjust_loop(&loopStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
// printf("%s %u LED%s per channel, %u channels\n", IsTestMode ? "Testing" : "Setting",
|
|
||||||
// chanLedCount,
|
|
||||||
// chanLedCount == 1 ? "" : "s", LED_NCHANS);
|
|
||||||
|
|
||||||
// for (size_t colorIndex = 0; colorIndex < 3; ++colorIndex) {
|
|
||||||
// for (size_t i = 0; i < chanLedCount; ++i) {
|
|
||||||
// set_color(on_rgbs[colorIndex], &tx_buffer[LED_TX_OSET(i)]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #if LED_NCHANS <= 8
|
|
||||||
// swap_bytes(tx_buffer, TX_BUFF_SIZE(chanLedCount));
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chanLedCount));
|
|
||||||
// start_smi(&vc_mem);
|
|
||||||
// usleep(CHASE_MSEC * 1000);
|
|
||||||
// sleep(1);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// artnet_init();
|
|
||||||
|
|
||||||
// // loops
|
|
||||||
// if (IsTestMode) {
|
|
||||||
// while (1) {
|
|
||||||
// test_leds();
|
|
||||||
// sleep(3);
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// while (1) {
|
|
||||||
// artDmx_t *artDmx;
|
|
||||||
// if (artnet_read(&artDmx) == OpDmx) {
|
|
||||||
// uint16_t dmxLength = (artDmx->lengthHi << 8) | artDmx->lengthLo;
|
|
||||||
// unsigned int ledCountInFrame = dmxLength / 3;
|
|
||||||
// uint16_t maxBound = ledCountInFrame < chanLedCount ? ledCountInFrame : chanLedCount;
|
|
||||||
// unsigned int universe = artDmx->subUni & (LED_NCHANS - 1);
|
|
||||||
// for (size_t i = 0; i < maxBound; ++i) {
|
|
||||||
// uint8_t *rgb = artDmx->data + (i * 3);
|
|
||||||
// rgb_data[i][universe] = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
|
|
||||||
// rgb_txdata(rgb_data[i], &tx_buffer[LED_TX_OSET(i)]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #if LED_NCHANS <= 8
|
|
||||||
// swap_bytes(tx_buffer, TX_BUFF_SIZE(chanLedCount));
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chanLedCount));
|
|
||||||
// // enable_dma(DMA_CHAN);
|
|
||||||
// start_smi(&vc_mem);
|
|
||||||
// // usleep(10);
|
|
||||||
// // while (dma_active(DMA_CHAN))
|
|
||||||
// // usleep(10);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// terminate(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseCommandLineArgs(int argc, char const *argv[]) {
|
void parseCommandLineArgs(int argc, char const *argv[]) {
|
||||||
@ -170,73 +156,105 @@ void parseCommandLineArgs(int argc, char const *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void terminate(int sig) {
|
||||||
|
leddriver_close();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
// Pointer to uncached Tx data buffer
|
// Pointer to uncached Tx data buffer
|
||||||
TXDATA_T tx_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)]; // Tx buffer for assembling data
|
// TXDATA_T tx_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)]; // Tx buffer for assembling data
|
||||||
|
|
||||||
void execute_test_mode() {
|
void execute_test_mode() {
|
||||||
// RGB values for test mode (1 value for each of 16 channels)
|
// RGB values for test mode (1 value for each of 16 channels)
|
||||||
uint32_t on_rgbs[] = {0xef0000, 0x00ef00, 0x0000ef, 0xefef00, 0xef00ef, 0x00efef, 0xefefef};
|
uint32_t on_rgbs[] = {0xef0000, 0x00ef00, 0x0000ef, 0xefef00, 0xef00ef, 0x00efef, 0xefefef};
|
||||||
uint32_t off_rgbs = 0x000000;
|
uint32_t off_rgbs = 0x000000;
|
||||||
|
|
||||||
static int i = 0, offset = 0, ledIndex = 0;
|
static int i = 0, offset = 0;
|
||||||
|
|
||||||
if (ledIndex < chanLedCount) {
|
for (size_t ledIndex = 0; ledIndex < chanLedCount; ++ledIndex) {
|
||||||
set_color(ledIndex <= offset % chanLedCount ? on_rgbs[i] : off_rgbs,
|
set_color(ledIndex <= offset % chanLedCount ? on_rgbs[i] : off_rgbs, ledIndex);
|
||||||
&tx_buffer[LED_TX_OSET(ledIndex)]);
|
}
|
||||||
++ledIndex;
|
|
||||||
|
|
||||||
|
leddriver_refresh();
|
||||||
|
|
||||||
|
if (offset < chanLedCount) {
|
||||||
|
++offset;
|
||||||
} else {
|
} else {
|
||||||
ledIndex = 0;
|
offset = 0;
|
||||||
|
if (i < 7) {
|
||||||
#if LED_NCHANS <= 8
|
++i;
|
||||||
swap_bytes(tx_buffer, TX_BUFF_SIZE(chanLedCount));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chanLedCount));
|
|
||||||
start_smi(&vc_mem);
|
|
||||||
usleep(CHASE_MSEC * 1000);
|
|
||||||
if (offset < chanLedCount) {
|
|
||||||
++offset;
|
|
||||||
} else {
|
} else {
|
||||||
offset = 0;
|
i = 0;
|
||||||
if (i < 7) {
|
}
|
||||||
++i;
|
}
|
||||||
} else {
|
}
|
||||||
i = 0;
|
|
||||||
|
// RGB data
|
||||||
|
int rgb_data[CHAN_MAXLEDS][LED_NCHANS];
|
||||||
|
|
||||||
|
void execute_artnet_mode() {
|
||||||
|
uint8_t *dmxData;
|
||||||
|
|
||||||
|
for (size_t ledBar = 0; ledBar < LED_NCHANS; ledBar++) {
|
||||||
|
if (artnet_get_dmx_data(ledBar, &dmxData) == 0) {
|
||||||
|
for (size_t i = 0; i < chanLedCount; ++i) {
|
||||||
|
uint8_t *rgb = dmxData + (i * 3);
|
||||||
|
rgb_data[i][ledBar] = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
|
||||||
|
rgb_txdata(rgb_data[i], i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
leddriver_refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute_artnet_mode() {
|
void execute_autonomous_mode() {
|
||||||
artDmx_t *artDmx;
|
int ret;
|
||||||
// RGB data
|
uint16_t *buffer;
|
||||||
int rgb_data[CHAN_MAXLEDS][LED_NCHANS];
|
|
||||||
|
|
||||||
if (artnet_read(&artDmx) == OpDmx) {
|
if ((ret = get_cava_buffer(&buffer)) == 0) {
|
||||||
uint16_t dmxLength = (artDmx->lengthHi << 8) | artDmx->lengthLo;
|
for (size_t ledBarIndex = 0; ledBarIndex < 4; ++ledBarIndex) {
|
||||||
unsigned int ledCountInFrame = dmxLength / 3;
|
uint16_t barMax = 0;
|
||||||
uint16_t maxBound = ledCountInFrame < chanLedCount ? ledCountInFrame : chanLedCount;
|
for (size_t bar = 0; bar < 20 / 4; ++bar) {
|
||||||
unsigned int universe = artDmx->subUni & (LED_NCHANS - 1);
|
unsigned barIndex = ledBarIndex * 20 / 4 + bar;
|
||||||
for (size_t i = 0; i < maxBound; ++i) {
|
if (barMax < buffer[barIndex]) {
|
||||||
uint8_t *rgb = artDmx->data + (i * 3);
|
barMax = buffer[barIndex];
|
||||||
rgb_data[i][universe] = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
|
}
|
||||||
rgb_txdata(rgb_data[i], &tx_buffer[LED_TX_OSET(i)]);
|
}
|
||||||
|
unsigned ledToLight = barMax * chanLedCount / UINT16_MAX;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ledToLight; ++i) {
|
||||||
|
rgb_data[i][ledBarIndex] = 0xff0000;
|
||||||
|
rgb_txdata(rgb_data[i], i);
|
||||||
|
}
|
||||||
|
for (size_t i = ledToLight; i < chanLedCount; ++i) {
|
||||||
|
rgb_data[i][ledBarIndex] = 0x000000;
|
||||||
|
rgb_txdata(rgb_data[i], i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LED_NCHANS <= 8
|
leddriver_refresh();
|
||||||
swap_bytes(tx_buffer, TX_BUFF_SIZE(chanLedCount));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chanLedCount));
|
|
||||||
enable_dma(DMA_CHAN);
|
|
||||||
start_smi(&vc_mem);
|
|
||||||
usleep(10);
|
|
||||||
while (dma_active(DMA_CHAN))
|
|
||||||
usleep(10);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute_autonomous_mode() {}
|
void execute_manual_mode() {}
|
||||||
|
|
||||||
void execute_autonomous2_mode() {}
|
void adjust_loop(struct timespec const *loopStart) {
|
||||||
|
struct timespec loopEnd;
|
||||||
|
long elapsedTimeUs, remainingTimeUs;
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &loopEnd);
|
||||||
|
elapsedTimeUs = (loopEnd.tv_nsec - loopStart->tv_nsec) / 1E3 +
|
||||||
|
((unsigned)(loopEnd.tv_sec - loopStart->tv_sec)) * 1E6;
|
||||||
|
remainingTimeUs = 16000 - elapsedTimeUs;
|
||||||
|
|
||||||
|
if (remainingTimeUs >= 0) {
|
||||||
|
// printf("loop remaining time %ld\n", remainingTimeUs);
|
||||||
|
usleep(remainingTimeUs);
|
||||||
|
} else {
|
||||||
|
printf("loop overlap by %06ldus\n", -remainingTimeUs);
|
||||||
|
printf("loop start %ld.%09lds\n", loopStart->tv_sec, loopStart->tv_nsec);
|
||||||
|
printf("loop end %ld.%09lds\n", loopEnd.tv_sec, loopEnd.tv_nsec);
|
||||||
|
}
|
||||||
|
}
|
@ -1,70 +0,0 @@
|
|||||||
#include "rpi_artnet.h"
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#include "rpi_artnet_utils.h"
|
|
||||||
|
|
||||||
int udpSocket = -1;
|
|
||||||
char buffer[1024];
|
|
||||||
|
|
||||||
void sendPollReply(struct sockaddr_in srcAddr);
|
|
||||||
|
|
||||||
void artnet_init() {
|
|
||||||
struct sockaddr_in serverAddr;
|
|
||||||
|
|
||||||
/*Create UDP socket*/
|
|
||||||
udpSocket = socket(PF_INET, SOCK_DGRAM, 0);
|
|
||||||
if (udpSocket < 0) {
|
|
||||||
perror("Opening socket failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Configure settings in address struct */
|
|
||||||
serverAddr.sin_family = AF_INET;
|
|
||||||
serverAddr.sin_port = htons(ARTNET_PORT);
|
|
||||||
serverAddr.sin_addr.s_addr = inet_addr("0.0.0.0");
|
|
||||||
memset(serverAddr.sin_zero, '\0', sizeof(serverAddr.sin_zero));
|
|
||||||
|
|
||||||
/* Bind socket with address struct */
|
|
||||||
bind(udpSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
|
|
||||||
}
|
|
||||||
|
|
||||||
int artnet_read(artDmx_t **artDmx) {
|
|
||||||
struct sockaddr_in srcAddr;
|
|
||||||
socklen_t srcLen = sizeof(srcAddr);
|
|
||||||
ssize_t bufferLen =
|
|
||||||
recvfrom(udpSocket, buffer, ARTNET_MAX_BUFFER, 0, (struct sockaddr *)&srcAddr, &srcLen);
|
|
||||||
|
|
||||||
if (bufferLen <= ARTNET_MAX_BUFFER && bufferLen > sizeof(artnetHeader_t)) {
|
|
||||||
artnetHeader_t *artnetHeader = (artnetHeader_t *)buffer;
|
|
||||||
if (memcmp(artnetHeader->id, ARTNET_ID, sizeof(ARTNET_ID)) == 0) {
|
|
||||||
switch (artnetHeader->opCode) {
|
|
||||||
case OpDmx:
|
|
||||||
if (bufferLen >= 20) {
|
|
||||||
*artDmx = (artDmx_t *)buffer;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case OpPoll:
|
|
||||||
sendPollReply(srcAddr);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return artnetHeader->opCode;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendPollReply(struct sockaddr_in srcAddr) {
|
|
||||||
/* Configure settings in address struct */
|
|
||||||
srcAddr.sin_family = AF_INET;
|
|
||||||
srcAddr.sin_port = htons(ARTNET_PORT);
|
|
||||||
memset(srcAddr.sin_zero, '\0', sizeof(srcAddr.sin_zero));
|
|
||||||
|
|
||||||
sendto(udpSocket, (uint8_t *)&artPollReply, sizeof(artPollReply_t), 0,
|
|
||||||
(struct sockaddr *)&srcAddr, sizeof(srcAddr));
|
|
||||||
}
|
|
95
RpiLedBars/src/rpi_midi_controller.c
Normal file
95
RpiLedBars/src/rpi_midi_controller.c
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#include "rpi_midi_controller.h"
|
||||||
|
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
|
|
||||||
|
static snd_seq_t *seq_handle;
|
||||||
|
static int sys_port;
|
||||||
|
static int in_port;
|
||||||
|
|
||||||
|
void subscribe_system_port();
|
||||||
|
|
||||||
|
void subscribe_midi_controller(snd_seq_addr_t controller);
|
||||||
|
|
||||||
|
void handle_system_port_events(snd_seq_event_t *ev);
|
||||||
|
|
||||||
|
void handle_in_port_events(snd_seq_event_t *ev);
|
||||||
|
|
||||||
|
void setup_midi_controller() {
|
||||||
|
if (snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK) != 0) {
|
||||||
|
SNDERR("snd_seq_open");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snd_seq_set_client_name(seq_handle, "Midi Listener") != 0) {
|
||||||
|
SNDERR("snd_seq_set_client_name");
|
||||||
|
}
|
||||||
|
|
||||||
|
sys_port = snd_seq_create_simple_port(seq_handle, "listen:in",
|
||||||
|
SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_NO_EXPORT,
|
||||||
|
SND_SEQ_PORT_TYPE_APPLICATION);
|
||||||
|
|
||||||
|
if (sys_port < 0) {
|
||||||
|
SNDERR("snd_seq_create_simple_port");
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe_system_port();
|
||||||
|
|
||||||
|
in_port = snd_seq_create_simple_port(seq_handle, "listen:in",
|
||||||
|
SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE,
|
||||||
|
SND_SEQ_PORT_TYPE_APPLICATION);
|
||||||
|
|
||||||
|
if (in_port < 0) {
|
||||||
|
SNDERR("snd_seq_create_simple_port");
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_seq_addr_t controller = {.client = 24, .port = 0};
|
||||||
|
subscribe_midi_controller(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute_midi_controller() {
|
||||||
|
snd_seq_event_t *ev = NULL;
|
||||||
|
int ret;
|
||||||
|
while ((ret = snd_seq_event_input(seq_handle, &ev)) > 0) {
|
||||||
|
if (ev->dest.port == sys_port) {
|
||||||
|
handle_system_port_events(ev);
|
||||||
|
} else if (ev->dest.port == in_port) {
|
||||||
|
printf("ev %02d : %#010x - %#010x - %#010x\n", ev->type, ev->data.raw32.d[0],
|
||||||
|
ev->data.raw32.d[1], ev->data.raw32.d[2]);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "unkonwn midi dest port\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret < 0 && ret != -EAGAIN) {
|
||||||
|
SNDERR("snd_seq_event_input");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_midi_controller() {}
|
||||||
|
|
||||||
|
void subscribe_system_port() {
|
||||||
|
snd_seq_addr_t sender = {.client = 0, .port = SND_SEQ_PORT_SYSTEM_ANNOUNCE};
|
||||||
|
snd_seq_connect_from(seq_handle, sys_port, sender.client, sender.port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subscribe_midi_controller(snd_seq_addr_t controller) {
|
||||||
|
snd_seq_connect_from(seq_handle, in_port, controller.client, controller.port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_system_port_events(snd_seq_event_t *ev) {
|
||||||
|
if (ev->type == SND_SEQ_EVENT_PORT_START) {
|
||||||
|
snd_seq_addr_t *newport = (snd_seq_addr_t *)&ev->data;
|
||||||
|
if (newport->client != snd_seq_client_id(seq_handle)) {
|
||||||
|
fprintf(stdout, "New port %d:%d\n", newport->client, newport->port);
|
||||||
|
subscribe_midi_controller(*newport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_in_port_events(snd_seq_event_t *ev) {
|
||||||
|
if (ev->type == SND_SEQ_EVENT_PORT_START) {
|
||||||
|
snd_seq_addr_t *newport = (snd_seq_addr_t *)&ev->data;
|
||||||
|
if (newport->client != snd_seq_client_id(seq_handle)) {
|
||||||
|
fprintf(stdout, "New port %d:%d\n", newport->client, newport->port);
|
||||||
|
subscribe_midi_controller(*newport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
RpiLedBars/src/rpi_midi_controller.h
Normal file
10
RpiLedBars/src/rpi_midi_controller.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#if !defined(__RPI_MIDI_CONTROLLER_H__)
|
||||||
|
#define __RPI_MIDI_CONTROLLER_H__
|
||||||
|
|
||||||
|
void setup_midi_controller();
|
||||||
|
|
||||||
|
void execute_midi_controller();
|
||||||
|
|
||||||
|
void close_midi_controller();
|
||||||
|
|
||||||
|
#endif /* __RPI_MIDI_CONTROLLER_H__ */
|
1
RpiLedBars/src/rpi_param.c
Normal file
1
RpiLedBars/src/rpi_param.c
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "rpi_param.h"
|
4
RpiLedBars/src/rpi_param.h
Normal file
4
RpiLedBars/src/rpi_param.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#if !defined(__RPI_PARAM_H__)
|
||||||
|
#define __RPI_PARAM_H__
|
||||||
|
|
||||||
|
#endif /* __RPI_PARAM_H__ */
|
139
RpiLedBars/src/tasks/artnet/rpi_artnet.c
Normal file
139
RpiLedBars/src/tasks/artnet/rpi_artnet.c
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#include "rpi_artnet.h"
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "rpi_artnet_utils.h"
|
||||||
|
|
||||||
|
int udpSocket = -1;
|
||||||
|
char buffer[1024];
|
||||||
|
bool isUdpListenerRunning = false;
|
||||||
|
pthread_t udpListener;
|
||||||
|
uint8_t artDmxBufferArray[16][512];
|
||||||
|
|
||||||
|
static void *artnet_udp_handler(void *arg);
|
||||||
|
|
||||||
|
void artnet_send_poll_reply(struct sockaddr_in srcAddr);
|
||||||
|
|
||||||
|
void artnet_init() {}
|
||||||
|
|
||||||
|
void start_artnet_bg_worker() {
|
||||||
|
isUdpListenerRunning = true;
|
||||||
|
|
||||||
|
if (pthread_create(&udpListener, NULL, artnet_udp_handler, NULL) < 0) {
|
||||||
|
perror("pthread_create");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop_artnet_bg_worker() {
|
||||||
|
isUdpListenerRunning = false;
|
||||||
|
|
||||||
|
if (pthread_join(udpListener, NULL) != 0) {
|
||||||
|
perror("pthread_join");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int artnet_get_dmx_data(unsigned int univerve, uint8_t **dmxData) {
|
||||||
|
if (univerve > 8) {
|
||||||
|
fprintf(stderr, "Universe %d out of bounds %d\n", univerve, 16);
|
||||||
|
*dmxData = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*dmxData = artDmxBufferArray[univerve];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *artnet_udp_handler(void *arg) {
|
||||||
|
struct sockaddr_in serverAddr;
|
||||||
|
struct pollfd fds[1];
|
||||||
|
int timeoutMs;
|
||||||
|
int ret;
|
||||||
|
int flags;
|
||||||
|
|
||||||
|
/* Create UDP socket */
|
||||||
|
udpSocket = socket(PF_INET, SOCK_DGRAM, 0);
|
||||||
|
if (udpSocket < 0) {
|
||||||
|
perror("Opening socket failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set non-blocking socket */
|
||||||
|
flags = fcntl(udpSocket, F_GETFL, 0);
|
||||||
|
fcntl(udpSocket, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
|
||||||
|
/* pollfd structure and timeout */
|
||||||
|
memset(fds, 0, sizeof(fds));
|
||||||
|
fds[0].fd = udpSocket;
|
||||||
|
fds[0].events = POLLIN;
|
||||||
|
timeoutMs = 10;
|
||||||
|
|
||||||
|
/* Configure settings in address struct */
|
||||||
|
serverAddr.sin_family = AF_INET;
|
||||||
|
serverAddr.sin_port = htons(ARTNET_PORT);
|
||||||
|
serverAddr.sin_addr.s_addr = inet_addr("0.0.0.0");
|
||||||
|
memset(serverAddr.sin_zero, '\0', sizeof(serverAddr.sin_zero));
|
||||||
|
|
||||||
|
/* Bind socket with address struct */
|
||||||
|
bind(udpSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
|
||||||
|
|
||||||
|
struct sockaddr_in srcAddr;
|
||||||
|
socklen_t srcLen = sizeof(srcAddr);
|
||||||
|
|
||||||
|
while (isUdpListenerRunning) {
|
||||||
|
|
||||||
|
if ((ret = poll(fds, 1, timeoutMs)) == 1) {
|
||||||
|
ssize_t bufferLen =
|
||||||
|
recvfrom(udpSocket, buffer, ARTNET_MAX_BUFFER, 0, (struct sockaddr *)&srcAddr, &srcLen);
|
||||||
|
|
||||||
|
if (bufferLen <= ARTNET_MAX_BUFFER && bufferLen > sizeof(artnetHeader_t)) {
|
||||||
|
artnetHeader_t *artnetHeader = (artnetHeader_t *)buffer;
|
||||||
|
|
||||||
|
if (memcmp(artnetHeader->id, ARTNET_ID, sizeof(ARTNET_ID)) == 0) {
|
||||||
|
switch (artnetHeader->opCode) {
|
||||||
|
case OpDmx:
|
||||||
|
if (bufferLen >= 20) {
|
||||||
|
artDmx_t *artDmx = (artDmx_t *)buffer;
|
||||||
|
uint16_t dmxLength = (artDmx->lengthHi << 8) | artDmx->lengthLo;
|
||||||
|
uint8_t *artDmxBuffer = artDmxBufferArray[artDmx->subUni & 0x00ff];
|
||||||
|
if (dmxLength <= 512) {
|
||||||
|
// store for later use
|
||||||
|
memcpy(artDmxBuffer, artDmx->data, dmxLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpPoll:
|
||||||
|
artnet_send_poll_reply(srcAddr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (ret < 0) {
|
||||||
|
fprintf(stderr, "error polling %d: %s\n", udpSocket, strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(udpSocket);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void artnet_send_poll_reply(struct sockaddr_in srcAddr) {
|
||||||
|
/* Configure settings in address struct */
|
||||||
|
srcAddr.sin_family = AF_INET;
|
||||||
|
srcAddr.sin_port = htons(ARTNET_PORT);
|
||||||
|
memset(srcAddr.sin_zero, '\0', sizeof(srcAddr.sin_zero));
|
||||||
|
|
||||||
|
sendto(udpSocket, (uint8_t *)&artPollReply, sizeof(artPollReply_t), 0,
|
||||||
|
(struct sockaddr *)&srcAddr, sizeof(srcAddr));
|
||||||
|
}
|
@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
void artnet_init();
|
void artnet_init();
|
||||||
|
|
||||||
int artnet_read(artDmx_t **artDmx);
|
void start_artnet_bg_worker();
|
||||||
|
|
||||||
|
void stop_artnet_bg_worker();
|
||||||
|
|
||||||
|
int artnet_get_dmx_data(unsigned int univerve, uint8_t **dmxData);
|
||||||
|
|
||||||
#endif // __RPI_ARTNET_H__
|
#endif // __RPI_ARTNET_H__
|
146
RpiLedBars/src/tasks/cava/rpi_cava.c
Normal file
146
RpiLedBars/src/tasks/cava/rpi_cava.c
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
#include "rpi_cava.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAXLINECHAR 128 * (5 + 1) + 1
|
||||||
|
|
||||||
|
pid_t cavaPid;
|
||||||
|
bool isFifoReaderRunning = false;
|
||||||
|
pthread_t fifoReader;
|
||||||
|
int cavaFifo;
|
||||||
|
char lineBuffer[MAXLINECHAR];
|
||||||
|
uint16_t buffer[128 + 2];
|
||||||
|
|
||||||
|
static void *fifo_to_buffer(void *arg);
|
||||||
|
|
||||||
|
void setup_cava() {
|
||||||
|
if ((cavaPid = fork()) == -1) {
|
||||||
|
perror("fork");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cavaPid == 0) {
|
||||||
|
/* Child process*/
|
||||||
|
pthread_setschedprio(pthread_self(), 30);
|
||||||
|
char *args[] = {"/usr/local/bin/cava", "-p", "/home/pi/LedBars/RpiLedBars/cava_config", NULL};
|
||||||
|
|
||||||
|
if (execv(args[0], args) != 0) {
|
||||||
|
perror("execv");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_cava() {
|
||||||
|
stop_cava_bg_worker();
|
||||||
|
kill(cavaPid, SIGTERM);
|
||||||
|
}
|
||||||
|
|
||||||
|
void start_cava_bg_worker() {
|
||||||
|
isFifoReaderRunning = true;
|
||||||
|
pthread_create(&fifoReader, NULL, fifo_to_buffer, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop_cava_bg_worker() {
|
||||||
|
isFifoReaderRunning = false;
|
||||||
|
if (pthread_join(fifoReader, NULL) != 0) {
|
||||||
|
perror("pthread_join");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_cava_buffer(uint16_t **buffer_dst) {
|
||||||
|
*buffer_dst = buffer;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t valueIndex = 0, charOffset = 0;
|
||||||
|
char strValue[6] = "0\0";
|
||||||
|
bool hasToBeDiscarded = false;
|
||||||
|
|
||||||
|
static void *fifo_to_buffer(void *arg) {
|
||||||
|
struct pollfd fds[1];
|
||||||
|
int timeoutMs;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if ((cavaFifo = open("/tmp/cava_output", O_RDONLY | O_NONBLOCK)) < 0) {
|
||||||
|
perror("open");
|
||||||
|
close_cava();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(fds, 0, sizeof(fds));
|
||||||
|
fds[0].fd = cavaFifo;
|
||||||
|
fds[0].events = POLLIN;
|
||||||
|
timeoutMs = 10;
|
||||||
|
|
||||||
|
hasToBeDiscarded = true;
|
||||||
|
|
||||||
|
while (isFifoReaderRunning) {
|
||||||
|
|
||||||
|
if ((ret = poll(fds, 1, timeoutMs)) == 1) {
|
||||||
|
int nread;
|
||||||
|
nread = read(cavaFifo, lineBuffer, 128 + 1);
|
||||||
|
|
||||||
|
if (nread >= 0) {
|
||||||
|
for (size_t i = 0; i < nread; ++i) {
|
||||||
|
char current = lineBuffer[i];
|
||||||
|
|
||||||
|
if (hasToBeDiscarded) {
|
||||||
|
if (current == '\n') {
|
||||||
|
charOffset = 0;
|
||||||
|
strValue[charOffset] = '\0';
|
||||||
|
valueIndex = 0;
|
||||||
|
hasToBeDiscarded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if ('0' <= current && current <= '9') {
|
||||||
|
strValue[charOffset++] = current;
|
||||||
|
} else if (current == '\n' || current == ';') {
|
||||||
|
strValue[charOffset] = '\0';
|
||||||
|
charOffset = 0;
|
||||||
|
buffer[valueIndex++] = atoi(strValue);
|
||||||
|
|
||||||
|
if (current == '\n' || valueIndex > 129) {
|
||||||
|
valueIndex = 0;
|
||||||
|
|
||||||
|
if (valueIndex > 129) {
|
||||||
|
fprintf(stderr, "Buffer overflow, \\n missed, discarding next\n");
|
||||||
|
hasToBeDiscarded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Unexpected char %d [%c]\n", current, current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (errno != EAGAIN) {
|
||||||
|
perror("read");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (ret < 0) {
|
||||||
|
fprintf(stderr, "error polling %d: %s\n", cavaFifo, strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(cavaFifo);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
16
RpiLedBars/src/tasks/cava/rpi_cava.h
Normal file
16
RpiLedBars/src/tasks/cava/rpi_cava.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#if !defined(__RPI_CAVA_H__)
|
||||||
|
#define __RPI_CAVA_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void setup_cava();
|
||||||
|
|
||||||
|
int get_cava_buffer(uint16_t **buffer_dst);
|
||||||
|
|
||||||
|
void close_cava();
|
||||||
|
|
||||||
|
void start_cava_bg_worker();
|
||||||
|
|
||||||
|
void stop_cava_bg_worker();
|
||||||
|
|
||||||
|
#endif /* __RPI_CAVA_H__ */
|
11
RpiLedBars/wget-log
Normal file
11
RpiLedBars/wget-log
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
--2021-07-24 09:17:35-- https://forums.adafruit.com/download/file.php?id=49380
|
||||||
|
Resolving forums.adafruit.com (forums.adafruit.com)... 34.200.112.132
|
||||||
|
Connecting to forums.adafruit.com (forums.adafruit.com)|34.200.112.132|:443... connected.
|
||||||
|
HTTP request sent, awaiting response... 200 OK
|
||||||
|
Length: 1510 (1.5K) [application/octet-stream]
|
||||||
|
Saving to: ‘file.php?id=49380’
|
||||||
|
|
||||||
|
file.php?id=49380 0%[ ] 0 --.-KB/s
file.php?id=49380 100%[===================================================================================================================>] 1.47K --.-KB/s in 0s
|
||||||
|
|
||||||
|
2021-07-24 09:17:36 (3.03 MB/s) - ‘file.php?id=49380’ saved [1510/1510]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user