Created
December 13, 2016 19:13
-
-
Save anonymous/1a9c58c6035d442a6a3ad7d73f27d6c0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Simple Raspberry Pi2 Audio Spectrum Analyser | |
// The analog to digital sampling is done on a | |
// coprocessor board using an MSP430 microcontroller | |
// connected to the RP2 with the i2c bus. | |
// | |
// Written By: CFLanger | |
// 10jun15 | |
// | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <errno.h> | |
#include <unistd.h> | |
#include <math.h> | |
#include <time.h> | |
#include <string.h> | |
#include <wiringPi.h> | |
#include <wiringPiSPI.h> | |
#include <wiringPiI2C.h> | |
#include <pthread.h> | |
#include "lcd_fonts.h" | |
#include "lcd_Fonts.c" | |
#include "mailbox.h" | |
#include "gpu_fft.h" | |
#define TOTAL_ADC_SAMPS 4096 | |
#define MAX_I2C_READ_SIZE 8192 | |
#define FFT_N 4096 | |
#define SAMPLING_RATE 44500.0 // Samples/second | |
#define HZ_PER_BIN SAMPLING_RATE / FFT_N | |
#define LCD_CS_HI digitalWrite(5, HIGH); | |
#define LCD_CS_LO digitalWrite(5, LOW); | |
#define LCD_RS_HI digitalWrite(6, HIGH); | |
#define LCD_RS_LO digitalWrite(6, LOW); | |
#define LCD_DISP_OFF 0xAE // turn LCD panel OFF | |
#define LCD_DISP_ON 0xAF // turn LCD panel ON | |
#define LCD_SET_LINE 0x40 // set line for COM0 (6 lsbs = ST5:ST4:ST3:ST2:ST1:ST0) | |
#define LCD_SET_PAGE 0xB0 // set page address (4 lsbs = P3:P2:P1:P0) | |
#define LCD_SET_COL_HI 0x10 // set column address (4 lsbs = Y7:Y6:Y5:Y4) | |
#define LCD_SET_COL_LO 0x00 // set column address (4 lsbs = Y3:Y2:Y1:Y0) | |
#define LCD_SET_ADC_NOR 0xA0 // ADC set for normal direction | |
#define LCD_SET_ADC_REV 0xA1 // ADC set for reverse direction | |
#define LCD_DISP_NOR 0xA6 // normal pixel display | |
#define LCD_DISP_REV 0xA7 // reverse pixel display | |
#define LCD_EON_OFF 0xA4 // normal display mode | |
#define LCD_EON_ON 0xA5 // entire dsplay on | |
#define LCD_SET_BIAS_0 0xA2 // set LCD bias select to 0 | |
#define LCD_SET_BIAS_1 0xA3 // set LCD boas select to 1 | |
#define LCD_SET_MODIFY 0xE0 // set modfy read mode | |
#define LCD_CLR_MODIFY 0xEE // clear modify read mode | |
#define LCD_RESET 0xE2 // soft reset command | |
#define LCD_SET_SHL_NOR 0xC0 // set COM direction normal | |
#define LCD_SET_SHL_REV 0xC8 // set COM direction reverse | |
#define LCD_PWR_CTL 0x28 // set power control (3 lsbs = VC:VR:VF) | |
#define LCD_REG_RESISTOR 0x20 // set regulator resistor (3 lsbs = R2:R1:R0) | |
#define LCD_REF_VOLT_MODE 0x81 // set reference voltage mode (first of dual command) | |
#define LCD_REF_VOLT_REG 0x00 // set reference voltage register (6 lsbs = SV5:SV4:SV3:SV2:SV1:SV0) | |
#define LCD_ST_IND_MODE_0 0xAC // set static indicator mode to 0 | |
#define LCD_ST_IND_MODE_1 0xAD // set static indicator mode to 1 | |
#define LCD_ST_IND_REG 0x00 // set the static indicator register (2 lsbs = S1:S0) | |
#define LCD_NOP 0xE3 // LCD controller NOP command | |
#define LCD_TEST_CMD_1 0xF0 // LCD Test Instruction 1.. do not use | |
#define LCD_TEST_CMD_2 0x90 // LCD Test Instruction 2.. do not use | |
#define X_BYTES 128 | |
#define Y_BYTES 8 | |
#define SCRN_LEFT 0 | |
#define SCRN_TOP 0 | |
#define SCRN_RIGHT 127 | |
#define SCRN_BOTTOM 63 | |
const unsigned char img_rplogo_bmp_char_table[] = { | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* [ ] */ | |
0x03, 0xFF, 0xE0, 0x07, 0xFF, 0x80, /* [ ************* ************ ] */ | |
0x07, 0xFF, 0xF8, 0x1F, 0xFF, 0xE0, /* [ **************** **************** ] */ | |
0x18, 0x00, 0x14, 0x38, 0x00, 0x18, /* [ ** * * *** ** ] */ | |
0x18, 0x00, 0x06, 0x60, 0x00, 0x18, /* [ ** ** ** ** ] */ | |
0x3C, 0x00, 0x06, 0xE0, 0x00, 0x38, /* [ **** ** *** *** ] */ | |
0x3C, 0x40, 0x06, 0xE0, 0x02, 0x38, /* [ **** * ** *** * *** ] */ | |
0x1C, 0x0C, 0x03, 0xC0, 0x30, 0x38, /* [ *** ** **** ** *** ] */ | |
0x1C, 0x04, 0x03, 0xC0, 0x60, 0x78, /* [ *** * **** ** **** ] */ | |
0x1C, 0x03, 0x03, 0xC0, 0x80, 0x38, /* [ *** ** **** * *** ] */ | |
0x1C, 0x00, 0xE3, 0xC7, 0x00, 0x70, /* [ *** *** **** *** *** ] */ | |
0x0E, 0x00, 0x77, 0xEC, 0x00, 0x70, /* [ *** *** ****** ** *** ] */ | |
0x07, 0x00, 0x1F, 0xF8, 0x00, 0xE0, /* [ *** ********** *** ] */ | |
0x03, 0x80, 0x1F, 0xF0, 0x01, 0x80, /* [ *** ********* ** ] */ | |
0x03, 0x80, 0x1F, 0xF8, 0x03, 0x80, /* [ *** ********** *** ] */ | |
0x01, 0xE0, 0x3F, 0xF8, 0x07, 0x80, /* [ **** *********** **** ] */ | |
0x00, 0xF0, 0x7F, 0xFE, 0x0E, 0x00, /* [ **** ************** *** ] */ | |
0x00, 0x7F, 0xFE, 0xFF, 0xFC, 0x00, /* [ ************** ************** ] */ | |
0x00, 0x7F, 0xF8, 0x1F, 0xFC, 0x00, /* [ ************ *********** ] */ | |
0x00, 0xFD, 0xF0, 0x1F, 0x7E, 0x00, /* [ ****** ***** ***** ****** ] */ | |
0x01, 0x81, 0xC0, 0x07, 0x07, 0x00, /* [ ** *** *** *** ] */ | |
0x03, 0x03, 0xC0, 0x07, 0x83, 0x80, /* [ ** **** **** *** ] */ | |
0x03, 0x03, 0xC0, 0x07, 0x81, 0x80, /* [ ** **** **** ** ] */ | |
0x07, 0x0F, 0xF8, 0x1F, 0xE1, 0x80, /* [ *** ********* ******** ** ] */ | |
0x06, 0x1F, 0xFF, 0xFF, 0xE1, 0xC0, /* [ ** ************************ *** ] */ | |
0x06, 0x3F, 0xBF, 0xF8, 0x79, 0xC0, /* [ ** ******* *********** **** *** ] */ | |
0x06, 0xF8, 0x07, 0xE0, 0x3E, 0xC0, /* [ ** ***** ****** ***** ** ] */ | |
0x07, 0xF0, 0x07, 0xE0, 0x1F, 0xC0, /* [ ******* ****** ******* ] */ | |
0x0F, 0xF0, 0x07, 0xC0, 0x1F, 0xF0, /* [ ******** ***** ********* ] */ | |
0x39, 0xC0, 0x03, 0xC0, 0x06, 0x38, /* [ *** *** **** ** *** ] */ | |
0x30, 0xC0, 0x03, 0xC0, 0x02, 0x18, /* [ ** ** **** * ** ] */ | |
0x60, 0x80, 0x03, 0xC0, 0x02, 0x0C, /* [ ** * **** * ** ] */ | |
0x60, 0x80, 0x03, 0xC0, 0x02, 0x0C, /* [ ** * **** * ** ] */ | |
0x60, 0x80, 0x07, 0xE0, 0x02, 0x0C, /* [ ** * ****** * ** ] */ | |
0x60, 0x80, 0x07, 0xE0, 0x02, 0x0C, /* [ ** * ****** * ** ] */ | |
0x41, 0xC0, 0x07, 0xE0, 0x03, 0x0C, /* [ * *** ****** ** ** ] */ | |
0x61, 0xE0, 0x07, 0xE0, 0x07, 0x0C, /* [ ** **** ****** *** ** ] */ | |
0x61, 0xE0, 0x0F, 0xF0, 0x0F, 0x0C, /* [ ** **** ******** **** ** ] */ | |
0x63, 0xFF, 0xF8, 0x1F, 0xFF, 0x88, /* [ ** *************** ************** * ] */ | |
0x33, 0xFF, 0xE0, 0x07, 0xFF, 0x98, /* [ ** ************* ************ ** ] */ | |
0x3F, 0xFF, 0xE0, 0x07, 0xFD, 0xF8, /* [ ***************** ********* ****** ] */ | |
0x3F, 0xFF, 0xC0, 0x07, 0xF0, 0x70, /* [ **************** ******* *** ] */ | |
0x1C, 0x0F, 0xC0, 0x03, 0xC0, 0x70, /* [ *** ****** **** *** ] */ | |
0x1C, 0x07, 0xC0, 0x03, 0xC0, 0x70, /* [ *** ***** **** *** ] */ | |
0x1C, 0x03, 0xC0, 0x03, 0x80, 0x70, /* [ *** **** *** *** ] */ | |
0x0C, 0x03, 0xC0, 0x03, 0x80, 0x70, /* [ ** **** *** *** ] */ | |
0x0C, 0x03, 0xC0, 0x07, 0x80, 0x70, /* [ ** **** **** *** ] */ | |
0x0E, 0x01, 0xC0, 0x07, 0x00, 0x60, /* [ *** *** *** ** ] */ | |
0x07, 0x01, 0xF0, 0x1F, 0x00, 0xE0, /* [ *** ***** ***** *** ] */ | |
0x03, 0x00, 0xF8, 0x1F, 0x01, 0xC0, /* [ ** ***** ***** *** ] */ | |
0x03, 0x81, 0xFF, 0xFF, 0x01, 0x80, /* [ *** ***************** ** ] */ | |
0x03, 0x81, 0xFF, 0xFF, 0x03, 0x80, /* [ *** ***************** *** ] */ | |
0x00, 0xFF, 0xFC, 0x1F, 0xFE, 0x00, /* [ ************** ************ ] */ | |
0x00, 0x7F, 0xF0, 0x07, 0xFE, 0x00, /* [ *********** ********** ] */ | |
0x00, 0x3F, 0xC0, 0x07, 0xF8, 0x00, /* [ ******** ******** ] */ | |
0x00, 0x03, 0xC0, 0x07, 0x80, 0x00, /* [ **** **** ] */ | |
0x00, 0x03, 0xE0, 0x07, 0x80, 0x00, /* [ ***** **** ] */ | |
0x00, 0x00, 0xF0, 0x1E, 0x00, 0x00, /* [ **** **** ] */ | |
0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, /* [ *********** ] */ | |
0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, /* [ ********** ] */ | |
0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, /* [ ****** ] */ | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* [ ] */ | |
}; | |
unsigned char l_display_array[Y_BYTES][X_BYTES]; | |
const unsigned char l_mask_array[8] = { 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 }; | |
unsigned char disp_buf[50]; | |
int graph_data[28]; | |
double dbl_graph_data[FFT_N]; | |
double FFT_windowed_input[FFT_N]; | |
double fft_magnitudes[FFT_N]; | |
float DTFT_input[FFT_N]; | |
double maxval; | |
int maxbin, gsb; | |
int m, run, filter; | |
char hostName[128]; | |
int inputzoom_x0 = 0; | |
int inputzoom_x1 = 4095; | |
int outputzoom_x0 = 0; | |
int outputzoom_x1 = 100; | |
unsigned char i2c_rbuf[MAX_I2C_READ_SIZE], *ucptr; | |
int audio_samples[TOTAL_ADC_SAMPS], *iptr; | |
pthread_t t_MenuThread, t_SampleThread; | |
pthread_attr_t attr; | |
// | |
// IIR Filter Variables | |
// | |
#define IIR_NUMBER_OF_STAGES 10 // Define the filter length (SET TO MAXIMUM of all filter coeff sets pulled in !!! ) | |
#define SAMPLE_RATE SAMPLING_RATE // Define the analog sample rate | |
#define SIGLIB_IIR_DELAY_SIZE 2L // Size of IIR filter state vector | |
#define SIGLIB_IIR_COEFFS_PER_BIQUAD 5L // Number of coefficients per biquad | |
float pFilterState[ 2 * IIR_NUMBER_OF_STAGES * SIGLIB_IIR_DELAY_SIZE ]; // Need twice number of poles and zeros for BPFs and BSFs | |
// High Pass, 10 pole, 40hz | |
float IIRCoeffs_10p40hz [] = { /* IIR Coefficient array */ | |
1.000000, -2.000000, 1.000000, /* Alpha coeffs */ | |
-1.988873, 0.988905, /* Beta coeffs */ | |
1.000000, -2.000000, 1.000000, /* Alpha coeffs */ | |
-1.988873, 0.988905, /* Beta coeffs */ | |
1.000000, -2.000000, 1.000000, /* Alpha coeffs */ | |
-1.989953, 0.989985, /* Beta coeffs */ | |
1.000000, -2.000000, 1.000000, /* Alpha coeffs */ | |
-1.989953, 0.989985, /* Beta coeffs */ | |
1.000000, -2.000000, 1.000000, /* Alpha coeffs */ | |
-1.992012, 0.992044, /* Beta coeffs */ | |
1.000000, -2.000000, 1.000000, /* Alpha coeffs */ | |
-1.992012, 0.992044, /* Beta coeffs */ | |
1.000000, -2.000000, 1.000000, /* Alpha coeffs */ | |
-1.994853, 0.994885, /* Beta coeffs */ | |
1.000000, -2.000000, 1.000000, /* Alpha coeffs */ | |
-1.994853, 0.994885, /* Beta coeffs */ | |
1.000000, -2.000000, 1.000000, /* Alpha coeffs */ | |
-1.998203, 0.998235, /* Beta coeffs */ | |
1.000000, -2.000000, 1.000000, /* Alpha coeffs */ | |
-1.998203, 0.998235, /* Beta coeffs */ | |
}; | |
// Bandpass, 5 pole, 100 hz - 16000hz | |
float IIRCoeffs_5p10016khz [] = { /* IIR Coefficient array */ | |
1.000000, 2.000000, 1.000000, /* Alpha coeffs */ | |
0.109062, 0.002974, /* Beta coeffs */ | |
1.000000, 2.000000, 1.000000, /* Alpha coeffs */ | |
0.125722, 0.110945 , /* Beta coeffs */ | |
1.000000, 2.000000, 1.000000, /* Alpha coeffs */ | |
0.125722, 0.110945 , /* Beta coeffs */ | |
1.000000, 2.000000, 1.000000, /* Alpha coeffs */ | |
0.184266, 0.534542, /* Beta coeffs */ | |
1.000000, 2.000000, 1.000000, /* Alpha coeffs */ | |
0.184266, 0.534542, /* Beta coeffs */ | |
1.000000, -2.000000, 1.000000, /* Alpha coeffs */ | |
-1.991202, 0.991401, /* Beta coeffs */ | |
1.000000, -2.000000, 1.000000, /* Alpha coeffs */ | |
-1.991202, 0.991401 , /* Beta coeffs */ | |
1.000000, -2.000000, 1.000000, /* Alpha coeffs */ | |
-1.977130, 0.977330, /* Beta coeffs */ | |
1.000000, -2.000000, 1.000000, /* Alpha coeffs */ | |
-1.977130, 0.977330, /* Beta coeffs */ | |
1.000000, -2.000000, 1.000000, /* Alpha coeffs */ | |
-1.971604, 0.971806, /* Beta coeffs */ | |
}; | |
void lcd_init(void); | |
void lcd_erase(void); | |
void lcd_clear_area(unsigned char left, unsigned char top, unsigned char right, unsigned char bottom); | |
void lcd_invert_area(unsigned char left, unsigned char top, unsigned char right, unsigned char bottom); | |
void lcd_horz_line(unsigned char left, unsigned char right, unsigned char row); | |
void lcd_vert_line(unsigned char top, unsigned char bottom, unsigned char column); | |
void lcd_clr_horz_line(unsigned char left, unsigned char right, unsigned char row); | |
void lcd_clr_vert_line(unsigned char top, unsigned char bottom, unsigned char column); | |
void lcd_box(unsigned char left, unsigned char top, unsigned char right, unsigned char bottom); | |
void lcd_clr_box(unsigned char left, unsigned char top, unsigned char right, unsigned char bottom); | |
void lcd_glyph(unsigned char left, unsigned char top, unsigned char width, unsigned char height, | |
unsigned char const *glyph, unsigned char store_width); | |
void lcd_text(unsigned char left, unsigned char top, unsigned char font, char *str); | |
void lcd_update(unsigned char top, unsigned char bottom); | |
void put_serial_to_lcd(unsigned char data); | |
void write_input(void); | |
void write_output(void); | |
unsigned int microseconds(void); | |
int get_data(int fd, int bytes_to_read); | |
void sfiDtft(float f, float *re, float *im); | |
float ZoomIn(int peak_pt); | |
void mkWebPage( float zf, float zfreq); | |
// Fast Approximations from ARM CMSIS DSP LIBRARY | |
float arm_sin_f32(float x); | |
float arm_cos_f32(float x); | |
void SDAIir( float * input_buffer, float * output_buffer, float * pState, float * pCoeffs, int NumberOfStages, int SampleLength); | |
void SIFIir(float * pState, const int NumberOfStages); | |
void quit( void ); | |
void * MenuThread(void * tdata); | |
void * SampleThread(void * tdata); | |
void mkInpGNUplt( float zbin, float zfreq); | |
void mkOutpGNUplt( void); | |
void graph_signal_spectrum( float zf, float zfreq); | |
void write_graphdata(void); | |
// | |
// Do Werk here | |
// | |
int main() { | |
register int i, j, k; | |
int ret, log2_N, jobs, N, mb = mbox_open(); | |
unsigned t[2]; | |
double max, multiplier; | |
float zf, freq, f0, f1; | |
int rc; | |
long g; | |
FILE *f; | |
struct GPU_FFT_COMPLEX *base; | |
struct GPU_FFT *fft; | |
log2_N = 12; // 12 = 4096 // 8 <= log2_N <= 21 | |
jobs = 1; | |
N = 1<<log2_N; // FFT length | |
ret = gpu_fft_prepare(mb, log2_N, GPU_FFT_FWD, jobs, &fft); // call once | |
switch(ret) { | |
case -1: printf("Unable to enable V3D. Please check your firmware is up to date.\n"); return -1; | |
case -2: printf("log2_N=%d not supported. Try between 8 and 21.\n", log2_N); return -1; | |
case -3: printf("Out of memory. Try a smaller batch or increase GPU memory.\n"); return -1; | |
case -4: printf("Unable to map Videocore peripherals into ARM memory space.\n"); return -1; | |
} | |
if(wiringPiSetup() == -1) { | |
printf("Unable to start wiringPi: %s\n"); | |
return -1; | |
} | |
// Initialize and set thread detached attribute | |
pthread_attr_init(&attr); | |
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); | |
run = 1; | |
filter = 0; | |
gethostname(hostName, 127); | |
atexit(quit); | |
wiringPiSPISetup(1,31000000) ; // lcd spi bus | |
pinMode(5,OUTPUT); // lcd chip select | |
pinMode(6,OUTPUT); // lcd register select | |
lcd_init(); | |
lcd_erase(); | |
lcd_glyph(0,0,47,62,img_rplogo_bmp_char_table,6); | |
lcd_update(SCRN_TOP, SCRN_BOTTOM); | |
lcd_text(53,3,FONT_TERMINAL_SIX_DOT,"FFT Analysis"); | |
lcd_update(SCRN_TOP,SCRN_BOTTOM); | |
SIFIir( pFilterState, IIR_NUMBER_OF_STAGES ); | |
// Create Thread | |
rc = pthread_create(&t_MenuThread, &attr, MenuThread, (void *)g); | |
if (rc) { | |
printf("ERROR; return code from pthread_create() is %d\n", rc); | |
exit(-1); | |
} | |
// Create Thread | |
rc = pthread_create(&t_SampleThread, &attr, SampleThread, (void *)g); | |
if (rc) { | |
printf("ERROR; return code from pthread_create() is %d\n", rc); | |
exit(-1); | |
} | |
while(run) { | |
ucptr = &i2c_rbuf[0]; | |
iptr = &audio_samples[0]; | |
// Pack the bytes together | |
// Samples from ADC are 12 bits | |
for(m = 0; m < TOTAL_ADC_SAMPS; m++) { | |
*iptr = (int)(*ucptr++ & 0xFF); // LSB | |
*iptr++ |= ((int)(*ucptr++ & 0xFF)) << 8; // MSB | |
} | |
// Remove DC bias from amplifier (ADC reference = 2.5 VDC, 12 bit samples, FSC=4096) | |
for(i = 0; i < TOTAL_ADC_SAMPS; i++) | |
audio_samples[i] -= 2048; | |
// Fill input arrays | |
for(i = 0; i < TOTAL_ADC_SAMPS; i++) | |
DTFT_input[i] = (float)audio_samples[i]; | |
#if 0 | |
// Mix in other sine wave(s) or use for testing digital filter | |
f = fopen("outputdata.dat", "r+"); | |
for(i = 0; i < TOTAL_ADC_SAMPS; i++) { | |
// DTFT_input[i] += (1*1000 * sin(65.185547 * (2 * GPU_FFT_PI) * i / SAMPLING_RATE)); | |
fscanf(f, "%d,%f,%f\n", &rc, &f0, &f1); | |
printf("%d, %f, %f\n", rc,f0,f1); | |
DTFT_input[i] += (f0*1000 * sin(f1 * (2 * GPU_FFT_PI) * i / SAMPLING_RATE)); | |
} | |
fclose(f); | |
#endif | |
if(filter) { | |
// Apply digital IIR filter, 40 hz, High Pass Butterworth | |
SDAIir( DTFT_input, DTFT_input, pFilterState, IIRCoeffs_10p40hz, 10, 4096 ); | |
// SDAIir( DTFT_input, DTFT_input, pFilterState, IIRCoeffs_5p10016khz, 5, 4096 ); | |
} | |
for(i = 0; i < TOTAL_ADC_SAMPS; i++) | |
FFT_windowed_input[i] = (double)DTFT_input[i]; | |
// Apply a Hanning window to the input array | |
for(i = 0; i < FFT_N; i++) { | |
multiplier = 0.5 * (1 - cos(2 * GPU_FFT_PI * i / FFT_N)); | |
FFT_windowed_input[i] = multiplier * FFT_windowed_input[i]; | |
} | |
// Instruct the FFT function where the input data is | |
base = fft->in; | |
for(i=0; i < N; i++) | |
base[i].re = base[i].im = 0; // zero imaginary parts | |
for(i = 0; i < N; i++) { | |
base[i].re = FFT_windowed_input[i]; // real parts | |
base[i].im = 0; | |
} | |
// Perform the FFT | |
gpu_fft_execute(fft); | |
// Point to the output array | |
base = fft->out; | |
// Compute "sum of squares" magnitude of the real parts | |
for(i = 0; i < N; i++) | |
fft_magnitudes[i] = (base[i].re * base[i].re) + (base[i].im * base[i].im); | |
// Find the maximum bin energy | |
k = 0; | |
j = 0; | |
max = 0; | |
for( i= 0; i < N / 2; i++) { | |
if(fft_magnitudes[i] >= max) { | |
max=fft_magnitudes[i]; | |
j = i; | |
} | |
} | |
maxbin = j; | |
maxval = fft_magnitudes[j]; | |
// Normalize | |
for(i = 0; i < FFT_N/2; i++) { | |
dbl_graph_data[i] = fft_magnitudes[i]; | |
fft_magnitudes[i] = (1.0 * fft_magnitudes[i]) / maxval; | |
} | |
// Find the real peak via golden search | |
usleep(1); // Yield to OS | |
t[0] = microseconds(); // Measure how long Zoom function takes | |
zf = ZoomIn(j); | |
t[1] = microseconds(); | |
// Save the data for gnuplot | |
mkInpGNUplt( zf, freq ); | |
write_input( ); | |
// Save the data for gnuplot | |
mkOutpGNUplt(); | |
write_output(); | |
// Calculate the frequency | |
freq = HZ_PER_BIN * zf; | |
// Display the results on 128x64 LCD | |
sprintf(disp_buf,"jbin=%d ",j); | |
lcd_text(53,15,FONT_TERMINAL_SIX_DOT,disp_buf); | |
sprintf(disp_buf,"zbin=%.2f",zf); | |
lcd_text(53,24,FONT_TERMINAL_SIX_DOT,disp_buf); | |
sprintf(disp_buf,"us=%u ",(t[1] - t[0])); | |
lcd_text(53,33,FONT_TERMINAL_SIX_DOT,disp_buf); | |
sprintf(disp_buf,"Fj=%8.3f",(float)j * HZ_PER_BIN) ; | |
lcd_text(53,42,FONT_TERMINAL_SIX_DOT,disp_buf); | |
sprintf(disp_buf,"Fz=%8.3f",freq); | |
lcd_text(53,51,FONT_TERMINAL_SIX_DOT,disp_buf); | |
lcd_update(SCRN_TOP,SCRN_BOTTOM); | |
mkWebPage(zf, freq ); | |
// graph_signal_spectrum( zf, freq ); | |
} | |
gpu_fft_release(fft); // Videocore memory lost if not freed ! | |
} | |
// | |
// The sampling thread | |
// | |
void * SampleThread(void * tdata) { | |
int fd; | |
if((fd = wiringPiI2CSetup(0x50)) < 0) | |
printf("I2CSetup Failed, %i\n",fd); | |
while(run) { | |
// Start sampling the MSP430 5419A coprocessor (external microcontroller) | |
wiringPiI2CWrite(fd, 0x20); | |
get_data( fd, MAX_I2C_READ_SIZE); | |
} | |
close(fd); | |
} | |
void graph_signal_spectrum( float zf, float zfreq) { | |
double max; | |
int s, i; | |
unsigned char l, t, b; | |
unsigned int g, v, k; | |
double ftmp[28]; | |
int centerfreqs[] = { 3,4,5,7,9,11,14,18,23,28,36,46,57,73,92,115,147,184,230,289,368,460,579,736,920,1150,1472,1840 }; | |
for(i = 0; i < 28; i++) | |
ftmp[i] = dbl_graph_data[ centerfreqs[i] ] ; | |
// Find the maximum | |
max = 0; | |
for( i= 0; i < 28; i++) { | |
if( ftmp[i] >= max) { | |
max = ftmp[i] ; | |
} | |
} | |
// Normalize to 50 | |
for(i = 0; i < 28; i++) { | |
graph_data[i] = (int)( (50.0 * ftmp[i] ) / max ); | |
if(graph_data[i] > 50 ) graph_data[i] = 50; | |
} | |
write_graphdata(); | |
lcd_clear_area( SCRN_LEFT, SCRN_TOP, SCRN_RIGHT, SCRN_BOTTOM ); | |
for( g = 0; g < 28; g++ ) { | |
l = 14 + ( g * 3 ); | |
b = 50 - graph_data[ g ]; | |
lcd_box( l, b, l+2, 50 ); | |
} | |
lcd_vert_line( 0, 53, 13 ); // VERTICAL Y axis line | |
lcd_horz_line( 10, 13, 0 ); | |
lcd_horz_line( 10, 13, 10 ); | |
lcd_horz_line( 10, 13, 20 ); | |
lcd_horz_line( 10, 13, 30 ); | |
lcd_horz_line( 10, 13, 40 ); | |
lcd_horz_line( 10, 13, 50 ); | |
// Draw Grid | |
for( v = 0; v < 5; v++ ) { | |
for( t = 0, k = 0; k < 6; k++ ) { | |
lcd_horz_line( 15 + t, 15 + t, v * 10 ); | |
t += 15; | |
} | |
} | |
strcpy( ( char * )disp_buf, "10" ); | |
lcd_text( 0, 0, FONT_TERMINAL_FIVE_DOT, ( char * )disp_buf ); | |
strcpy( ( char * )disp_buf, "8" ); | |
lcd_text( 3, 10, FONT_TERMINAL_FIVE_DOT, ( char * )disp_buf ); | |
strcpy( ( char * )disp_buf, "6" ); | |
lcd_text( 3, 20, FONT_TERMINAL_FIVE_DOT, ( char * )disp_buf ); | |
strcpy( ( char * )disp_buf, "4" ); | |
lcd_text( 3, 30, FONT_TERMINAL_FIVE_DOT, ( char * )disp_buf ); | |
strcpy( ( char * )disp_buf, "2" ); | |
lcd_text( 3, 40, FONT_TERMINAL_FIVE_DOT, ( char * )disp_buf ); | |
sprintf(disp_buf,"%.2f %8.3f %d",zf, zfreq, filter); | |
lcd_text(0,57,FONT_TERMINAL_SIX_DOT,disp_buf); | |
lcd_update( SCRN_TOP, SCRN_BOTTOM ); | |
} | |
// | |
// The menu thread | |
// | |
void * MenuThread(void * tdata) { | |
char s[128]; | |
char tmpData[50]; | |
printf("Raspberry Pi - Simple Spectrum Analyzer\n"); | |
printf("=======================================\n"); | |
while(run) { | |
if(fgets(s, 80, stdin) != NULL) { | |
if(s[0] == 'q') { | |
printf("Quiting from q press..\n"); | |
run = 0; | |
usleep(100000); | |
break; | |
} else if(s[0] == 'f' || s[0] == 'F' ) { | |
filter ^= 1; | |
} else if( strstr(s, "ix0=") != NULL ) { | |
strcpy(tmpData, strstr(s, "ix0=") + 4); | |
inputzoom_x0 = atoi(tmpData); | |
printf("inputzoom_x0 = %d\n", inputzoom_x0 ); | |
} else if( strstr(s, "ix1=") != NULL ) { | |
strcpy(tmpData, strstr(s, "ix1=") + 4); | |
inputzoom_x1 = atoi(tmpData); | |
printf("inputzoom_x1 = %d\n", inputzoom_x1 ); | |
} else if( strstr(s, "ox0=") != NULL ) { | |
strcpy(tmpData, strstr(s, "ox0=") + 4); | |
outputzoom_x0 = atoi(tmpData); | |
printf("outputzoom_x0 = %d\n", outputzoom_x0 ); | |
} else if( strstr(s, "ox1=") != NULL ) { | |
strcpy(tmpData, strstr(s, "ox1=") + 4); | |
outputzoom_x1 = atoi(tmpData); | |
printf("outputzoom_x1 = %d\n", outputzoom_x1 ); | |
} else if(s[0] == 'r' || s[0] == 'R' ) { | |
inputzoom_x0 = 0; | |
inputzoom_x1 = 4095; | |
outputzoom_x0 = 0; | |
outputzoom_x1 = 2000; | |
} else if( strstr(s, "gsb=") != NULL ) { | |
strcpy(tmpData, strstr(s, "gsb=") + 4); | |
gsb = atoi(tmpData); | |
printf("gsb = %d\n", gsb ); | |
} | |
} | |
usleep(100000); | |
} | |
} | |
// | |
// | |
float ZoomIn(int peak_pt) { | |
// proprietary, sorry | |
} | |
// | |
// The discrete Time Fourier transform | |
// | |
void sfiDtft( float f, float * re, float * im ) { | |
// proprietary, sorry | |
} | |
// | |
// Measure Time | |
// | |
unsigned int microseconds(void) { | |
struct timespec ts; | |
clock_gettime(CLOCK_REALTIME, &ts); | |
return ts.tv_sec * 1000000 + ts.tv_nsec / 1000; | |
} | |
// | |
// Get data from i2c i/o stream | |
// | |
int get_data( int fd, int bytes_to_read) { | |
int i; | |
int bytes_read; | |
int rr = 500; | |
char * wp; | |
bytes_read = 0; | |
i = read(fd, i2c_rbuf, bytes_to_read); | |
if(i > 0) | |
bytes_read += i; | |
while (--rr && bytes_read < bytes_to_read) { | |
usleep(100); | |
i = read(fd,&(i2c_rbuf[bytes_read]),bytes_to_read - bytes_read); | |
if(i > 0) | |
bytes_read += i; | |
} | |
return bytes_read; | |
} | |
// | |
// Write Data to file | |
// | |
void write_input(void) { | |
FILE *f; | |
int i; | |
f = fopen("inputdata.dat", "w+"); | |
if(f == NULL) { | |
printf("File open error for write %s\n", "inputdata.dat"); | |
return; | |
} | |
for(i = 0; i < FFT_N; ++i) { | |
fprintf(f,"%d %f\n",i,FFT_windowed_input[i]); | |
} | |
fclose(f); | |
} | |
// | |
// Write LCD Graph Data to file | |
// | |
void write_graphdata(void) { | |
FILE *f; | |
int i; | |
f = fopen("graphdata.dat", "w+"); | |
if(f == NULL) { | |
printf("File open error for write %s\n", "graphdata.dat"); | |
return; | |
} | |
for(i = 0; i < 28; ++i) { | |
fprintf(f,"%d %d\n",i,graph_data[i]); | |
} | |
fclose(f); | |
} | |
// | |
// Write Data to file | |
// | |
void write_output(void) { | |
FILE *f; | |
int i; | |
f = fopen("outputdata.dat", "w+"); | |
if(f == NULL) { | |
printf("File open error for write %s\n", "outputdata.dat"); | |
return; | |
} | |
for(i = 0; i < FFT_N / 2; ++i) { | |
fprintf(f,"%d %f %f\n",i, fft_magnitudes[i], i * HZ_PER_BIN); | |
} | |
fclose(f); | |
} | |
// | |
// Make the web page showing the graphs | |
// | |
void mkWebPage( float zbin, float zfreq ) { | |
FILE *f; | |
int x = 0; | |
time_t tt = time(NULL); | |
struct tm tm = *localtime(&tt); | |
char tbuf[128]; | |
system("gnuplot inputdata.plt"); | |
system("gnuplot outputdata.plt"); | |
f = fopen("evotmp.html", "w"); | |
if(f == NULL) { | |
printf("File open error %s\n", "evotmp.html"); | |
return; | |
} | |
fputs("<!doctype html>", f); | |
fputs("<html>", f); | |
fputs("<head>", f); | |
fputs("<meta charset=UTF-8>", f); | |
fprintf(f, "<meta %s%c%c\n", "Content-Type:text/html;charset=iso-8859-1>", 13, 10); | |
// fputs("<meta http-equiv=Refresh Content=2; URL=index.html>", f); | |
fprintf(f,"<title>Pievo @%s</title> ", hostName); | |
fputs("<style type=text/css>", f); | |
fputs("th { text-align: center; }", f); | |
fputs("td { text-align: center; }", f); | |
fputs("p { text-align: center; }", f); | |
fputs("th { color: black; }", f); | |
fputs("th { background-color: lightgray; }", f); | |
fputs("td { font-family: arial; font-size: 14pt; }", f); | |
fputs("th { font-family: arial; font-size: 12pt; }", f); | |
fputs("p { font-family: arial; font-size: 11pt; }", f); | |
fputs("</style>",f); | |
fputs("</head>", f); | |
fputs("<body>", f); | |
fprintf(f,"<h1><center><font size=+3>[Raspberry Pi GPU FFT], Last Update %d-%02d-%02d %02d:%02d:%02d %s</font></h1>", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_zone ); | |
fprintf(f,"<h1><center><font size=+3>Freq at Maxbin = %.3f Hz : Maxbin = %.3f : Filter = %s</font></h1>", zfreq, zbin, (filter)?"ON":"OFF" ); | |
fputs("</table>", f); | |
fputs("<table align='center'>", f); | |
fputs("<tr>",f); | |
fputs("<td><img src=./inputdata.png width=1600 height=500></td>", f); | |
fputs("</tr>",f); | |
fputs("<td><img src=./outputdata.png width=1600 height=500></td>", f); | |
fputs("</tr>",f); | |
fputs("</table>", f); | |
fputs("</body>", f); | |
fputs("</html>", f); | |
fclose(f); | |
system("sudo cp evotmp.html /var/www/html/index.html"); | |
system("sudo cp inputdata.png /var/www/html/inputdata.png"); | |
system("sudo cp outputdata.png /var/www/html/outputdata.png"); | |
} | |
// | |
// Make gnuplot input.plt file | |
// | |
void mkInpGNUplt( float zbin, float zfreq) { | |
FILE *f; | |
char tbuf[128]; | |
f = fopen("inputdata.plt", "w+"); | |
if(f == NULL) { | |
printf("File open error %s\n", "inputdata.plt"); | |
return; | |
} | |
fputs("set terminal png large enhanced font arial size 1600,500\n", f); | |
fputs("set title 'Windowed Samples'\n", f); | |
fputs("set output 'inputdata.png'\n", f); | |
fputs("set tics scale 5\n", f); | |
fputs("set mxtics 5\n", f); | |
fputs("set mytics 8\n", f); | |
fputs("unset autoscale\n", f); | |
fputs("set grid\n", f); | |
// fprintf(f, "set label 'Freq at Maxbin = %.3f Hz' at 55,3100\n", zfreq ); | |
// fprintf(f, "set label 'Maxbin = %.3f' at 55,2600\n", zbin ); | |
// fprintf(f, "set label 'Filter = %s' at 55,2100\n", (filter)?"ON":"OFF" ); | |
fputs("set ylabel 'ADC Counts'\n", f); | |
fprintf(f, "set xrange[%d:%d]\n", inputzoom_x0, inputzoom_x1); | |
fputs("set yrange[-5000:5000]\n", f); | |
fputs("plot 'inputdata.dat' with lines lw 1 lt rgb 'red'\n", f); | |
fputs("exit 0", f); | |
fclose(f); | |
} | |
// | |
// Make gnuplot output.plt file | |
// | |
void mkOutpGNUplt( void) { | |
FILE *f; | |
char tbuf[128]; | |
f = fopen("outputdata.plt", "w+"); | |
if(f == NULL) { | |
printf("File open error %s\n", "outputdata.plt"); | |
return; | |
} | |
fputs("set terminal png large enhanced font arial size 1600,500\n", f); | |
fputs("set output 'outputdata.png'\n", f); | |
fputs("set title 'Frequency Spectrum'\n", f); | |
fputs("set tics scale 2\n", f); | |
fputs("set xtics 10\n", f); | |
fputs("set ytics 0.1\n", f); | |
fputs("unset autoscale\n", f); | |
fputs("set grid\n", f); | |
fputs("set ylabel 'Magnitude'\n", f); | |
fprintf(f, "set xrange[%d:%d]\n", outputzoom_x0, outputzoom_x1); | |
fputs("set yrange[0:1]\n", f); | |
fputs("set style data boxes\n", f); | |
fputs("set boxwidth 0.4 relative\n", f); | |
fputs("set style fill solid 1\n", f); | |
fputs("plot 'outputdata.dat' using 1:2 with boxes lt rgb 'blue'\n", f); | |
fputs("exit 0", f); | |
fclose(f); | |
} | |
// | |
// Shutdown gracefully | |
// | |
void quit( void ) { | |
int rc; | |
long t; | |
void *status; | |
printf("test program quiting...\n"); | |
run = 0; | |
printf("Thread Joining\n"); | |
/* Free attribute and wait for the other threads */ | |
pthread_attr_destroy(&attr); | |
rc = pthread_join(t_MenuThread, &status); | |
if (rc) { | |
printf("ERROR; return code from pthread_join() is %d\n", rc); | |
} | |
// rc = pthread_join(t_SampleThread, &status); | |
// if (rc) { | |
// printf("ERROR; return code from pthread_join() is %d\n", rc); | |
// } | |
// printf("QUIT: completed join with Menu thread having a status of %ld\n",(long)status); | |
printf("bye bye ...\n"); | |
} | |
void SDAIir( float * input_buffer, float * output_buffer, float * pState, float * pCoeffs, int NumberOfStages, int SampleLength) { | |
float FeedbackSumOfProducts, pSrc, pDst; | |
int i, j; | |
for (j = 0; j < SampleLength; j++) | |
{ | |
pSrc = *input_buffer++; | |
for (i = 0; i < NumberOfStages; i++) | |
{ | |
FeedbackSumOfProducts = pSrc - (*(pCoeffs+3) * *pState) - | |
(*(pCoeffs+4) * *(pState+1)); /* Feedback */ | |
pDst = (*pCoeffs * FeedbackSumOfProducts) + | |
(*(pCoeffs + 1) * *pState) + | |
(*(pCoeffs + 2) * *(pState+1)); /* Feedforward */ | |
*(pState+1) = *pState; /* Move delayed samples */ | |
*pState = FeedbackSumOfProducts; | |
pSrc = pDst; /* Set input for next stage */ | |
pState += SIGLIB_IIR_DELAY_SIZE; /* Increment array pointers */ | |
pCoeffs += SIGLIB_IIR_COEFFS_PER_BIQUAD; | |
} | |
pState -= (SIGLIB_IIR_DELAY_SIZE * NumberOfStages); /* Reset array pointers */ | |
pCoeffs -= (SIGLIB_IIR_COEFFS_PER_BIQUAD * NumberOfStages); | |
*output_buffer++ = pDst; | |
} | |
} | |
void SIFIir(float * pState, const int NumberOfStages) { | |
int i; | |
// Initialise the filter state array to 0 | |
for (i = 0; i < (NumberOfStages * SIGLIB_IIR_DELAY_SIZE); i++) { | |
*pState++ = 0; | |
} | |
} | |
// | |
// ****************************************************************** | |
// Fast ARM CMSIS DSP library function for cos and sin | |
// ****************************************************************** | |
// | |
#define TABLE_SIZE 256 | |
static const float sinTable[259] = { | |
-0.024541229009628296f, 0.000000000000000000f, 0.024541229009628296f, | |
0.049067676067352295f, 0.073564566671848297f, 0.098017141222953796f, | |
0.122410677373409270f, 0.146730467677116390f, | |
0.170961886644363400f, 0.195090323686599730f, 0.219101235270500180f, | |
0.242980182170867920f, 0.266712754964828490f, 0.290284663438797000f, | |
0.313681751489639280f, 0.336889863014221190f, | |
0.359895050525665280f, 0.382683426141738890f, 0.405241310596466060f, | |
0.427555084228515630f, 0.449611335992813110f, 0.471396744251251220f, | |
0.492898195981979370f, 0.514102756977081300f, | |
0.534997642040252690f, 0.555570244789123540f, 0.575808167457580570f, | |
0.595699310302734380f, 0.615231573581695560f, 0.634393274784088130f, | |
0.653172850608825680f, 0.671558976173400880f, | |
0.689540565013885500f, 0.707106769084930420f, 0.724247097969055180f, | |
0.740951120853424070f, 0.757208824157714840f, 0.773010432720184330f, | |
0.788346409797668460f, 0.803207516670227050f, | |
0.817584812641143800f, 0.831469595432281490f, 0.844853579998016360f, | |
0.857728600502014160f, 0.870086967945098880f, 0.881921291351318360f, | |
0.893224298954010010f, 0.903989315032958980f, | |
0.914209783077239990f, 0.923879504203796390f, 0.932992815971374510f, | |
0.941544055938720700f, 0.949528157711029050f, 0.956940352916717530f, | |
0.963776051998138430f, 0.970031261444091800f, | |
0.975702106952667240f, 0.980785250663757320f, 0.985277652740478520f, | |
0.989176511764526370f, 0.992479562759399410f, 0.995184719562530520f, | |
0.997290432453155520f, 0.998795449733734130f, | |
0.999698817729949950f, 1.000000000000000000f, 0.999698817729949950f, | |
0.998795449733734130f, 0.997290432453155520f, 0.995184719562530520f, | |
0.992479562759399410f, 0.989176511764526370f, | |
0.985277652740478520f, 0.980785250663757320f, 0.975702106952667240f, | |
0.970031261444091800f, 0.963776051998138430f, 0.956940352916717530f, | |
0.949528157711029050f, 0.941544055938720700f, | |
0.932992815971374510f, 0.923879504203796390f, 0.914209783077239990f, | |
0.903989315032958980f, 0.893224298954010010f, 0.881921291351318360f, | |
0.870086967945098880f, 0.857728600502014160f, | |
0.844853579998016360f, 0.831469595432281490f, 0.817584812641143800f, | |
0.803207516670227050f, 0.788346409797668460f, 0.773010432720184330f, | |
0.757208824157714840f, 0.740951120853424070f, | |
0.724247097969055180f, 0.707106769084930420f, 0.689540565013885500f, | |
0.671558976173400880f, 0.653172850608825680f, 0.634393274784088130f, | |
0.615231573581695560f, 0.595699310302734380f, | |
0.575808167457580570f, 0.555570244789123540f, 0.534997642040252690f, | |
0.514102756977081300f, 0.492898195981979370f, 0.471396744251251220f, | |
0.449611335992813110f, 0.427555084228515630f, | |
0.405241310596466060f, 0.382683426141738890f, 0.359895050525665280f, | |
0.336889863014221190f, 0.313681751489639280f, 0.290284663438797000f, | |
0.266712754964828490f, 0.242980182170867920f, | |
0.219101235270500180f, 0.195090323686599730f, 0.170961886644363400f, | |
0.146730467677116390f, 0.122410677373409270f, 0.098017141222953796f, | |
0.073564566671848297f, 0.049067676067352295f, | |
0.024541229009628296f, 0.000000000000000122f, -0.024541229009628296f, | |
-0.049067676067352295f, -0.073564566671848297f, -0.098017141222953796f, | |
-0.122410677373409270f, -0.146730467677116390f, | |
-0.170961886644363400f, -0.195090323686599730f, -0.219101235270500180f, | |
-0.242980182170867920f, -0.266712754964828490f, -0.290284663438797000f, | |
-0.313681751489639280f, -0.336889863014221190f, | |
-0.359895050525665280f, -0.382683426141738890f, -0.405241310596466060f, | |
-0.427555084228515630f, -0.449611335992813110f, -0.471396744251251220f, | |
-0.492898195981979370f, -0.514102756977081300f, | |
-0.534997642040252690f, -0.555570244789123540f, -0.575808167457580570f, | |
-0.595699310302734380f, -0.615231573581695560f, -0.634393274784088130f, | |
-0.653172850608825680f, -0.671558976173400880f, | |
-0.689540565013885500f, -0.707106769084930420f, -0.724247097969055180f, | |
-0.740951120853424070f, -0.757208824157714840f, -0.773010432720184330f, | |
-0.788346409797668460f, -0.803207516670227050f, | |
-0.817584812641143800f, -0.831469595432281490f, -0.844853579998016360f, | |
-0.857728600502014160f, -0.870086967945098880f, -0.881921291351318360f, | |
-0.893224298954010010f, -0.903989315032958980f, | |
-0.914209783077239990f, -0.923879504203796390f, -0.932992815971374510f, | |
-0.941544055938720700f, -0.949528157711029050f, -0.956940352916717530f, | |
-0.963776051998138430f, -0.970031261444091800f, | |
-0.975702106952667240f, -0.980785250663757320f, -0.985277652740478520f, | |
-0.989176511764526370f, -0.992479562759399410f, -0.995184719562530520f, | |
-0.997290432453155520f, -0.998795449733734130f, | |
-0.999698817729949950f, -1.000000000000000000f, -0.999698817729949950f, | |
-0.998795449733734130f, -0.997290432453155520f, -0.995184719562530520f, | |
-0.992479562759399410f, -0.989176511764526370f, | |
-0.985277652740478520f, -0.980785250663757320f, -0.975702106952667240f, | |
-0.970031261444091800f, -0.963776051998138430f, -0.956940352916717530f, | |
-0.949528157711029050f, -0.941544055938720700f, | |
-0.932992815971374510f, -0.923879504203796390f, -0.914209783077239990f, | |
-0.903989315032958980f, -0.893224298954010010f, -0.881921291351318360f, | |
-0.870086967945098880f, -0.857728600502014160f, | |
-0.844853579998016360f, -0.831469595432281490f, -0.817584812641143800f, | |
-0.803207516670227050f, -0.788346409797668460f, -0.773010432720184330f, | |
-0.757208824157714840f, -0.740951120853424070f, | |
-0.724247097969055180f, -0.707106769084930420f, -0.689540565013885500f, | |
-0.671558976173400880f, -0.653172850608825680f, -0.634393274784088130f, | |
-0.615231573581695560f, -0.595699310302734380f, | |
-0.575808167457580570f, -0.555570244789123540f, -0.534997642040252690f, | |
-0.514102756977081300f, -0.492898195981979370f, -0.471396744251251220f, | |
-0.449611335992813110f, -0.427555084228515630f, | |
-0.405241310596466060f, -0.382683426141738890f, -0.359895050525665280f, | |
-0.336889863014221190f, -0.313681751489639280f, -0.290284663438797000f, | |
-0.266712754964828490f, -0.242980182170867920f, | |
-0.219101235270500180f, -0.195090323686599730f, -0.170961886644363400f, | |
-0.146730467677116390f, -0.122410677373409270f, -0.098017141222953796f, | |
-0.073564566671848297f, -0.049067676067352295f, | |
-0.024541229009628296f, -0.000000000000000245f, 0.024541229009628296f | |
}; | |
static const float cosTable[260] = { | |
0.999698817729949950f, 1.000000000000000000f, 0.999698817729949950f, | |
0.998795449733734130f, 0.997290432453155520f, 0.995184719562530520f, | |
0.992479562759399410f, 0.989176511764526370f, | |
0.985277652740478520f, 0.980785250663757320f, 0.975702106952667240f, | |
0.970031261444091800f, 0.963776051998138430f, 0.956940352916717530f, | |
0.949528157711029050f, 0.941544055938720700f, | |
0.932992815971374510f, 0.923879504203796390f, 0.914209783077239990f, | |
0.903989315032958980f, 0.893224298954010010f, 0.881921291351318360f, | |
0.870086967945098880f, 0.857728600502014160f, | |
0.844853579998016360f, 0.831469595432281490f, 0.817584812641143800f, | |
0.803207516670227050f, 0.788346409797668460f, 0.773010432720184330f, | |
0.757208824157714840f, 0.740951120853424070f, | |
0.724247097969055180f, 0.707106769084930420f, 0.689540565013885500f, | |
0.671558976173400880f, 0.653172850608825680f, 0.634393274784088130f, | |
0.615231573581695560f, 0.595699310302734380f, | |
0.575808167457580570f, 0.555570244789123540f, 0.534997642040252690f, | |
0.514102756977081300f, 0.492898195981979370f, 0.471396744251251220f, | |
0.449611335992813110f, 0.427555084228515630f, | |
0.405241310596466060f, 0.382683426141738890f, 0.359895050525665280f, | |
0.336889863014221190f, 0.313681751489639280f, 0.290284663438797000f, | |
0.266712754964828490f, 0.242980182170867920f, | |
0.219101235270500180f, 0.195090323686599730f, 0.170961886644363400f, | |
0.146730467677116390f, 0.122410677373409270f, 0.098017141222953796f, | |
0.073564566671848297f, 0.049067676067352295f, | |
0.024541229009628296f, 0.000000000000000061f, -0.024541229009628296f, | |
-0.049067676067352295f, -0.073564566671848297f, -0.098017141222953796f, | |
-0.122410677373409270f, -0.146730467677116390f, | |
-0.170961886644363400f, -0.195090323686599730f, -0.219101235270500180f, | |
-0.242980182170867920f, -0.266712754964828490f, -0.290284663438797000f, | |
-0.313681751489639280f, -0.336889863014221190f, | |
-0.359895050525665280f, -0.382683426141738890f, -0.405241310596466060f, | |
-0.427555084228515630f, -0.449611335992813110f, -0.471396744251251220f, | |
-0.492898195981979370f, -0.514102756977081300f, | |
-0.534997642040252690f, -0.555570244789123540f, -0.575808167457580570f, | |
-0.595699310302734380f, -0.615231573581695560f, -0.634393274784088130f, | |
-0.653172850608825680f, -0.671558976173400880f, | |
-0.689540565013885500f, -0.707106769084930420f, -0.724247097969055180f, | |
-0.740951120853424070f, -0.757208824157714840f, -0.773010432720184330f, | |
-0.788346409797668460f, -0.803207516670227050f, | |
-0.817584812641143800f, -0.831469595432281490f, -0.844853579998016360f, | |
-0.857728600502014160f, -0.870086967945098880f, -0.881921291351318360f, | |
-0.893224298954010010f, -0.903989315032958980f, | |
-0.914209783077239990f, -0.923879504203796390f, -0.932992815971374510f, | |
-0.941544055938720700f, -0.949528157711029050f, -0.956940352916717530f, | |
-0.963776051998138430f, -0.970031261444091800f, | |
-0.975702106952667240f, -0.980785250663757320f, -0.985277652740478520f, | |
-0.989176511764526370f, -0.992479562759399410f, -0.995184719562530520f, | |
-0.997290432453155520f, -0.998795449733734130f, | |
-0.999698817729949950f, -1.000000000000000000f, -0.999698817729949950f, | |
-0.998795449733734130f, -0.997290432453155520f, -0.995184719562530520f, | |
-0.992479562759399410f, -0.989176511764526370f, | |
-0.985277652740478520f, -0.980785250663757320f, -0.975702106952667240f, | |
-0.970031261444091800f, -0.963776051998138430f, -0.956940352916717530f, | |
-0.949528157711029050f, -0.941544055938720700f, | |
-0.932992815971374510f, -0.923879504203796390f, -0.914209783077239990f, | |
-0.903989315032958980f, -0.893224298954010010f, -0.881921291351318360f, | |
-0.870086967945098880f, -0.857728600502014160f, | |
-0.844853579998016360f, -0.831469595432281490f, -0.817584812641143800f, | |
-0.803207516670227050f, -0.788346409797668460f, -0.773010432720184330f, | |
-0.757208824157714840f, -0.740951120853424070f, | |
-0.724247097969055180f, -0.707106769084930420f, -0.689540565013885500f, | |
-0.671558976173400880f, -0.653172850608825680f, -0.634393274784088130f, | |
-0.615231573581695560f, -0.595699310302734380f, | |
-0.575808167457580570f, -0.555570244789123540f, -0.534997642040252690f, | |
-0.514102756977081300f, -0.492898195981979370f, -0.471396744251251220f, | |
-0.449611335992813110f, -0.427555084228515630f, | |
-0.405241310596466060f, -0.382683426141738890f, -0.359895050525665280f, | |
-0.336889863014221190f, -0.313681751489639280f, -0.290284663438797000f, | |
-0.266712754964828490f, -0.242980182170867920f, | |
-0.219101235270500180f, -0.195090323686599730f, -0.170961886644363400f, | |
-0.146730467677116390f, -0.122410677373409270f, -0.098017141222953796f, | |
-0.073564566671848297f, -0.049067676067352295f, | |
-0.024541229009628296f, -0.000000000000000184f, 0.024541229009628296f, | |
0.049067676067352295f, 0.073564566671848297f, 0.098017141222953796f, | |
0.122410677373409270f, 0.146730467677116390f, | |
0.170961886644363400f, 0.195090323686599730f, 0.219101235270500180f, | |
0.242980182170867920f, 0.266712754964828490f, 0.290284663438797000f, | |
0.313681751489639280f, 0.336889863014221190f, | |
0.359895050525665280f, 0.382683426141738890f, 0.405241310596466060f, | |
0.427555084228515630f, 0.449611335992813110f, 0.471396744251251220f, | |
0.492898195981979370f, 0.514102756977081300f, | |
0.534997642040252690f, 0.555570244789123540f, 0.575808167457580570f, | |
0.595699310302734380f, 0.615231573581695560f, 0.634393274784088130f, | |
0.653172850608825680f, 0.671558976173400880f, | |
0.689540565013885500f, 0.707106769084930420f, 0.724247097969055180f, | |
0.740951120853424070f, 0.757208824157714840f, 0.773010432720184330f, | |
0.788346409797668460f, 0.803207516670227050f, | |
0.817584812641143800f, 0.831469595432281490f, 0.844853579998016360f, | |
0.857728600502014160f, 0.870086967945098880f, 0.881921291351318360f, | |
0.893224298954010010f, 0.903989315032958980f, | |
0.914209783077239990f, 0.923879504203796390f, 0.932992815971374510f, | |
0.941544055938720700f, 0.949528157711029050f, 0.956940352916717530f, | |
0.963776051998138430f, 0.970031261444091800f, | |
0.975702106952667240f, 0.980785250663757320f, 0.985277652740478520f, | |
0.989176511764526370f, 0.992479562759399410f, 0.995184719562530520f, | |
0.997290432453155520f, 0.998795449733734130f, | |
0.999698817729949950f, 1.000000000000000000f, 0.999698817729949950f, | |
0.998795449733734130f | |
}; | |
// | |
// FAST aaproximation routine for sine using lookup table | |
// | |
float arm_sin_f32( float x) { | |
float sinVal, fract, in; /* Temporary variables for input, output */ | |
long index; /* Index variable */ | |
unsigned long tableSize = (unsigned long) TABLE_SIZE; /* Initialise tablesize */ | |
float wa, wb, wc, wd; /* Cubic interpolation coefficients */ | |
float a, b, c, d; /* Four nearest output values */ | |
float *tablePtr; /* Pointer to table */ | |
long n; | |
float fractsq, fractby2, fractby6, fractby3, fractsqby2; | |
float oneminusfractby2; | |
float frby2xfrsq, frby6xfrsq; | |
/* input x is in radians */ | |
/* Scale the input to [0 1] range from [0 2*PI] , divide input by 2*pi */ | |
in = x * 0.159154943092f; | |
/* Calculation of floor value of input */ | |
n = (long) in; | |
/* Make negative values towards -infinity */ | |
if(x < 0.0f) | |
{ | |
n = n - 1; | |
} | |
/* Map input value to [0 1] */ | |
in = in - (float) n; | |
/* Calculation of index of the table */ | |
index = (unsigned long) (tableSize * in); | |
/* fractional value calculation */ | |
fract = ((float) tableSize * in) - (float) index; | |
/* Checking min and max index of table */ | |
if(index < 0) | |
{ | |
index = 0; | |
} | |
else if(index > 256) | |
{ | |
index = 256; | |
} | |
/* Initialise table pointer */ | |
tablePtr = (float *) & sinTable[index]; | |
/* Read four nearest values of input value from the sin table */ | |
a = tablePtr[0]; | |
b = tablePtr[1]; | |
c = tablePtr[2]; | |
d = tablePtr[3]; | |
/* Cubic interpolation process */ | |
fractsq = fract * fract; | |
fractby2 = fract * 0.5f; | |
fractby6 = fract * 0.166666667f; | |
fractby3 = fract * 0.3333333333333f; | |
fractsqby2 = fractsq * 0.5f; | |
frby2xfrsq = (fractby2) * fractsq; | |
frby6xfrsq = (fractby6) * fractsq; | |
oneminusfractby2 = 1.0f - fractby2; | |
wb = fractsqby2 - fractby3; | |
wc = (fractsqby2 + fract); | |
wa = wb - frby6xfrsq; | |
wb = frby2xfrsq - fractsq; | |
sinVal = wa * a; | |
wc = wc - frby2xfrsq; | |
wd = (frby6xfrsq) - fractby6; | |
wb = wb + oneminusfractby2; | |
/* Calculate sin value */ | |
sinVal = (sinVal + (b * wb)) + ((c * wc) + (d * wd)); | |
/* Return the output value */ | |
return (sinVal); | |
} | |
// | |
// FAST aaproximation routine for cosine using lookup table | |
// | |
float arm_cos_f32(float x) { | |
float cosVal, fract, in; | |
long index; | |
unsigned long tableSize = (unsigned long) TABLE_SIZE; | |
float wa, wb, wc, wd; | |
float a, b, c, d; | |
float *tablePtr; | |
long n; | |
float fractsq, fractby2, fractby6, fractby3, fractsqby2; | |
float oneminusfractby2; | |
float frby2xfrsq, frby6xfrsq; | |
/* input x is in radians */ | |
/* Scale the input to [0 1] range from [0 2*PI] , divide input by 2*pi */ | |
in = x * 0.159154943092f; | |
/* Calculation of floor value of input */ | |
n = (long) in; | |
/* Make negative values towards -infinity */ | |
if(x < 0.0f) | |
{ | |
n = n - 1; | |
} | |
/* Map input value to [0 1] */ | |
in = in - (float) n; | |
/* Calculation of index of the table */ | |
index = (unsigned long) (tableSize * in); | |
/* fractional value calculation */ | |
fract = ((float) tableSize * in) - (float) index; | |
/* Checking min and max index of table */ | |
if(index < 0) | |
{ | |
index = 0; | |
} | |
else if(index > 256) | |
{ | |
index = 256; | |
} | |
/* Initialise table pointer */ | |
tablePtr = (float *) & cosTable[index]; | |
/* Read four nearest values of input value from the cos table */ | |
a = tablePtr[0]; | |
b = tablePtr[1]; | |
c = tablePtr[2]; | |
d = tablePtr[3]; | |
/* Cubic interpolation process */ | |
fractsq = fract * fract; | |
fractby2 = fract * 0.5f; | |
fractby6 = fract * 0.166666667f; | |
fractby3 = fract * 0.3333333333333f; | |
fractsqby2 = fractsq * 0.5f; | |
frby2xfrsq = (fractby2) * fractsq; | |
frby6xfrsq = (fractby6) * fractsq; | |
oneminusfractby2 = 1.0f - fractby2; | |
wb = fractsqby2 - fractby3; | |
wc = (fractsqby2 + fract); | |
wa = wb - frby6xfrsq; | |
wb = frby2xfrsq - fractsq; | |
cosVal = wa * a; | |
wc = wc - frby2xfrsq; | |
wd = (frby6xfrsq) - fractby6; | |
wb = wb + oneminusfractby2; | |
/* Calculate cos value */ | |
cosVal = (cosVal + (b * wb)) + ((c * wc) + (d * wd)); | |
/* Return the output value */ | |
return (cosVal); | |
} | |
// | |
// ****************************************************************** | |
// 128x64 Pixel LCD Routines START | |
// ****************************************************************** | |
// | |
void lcd_init(void) { | |
/* initialize the port control lines to the LCD module */ | |
LCD_CS_HI; /* set chip select high output */ | |
LCD_RS_HI; /* set the RS line low as output */ | |
LCD_RS_LO; /* select register for control port */ | |
LCD_CS_LO; /* enable interface via chip select */ | |
/* program the controller operational state */ | |
put_serial_to_lcd( LCD_SET_ADC_NOR ); /* set ADC REVERSE */ | |
// NEW FOCUS DISPLAY G126GLGFYSY6WT | |
put_serial_to_lcd( LCD_SET_SHL_REV ); /* set SHL REVERSE */ | |
put_serial_to_lcd( LCD_SET_BIAS_0 ); /* set for the low bias mode */ | |
put_serial_to_lcd( LCD_PWR_CTL+4 ); /* turn on the VC bit */ | |
delay(5); /* delay for the converter on */ | |
put_serial_to_lcd( LCD_PWR_CTL+6 ); /* now turn on VC+VR bits */ | |
delay(5); /* delay for the regulator on */ | |
put_serial_to_lcd( LCD_PWR_CTL+7 ); /* now turn on the VC+VR+VF */ | |
// NEW FOCUS DISPLAY G126GLGFYSY6WT | |
put_serial_to_lcd( LCD_REG_RESISTOR+7 ); /* set default resistor ratio */ | |
put_serial_to_lcd( LCD_REF_VOLT_MODE ); /* prime for the reference voltage */ | |
// Given percentage CONTRAST calculate register value i | |
put_serial_to_lcd( LCD_REF_VOLT_REG + 27 ); /* set default reference voltage select */ | |
delay(5); ; /* delay for power stabilize */ | |
put_serial_to_lcd( LCD_DISP_ON ); /* put the display on */ | |
put_serial_to_lcd( LCD_SET_LINE+0 ); /* set line for row 0 of display */ | |
put_serial_to_lcd( LCD_SET_PAGE+0 ); /* set page 0 */ | |
put_serial_to_lcd( LCD_SET_COL_HI+0 ); /* set column 0 */ | |
put_serial_to_lcd( LCD_SET_COL_LO+0 ); | |
LCD_CS_HI; /* force chip select back off */ | |
} | |
void put_serial_to_lcd( unsigned char data ) { | |
wiringPiSPIDataRW(1, (unsigned char *)&data, 1); | |
} | |
void lcd_erase(void) { | |
unsigned char p; | |
unsigned char i; | |
LCD_CS_LO; | |
for(p=0; p<9; p++) { | |
LCD_RS_LO; | |
put_serial_to_lcd( LCD_SET_PAGE+p ); /* set page */ | |
put_serial_to_lcd( LCD_SET_COL_HI+0 ); /* set column 0 */ | |
put_serial_to_lcd( LCD_SET_COL_LO+0 ); | |
LCD_RS_HI; | |
for(i=0; i<132; i++) | |
{ | |
put_serial_to_lcd( 0 ); /* clear the data */ | |
} | |
} | |
LCD_CS_HI; | |
} | |
void lcd_clear_area(unsigned char left, unsigned char top,unsigned char right, unsigned char bottom) { | |
unsigned char bit_pos; | |
unsigned char x; | |
unsigned char byte_offset; | |
unsigned char y_bits; | |
unsigned char remaining_bits; | |
unsigned char mask; | |
bit_pos = top & 0x07; /* get starting bit offset into byte */ | |
for(x = left; x <= right; x++) | |
{ | |
byte_offset = top >> 3; /* get byte offset into y direction */ | |
y_bits = (bottom - top) + 1; /* get length in the y direction to write */ | |
remaining_bits = 8 - bit_pos; /* number of bits left in byte */ | |
mask = l_mask_array[bit_pos]; /* get mask for this bit */ | |
while(y_bits) /* while there are still bits to write */ | |
{ | |
if((remaining_bits == 8) && (y_bits > 7)) | |
{ | |
/* here if we are byte aligned and have at least 1 byte to write */ | |
/* do the entire byte at once instead of bit by bit */ | |
while(y_bits > 7) /* while there are at least 8 more bits to do */ | |
{ | |
l_display_array[byte_offset][x] = 0x00; | |
byte_offset++; | |
y_bits -= 8; | |
} | |
} | |
else | |
{ | |
/* here if not byte aligned or an entire byte does not need written */ | |
/* thus do bit by bit */ | |
l_display_array[byte_offset][x] &= ~mask; | |
if(l_mask_array[0] & 0x80) | |
{ | |
mask >>= 1; | |
} | |
else | |
{ | |
mask <<= 1; | |
} | |
y_bits--; | |
remaining_bits--; | |
if(remaining_bits == 0) | |
{ | |
/* might have bust gotton byte aligned */ | |
/* so reset for beginning of a byte */ | |
remaining_bits = 8; | |
byte_offset++; | |
mask = l_mask_array[0]; | |
} | |
} | |
} | |
} | |
} | |
void lcd_invert_area(unsigned char left, unsigned char top, unsigned char right, unsigned char bottom) { | |
unsigned char bit_pos; | |
unsigned char x; | |
unsigned char byte_offset; | |
unsigned char y_bits; | |
unsigned char remaining_bits; | |
unsigned char mask; | |
bit_pos = top & 0x07; /* get starting bit offset into byte */ | |
for(x = left; x <= right; x++) | |
{ | |
byte_offset = top >> 3; /* get byte offset into y direction */ | |
y_bits = (bottom - top) + 1; /* get length in the x direction to write */ | |
remaining_bits = 8 - bit_pos; /* number of bits left in byte */ | |
mask = l_mask_array[bit_pos]; /* get mask for this bit */ | |
while(y_bits) /* while there are still bits to write */ | |
{ | |
if((remaining_bits == 8) && (y_bits > 7)) | |
{ | |
/* here if we are byte aligned and have at least 1 byte to write */ | |
/* do the entire byte at once instead of bit by bit */ | |
while(y_bits > 7) /* while there are at least 8 more bits to do */ | |
{ | |
l_display_array[byte_offset][x] ^= 0xFF; | |
byte_offset++; | |
y_bits -= 8; | |
} | |
} | |
else | |
{ | |
/* here if not byte aligned or an entire byte does not need written */ | |
/* thus do bit by bit */ | |
l_display_array[byte_offset][x] ^= mask; | |
if(l_mask_array[0] & 0x80) | |
{ | |
mask >>= 1; | |
} | |
else | |
{ | |
mask <<= 1; | |
} | |
y_bits--; | |
remaining_bits--; | |
if(remaining_bits == 0) | |
{ | |
/* might have bust gotton byte aligned */ | |
/* so reset for beginning of a byte */ | |
remaining_bits = 8; | |
byte_offset++; | |
mask = l_mask_array[0]; | |
} | |
} | |
} | |
} | |
} | |
void lcd_horz_line(unsigned char left, unsigned char right, unsigned char row) | |
{ | |
unsigned char bit_pos; | |
unsigned char byte_offset; | |
unsigned char mask; | |
unsigned char col; | |
bit_pos = row & 0x07; /* get the bit offset into a byte */ | |
byte_offset = row >> 3; /* get the byte offset into x array */ | |
mask = l_mask_array[bit_pos]; /* get the mask for this bit */ | |
for(col = left; col <= right; col++) | |
{ | |
l_display_array[byte_offset][col] |= mask; | |
} | |
} | |
void lcd_vert_line(unsigned char top, unsigned char bottom, unsigned char column) | |
{ | |
unsigned char bit_pos; | |
unsigned char byte_offset; | |
unsigned char y_bits; | |
unsigned char remaining_bits; | |
unsigned char mask; | |
bit_pos = top & 0x07; /* get starting bit offset into byte */ | |
byte_offset = top >> 3; /* get byte offset into y direction */ | |
y_bits = (bottom - top) + 1; /* get length in the x direction to write */ | |
remaining_bits = 8 - bit_pos; /* number of bits left in byte */ | |
mask = l_mask_array[bit_pos]; /* get mask for this bit */ | |
while(y_bits) /* while there are still bits to write */ | |
{ | |
if((remaining_bits == 8) && (y_bits > 7)) | |
{ | |
/* here if we are byte aligned and have at least 1 byte to write */ | |
/* do the entire byte at once instead of bit by bit */ | |
while(y_bits > 7) /* while there are at least 8 more bits to do */ | |
{ | |
l_display_array[byte_offset][column] = 0xFF; | |
byte_offset++; | |
y_bits -= 8; | |
} | |
} | |
else | |
{ | |
/* we are not byte aligned or an entire byte does not need written */ | |
/* do each individual bit */ | |
l_display_array[byte_offset][column] |= mask; | |
if(l_mask_array[0] & 0x80) | |
{ | |
mask >>= 1; | |
} | |
else | |
{ | |
mask <<= 1; | |
} | |
y_bits--; | |
remaining_bits--; | |
if(remaining_bits == 0) | |
{ | |
/* might have bust gotton byte aligned */ | |
/* so reset for beginning of a byte */ | |
remaining_bits = 8; | |
byte_offset++; | |
mask = l_mask_array[0]; | |
} | |
} | |
} | |
} | |
void lcd_clr_horz_line(unsigned char left, unsigned char right, unsigned char row) | |
{ | |
unsigned char bit_pos; | |
unsigned char byte_offset; | |
unsigned char mask; | |
unsigned char col; | |
bit_pos = row & 0x07; /* get the bit offset into a byte */ | |
byte_offset = row >> 3; /* get the byte offset into x array */ | |
mask = l_mask_array[bit_pos]; /* get the mask for this bit */ | |
for(col = left; col <= right; col++) | |
{ | |
l_display_array[byte_offset][col] &= ~mask; | |
} | |
} | |
void lcd_clr_vert_line(unsigned char top, unsigned char bottom, unsigned char column) | |
{ | |
unsigned char bit_pos; | |
unsigned char byte_offset; | |
unsigned char y_bits; | |
unsigned char remaining_bits; | |
unsigned char mask; | |
bit_pos = top & 0x07; /* get starting bit offset into byte */ | |
byte_offset = top >> 3; /* get byte offset into y direction */ | |
y_bits = (bottom - top) + 1; /* get length in the x direction to write */ | |
remaining_bits = 8 - bit_pos; /* number of bits left in byte */ | |
mask = l_mask_array[bit_pos]; /* get mask for this bit */ | |
while(y_bits) /* while there are still bits to write */ | |
{ | |
if((remaining_bits == 8) && (y_bits > 7)) | |
{ | |
/* here if we are byte aligned and have at least 1 byte to write */ | |
/* do the entire byte at once instead of bit by bit */ | |
while(y_bits > 7) /* while there are at least 8 more bits to do */ | |
{ | |
l_display_array[byte_offset][column] = 0x00; | |
byte_offset++; | |
y_bits -= 8; | |
} | |
} | |
else | |
{ | |
/* we are not byte aligned or an entire byte does not need written */ | |
/* do each individual bit */ | |
l_display_array[byte_offset][column] &= ~mask; | |
if(l_mask_array[0] & 0x80) | |
{ | |
mask >>= 1; | |
} | |
else | |
{ | |
mask <<= 1; | |
} | |
y_bits--; | |
remaining_bits--; | |
if(remaining_bits == 0) | |
{ | |
/* might have bust gotton byte aligned */ | |
/* so reset for beginning of a byte */ | |
remaining_bits = 8; | |
byte_offset++; | |
mask = l_mask_array[0]; | |
} | |
} | |
} | |
} | |
void lcd_box(unsigned char left, unsigned char top, unsigned char right, unsigned char bottom) | |
{ | |
/* to draw a box requires two vertical lines */ | |
lcd_vert_line(top,bottom,left); | |
lcd_vert_line(top,bottom,right); | |
/* and two horizonal lines */ | |
lcd_horz_line(left,right,top); | |
lcd_horz_line(left,right,bottom); | |
} | |
void lcd_clr_box(unsigned char left, unsigned char top, unsigned char right, unsigned char bottom) | |
{ | |
/* to undraw the box undraw the two vertical lines */ | |
lcd_clr_vert_line(top,bottom,left); | |
lcd_clr_vert_line(top,bottom,right); | |
/* and the two horizonal lines that comprise it */ | |
lcd_clr_horz_line(left,right,top); | |
lcd_clr_horz_line(left,right,bottom); | |
} | |
void lcd_glyph(unsigned char left, unsigned char top, unsigned char width, unsigned char height, unsigned char const *glyph, unsigned char store_width) | |
{ | |
unsigned char bit_pos; | |
unsigned char byte_offset; | |
unsigned char y_bits; | |
unsigned char remaining_bits; | |
unsigned char mask; | |
unsigned char char_mask; | |
unsigned char x; | |
unsigned char const *glyph_scan; | |
unsigned char glyph_offset; | |
bit_pos = top & 0x07; /* get the bit offset into a byte */ | |
glyph_offset = 0; /* start at left side of the glyph rasters */ | |
char_mask = 0x80; /* initial character glyph mask */ | |
for (x = left; x < (left + width); x++) | |
{ | |
byte_offset = top >> 3; /* get the byte offset into y direction */ | |
y_bits = height; /* get length in y direction to write */ | |
remaining_bits = 8 - bit_pos; /* number of bits left in byte */ | |
mask = l_mask_array[bit_pos]; /* get mask for this bit */ | |
glyph_scan = glyph + glyph_offset; /* point to base of the glyph */ | |
/* boundary checking here to account for the possibility of */ | |
/* write past the bottom of the screen. */ | |
while((y_bits) && (byte_offset < Y_BYTES)) /* while there are bits still to write */ | |
{ | |
/* check if the character pixel is set or not */ | |
if(*glyph_scan & char_mask) | |
{ | |
l_display_array[byte_offset][x] |= mask; /* set image pixel */ | |
} | |
else | |
{ | |
l_display_array[byte_offset][x] &= ~mask; /* clear the image pixel */ | |
} | |
if(l_mask_array[0] & 0x80) | |
{ | |
mask >>= 1; | |
} | |
else | |
{ | |
mask <<= 1; | |
} | |
y_bits--; | |
remaining_bits--; | |
if(remaining_bits == 0) | |
{ | |
/* just crossed over a byte boundry, reset byte counts */ | |
remaining_bits = 8; | |
byte_offset++; | |
mask = l_mask_array[0]; | |
} | |
/* bump the glyph scan to next raster */ | |
glyph_scan += store_width; | |
} | |
/* shift over to next glyph bit */ | |
char_mask >>= 1; | |
if(char_mask == 0) /* reset for next byte in raster */ | |
{ | |
char_mask = 0x80; | |
glyph_offset++; | |
} | |
} | |
} | |
void lcd_text(unsigned char left, unsigned char top, unsigned char font, char *str) | |
{ | |
unsigned char x = left; | |
unsigned char glyph; | |
unsigned char width; | |
unsigned char height; | |
unsigned char store_width; | |
unsigned char const *glyph_ptr; | |
while(*str != 0x00) | |
{ | |
glyph = (unsigned char)*str; | |
/* check to make sure the symbol is a legal one */ | |
/* if not then just replace it with the default character */ | |
if((glyph < fonts[font].glyph_beg) || (glyph > fonts[font].glyph_end)) | |
{ | |
glyph = fonts[font].glyph_def; | |
} | |
/* make zero based index into the font data arrays */ | |
glyph -= fonts[font].glyph_beg; | |
width = fonts[font].fixed_width; /* check if it is a fixed width */ | |
if(width == 0) | |
{ | |
width=fonts[font].width_table[glyph]; /* get the variable width instead */ | |
} | |
height = fonts[font].glyph_height; | |
store_width = fonts[font].store_width; | |
glyph_ptr = fonts[font].glyph_table + ((unsigned int)glyph * (unsigned int)store_width * (unsigned int)height); | |
/* range check / limit things here */ | |
if(x > SCRN_RIGHT) | |
{ | |
x = SCRN_RIGHT; | |
} | |
if((x + width) > SCRN_RIGHT+1) | |
{ | |
width = SCRN_RIGHT - x + 1; | |
} | |
if(top > SCRN_BOTTOM) | |
{ | |
top = SCRN_BOTTOM; | |
} | |
if((top + height) > SCRN_BOTTOM+1) | |
{ | |
height = SCRN_BOTTOM - top + 1; | |
} | |
lcd_glyph(x,top,width,height,glyph_ptr,store_width); /* plug symbol into audio_samples */ | |
x += width; /* move right for next character */ | |
str++; /* point to next character in string */ | |
} | |
} | |
void lcd_update(unsigned char top, unsigned char bottom) | |
{ | |
unsigned char x, err, e; | |
unsigned char y; | |
unsigned char yt; | |
unsigned char yb; | |
unsigned char *colptr; | |
unsigned int j; | |
yt = top >> 3; /* setup bytes of range */ | |
yb = bottom >> 3; | |
LCD_CS_LO; | |
for(y = yt; y <= yb; y++) | |
{ | |
LCD_RS_LO; | |
put_serial_to_lcd( LCD_SET_PAGE+y ); /* set page */ | |
put_serial_to_lcd( LCD_SET_COL_HI+0 ); /* set column 0 */ | |
put_serial_to_lcd( LCD_SET_COL_LO+0 ); | |
colptr = &l_display_array[y][0]; | |
LCD_RS_HI; | |
for (x=0; x < X_BYTES; x++) | |
{ | |
put_serial_to_lcd( *colptr++ ); | |
} | |
} | |
LCD_CS_HI; | |
} | |
// | |
// ****************************************************************** | |
// 128x64 Pixel LCD Routines END | |
// ****************************************************************** | |
// | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment