dodanie plikow

This commit is contained in:
mikolaj.s 2022-06-01 08:34:17 +02:00
commit 8d3482ec6f
4 changed files with 549 additions and 0 deletions

21
MCP4xxx.h Normal file
View File

@ -0,0 +1,21 @@
#include "spi.h"
/*
#define nWR 7 //bit aktywacja zapisu do rejestru przetwornika
#define DC 6 //bit nieużywany (Don't Care)
#define GA 5 //bit wyboru wzmocnienia
#define SHDN 4 //bit aktywacji wyjścia
#define GAIN_X1 1
#define GAIN_X2 0
*/
#define CONF1 0b00110000 //gain x1
#define CONF2 0b00010000 //gain x2
void analogWrite(uint8_t data){
SS_LOW;
spiTransfer(CONF1 | (data>>4));
spiTransfer(data<<4);
SS_HIGH;
}

109
effects.h Normal file
View File

@ -0,0 +1,109 @@
#include "MCP4xxx.h"
#define BUFFER_LENGTH 1851
//Przeliczenie przesunięcia w próbkach na czas dla fs = 10 kHz
#define _0MS 0
#define _1MS 10
#define _5MS 50
#define _10MS 100
#define _15MS 150
#define _16MS 160
#define _18MS 180
#define _20MS 200
#define _23MS 230
#define _25MS 250
#define _30MS 300
#define _35MS 350
#define _40MS 400
#define _41MS 410
#define _50MS 500
#define _60MS 600
#define _70MS 700
#define _80MS 800
#define _85MS 850
#define _90MS 900
#define _100MS 1000
#define _105MS 1050
#define _110MS 1100
#define _120MS 1200
#define _135MS 1350
#define _140MS 1400
#define _150MS 1500
#define _160MS 1600
#define _170MS 1700
#define _175MS 1750
#define _180MS 1800
#define _185MS 1850
int16_t reverb(uint8_t buffer [BUFFER_LENGTH], uint16_t bufferIndex, uint8_t roomSize){
int16_t echo1Index = bufferIndex - _35MS;
if(echo1Index < 0) echo1Index += BUFFER_LENGTH;
int16_t echo2Index = bufferIndex - _60MS;
if(echo2Index < 0) echo2Index += BUFFER_LENGTH;
int16_t echo3Index = bufferIndex - _85MS;
if(echo3Index < 0) echo3Index += BUFFER_LENGTH;
int16_t echo4Index = bufferIndex - _110MS;
if(echo4Index < 0) echo4Index += BUFFER_LENGTH;
int16_t echo5Index = bufferIndex - _135MS;
if(echo5Index < 0) echo5Index += BUFFER_LENGTH;
int16_t echo6Index = bufferIndex - _160MS;
if(echo6Index < 0) echo6Index += BUFFER_LENGTH;
int16_t echo7Index = bufferIndex - _185MS;
if(echo6Index < 0) echo7Index += BUFFER_LENGTH;
//wyznaczenie zmiennych tymczasowych (bez składowych stałych)
int16_t echo1Temp = -128 + buffer[echo1Index];
int16_t echo2Temp = -128 + buffer[echo2Index];
int16_t echo3Temp = -128 + buffer[echo3Index];
int16_t echo4Temp = -128 + buffer[echo4Index];
int16_t echo5Temp = -128 + buffer[echo5Index];
int16_t echo6Temp = -128 + buffer[echo6Index];
int16_t echo7Temp = -128 + buffer[echo6Index];
//obliczenie wartości próbki do przekazania DAC i przywrócenie składowej stałej
if(roomSize == 1){
return (1*echo1Temp + 3*echo2Temp + 2*echo3Temp)/30 +128;
} else if(roomSize == 2){
return (1*echo1Temp + 3*echo2Temp + 2*echo3Temp + 2*echo4Temp)/30 +128;
} else if(roomSize == 3){
return (1*echo1Temp + 3*echo2Temp + 3*echo3Temp + 2*echo4Temp + 1*echo5Temp)/30 +128;
} else if(roomSize == 4){
return (2*echo1Temp + 4*echo2Temp + 3*echo3Temp + 2*echo4Temp + 1*echo5Temp)/30 +128;
} else if(roomSize == 5){
return (2*echo1Temp + 4*echo2Temp + 3*echo3Temp + 3*echo4Temp + 2*echo5Temp + 1*echo6Temp)/30 +128;
} else if(roomSize == 6){
return (2*echo1Temp + 5*echo2Temp + 4*echo3Temp + 3*echo4Temp + 3*echo5Temp + 1*echo6Temp)/30 +128;
} else if(roomSize == 7){
return (2*echo1Temp + 5*echo2Temp + 5*echo3Temp + 4*echo4Temp + 3*echo5Temp + 2*echo6Temp + 1*echo7Temp)/30 +128;
} else if(roomSize == 8){
return (3*echo1Temp + 6*echo2Temp + 5*echo3Temp + 4*echo4Temp + 4*echo5Temp + 2*echo6Temp + 2*echo7Temp)/30 +128;
} else {
return (3*echo1Temp + 7*echo2Temp + 6*echo3Temp + 5*echo4Temp + 4*echo5Temp + 3*echo6Temp + 2*echo7Temp)/30 +128;
}
}
int16_t distortion(uint8_t cutoff, uint8_t adcVal){
//jeżeli wartość przekracza ustawiony próg, to jest ona do nigo zmniejszana (ucinanie górnej części przebiegu)
if (adcVal > cutoff){
return cutoff;
} else {
return adcVal;
}
}
int16_t chorusFlanger(uint8_t buffer [BUFFER_LENGTH], uint16_t bufferIndex, uint16_t delayVal, uint8_t attenuationLevel){
int16_t echoIndex = bufferIndex - delayVal;
if(echoIndex < 0) echoIndex += BUFFER_LENGTH;
int16_t echoTemp = -128 + buffer[echoIndex];
return echoTemp/(1+attenuationLevel) + 128;
}
int16_t tremolo(uint8_t adcVal, uint8_t amplitudeLevel){
return ((adcVal - 128)*(amplitudeLevel+70))/271 + 128;
}

356
main.cpp Normal file
View File

@ -0,0 +1,356 @@
#include "effects.h"
#define TESTING 0
#define REVERB 1
#define CHORUS 2
#define FLANGER 3
#define ECHO 4
#define DISTORTION 5
#define TREMOLO 6
volatile uint8_t sampleReady = 0; //zmienna kotrolująca możliwość podania nowej wartości do DAC
volatile uint8_t adcVal = 0;
volatile uint8_t effect = REVERB; //wybrany efekt
volatile uint32_t counter = 0;
const uint8_t sine [101] = {0,0,0,0,1,1,2,2,3,4,5,6,7,8,10,11,12,14,16,17,19,21,23,25,27,29,32,34,36,39,41,44,46,49,
52,55,57,60,63,66,69,72,75,78,81,84,87,91,94,97,100,103,106,109,113,116,119,122,125,128,131,134,137,140,143,145,148,
151,154,156,159,161,164,166,168,171,173,175,177,179,181,183,184,186,188,189,190,192,193,194,195,196,197,198,198,199,
199,200,200,200,200};
volatile uint8_t effectSetting = 5;
const uint8_t effectSettingVector [9] = {0, 5, 15, 30, 55, 90, 130, 180, 255};
volatile uint8_t encoderDir = 0; //0 - w lewo, 1 - w prawo
volatile uint8_t encoderServed = 1;
//timer0 służy tylko do aktywacji odczytu przez ADC (i do debouncingu)
ISR(TIMER0_COMPA_vect){
counter++;
}
ISR(TIMER1_OVF_vect){
OCR1A = effectSettingVector[effectSetting -1];//20 * effectSetting - 15; //od 25 do 225
}
ISR(INT0_vect){
if(encoderServed){
if(!(PIND & 0x08)){
encoderDir = 0;
} else if(PIND & 0x08){
encoderDir = 1;
}
encoderServed = 0;
}
}
ISR(ADC_vect){
adcVal = ADCH;
sampleReady = 1; //dopiero po odczytaniu nowej wartości ADC uruchomiona jest możliwość podania nowej wartości do DAC
}
int main(void){
//wyprowadzenie do sterowania wejsciem sygnalu nieprzetworzonego
DDRC |= 1<<DDC5;
//enkoder do zmiany parametru efektu
DDRD &= ~(1<<DDD2 | 1<<DDD3);
//przycisk do zmiany efektu
DDRD &= (1<<DDD4);
PORTD |= (1<<DDD4);
//przycisk do aktywacji efektu
DDRD &= (1<<DDD1);
PORTD |= (1<<DDD1);
//diody sygnalizujące wybrany efekt
DDRD |= (1<<DDD5) | (1<<DDD6) | (1<<DDD7);
//dioda sygnalizująca aktywację efektu
DDRB |= (1<<DDB0);
//dioda, której intensywność świecenia wskazuje na parametr efektu
DDRB |= (1<<DDB1);
//TIMER0
//Prescaler = 64; pojemność bufora 1850;
//250 000 / 41,67 kHz = 6 ; 1850 / 41,67 kHz = 44,4 ms
//250 000 / 20,83 kHz = 12; 1850 / 20,83 kHz = 88,8 ms
//250 000 / 10 kHz = 25; 1850 / 10 kHz = 185 ms
//250 000 / 8,065 kHz = 31; 1850 / 8,065 kHz = 229,4 ms
//250 000 / 5 kHz = 50; 1850 / 5 kHz = 370 ms
TCCR0A = (1<<WGM01); //CTC
TCCR0B = (1<<CS01) | (1<<CS00); //prescaler = 64
OCR0A = 25;
TIMSK0 = (1<<OCIE0A);
//TIMER1
//Do sterowania diodą (PWM)
TCCR1A = (1<<COM1A1) | (1<<WGM10); //OC1A, Clear on compare match; Fast PWM 8-bit
TCCR1B = (1<<WGM12) | (1<<CS12) | (1<<CS10); //Fast PWM 8-bit; fosc/1024
OCR1A = 128;
TIMSK1 = (1<<TOIE1); //Przerwanie przy przepełnieniu do aktualizacji wartości wypełnienia.
//ADC
ADMUX = (1<<REFS0) | (1<<ADLAR); //Vref = AVcc, przesuniecie do lewej, ADC0
ADCSRA = (1<<ADEN) | (1<<ADSC) | (1<<ADATE) | (1<<ADIE) | (1<<ADPS2) /*(1<<ADPS1) | (1<<ADPS0)*/; //częstotliwość zegara ADC = 1 MHz
ADCSRB = (1<<ADTS1) | (1<<ADTS0); //triggerowanie ADC przerwaniem od przepełnienia timera 0
DIDR0 = (1<<ADC0D);
//INT0
EICRA = 1<<ISC01; //wyzwalanie zboczem opadającym
EIMSK = 1<<INT0; //aktywacja
sei();
spiInit();
int16_t valueToSend = 128; //wartość do przekazania do DAC; int16_t (16 bitowy, ze znakiem) w celu implementacji nasycenia
//bufor na dane i jego inicjalizacja wartościami 128
uint8_t buffer [BUFFER_LENGTH] = {0};
for(uint16_t i = 0; i < BUFFER_LENGTH; i++){
buffer[i] = 128;
}
uint16_t bufferIndex = 0; //indeks najnowszej próbki
//Przyciski
uint32_t counterButton = 0;
uint8_t buttonPressed = 0;
uint32_t counterButton2 = 0;
uint8_t buttonPressed2 = 0;
uint8_t effectOn = 0;
//Enkoder
uint32_t counterEncoder = 0;
//Chorus
uint8_t sineIndex = 0; //indeks wartości z wektora z sinusem
uint8_t chorusCounter = 0; //licznik służacy do spowolnienia zmian w opóźnieniu
//użyty zamiast licznika od timera, żeby operować na wartości 8-bitowej zamiast 32-bitowej
uint8_t chorusDirChange = 0; //0 - inkrementacja, 1 - dekrementacja
//Flanger
uint8_t flangerDelayTime = _0MS;
uint8_t flangerCounter = 0;
uint8_t flangerDirChange = 0;
//Echo
uint16_t echoIndex = 0;
//Tremolo
uint8_t tremoloCounter = 0;
uint8_t tremoloDirChange = 0;
while(1){
//Obsługa przycisku od przełączania efektu
if(counter - counterButton >= _50MS){
if(!(PIND & 0x10) && !buttonPressed){
//Jeżeli przed zmianą na echo znajduje się coś w buforze, to istnieje ryzyko wystąpienia zapętlenia próbki,
//stąd zerowanie buforu.
if(effect == ECHO - 1) {
for(uint16_t i = 0; i < BUFFER_LENGTH; i++){
buffer[i] = 128;
}
}
effect++;
if(effect > TREMOLO) effect = REVERB;
buttonPressed = 1;
} else if(PIND & 0x10 && buttonPressed) buttonPressed = 0;
counterButton = counter;
}
//obsługa przycisku od aktywacji efektu i diody sygnalizującej aktywację efektu
if(counter - counterButton2 >= _50MS){
if(!(PIND & 0x02) && !buttonPressed2){
if(effectOn){
effectOn = 0;
PORTB &= ~(1<<PORTB0);
} else {
effectOn = 1;
PORTB |= (1<<PORTB0);
}
buttonPressed2 = 1;
} else if(PIND & 0x02 && buttonPressed2) buttonPressed2 = 0;
counterButton2 = counter;
}
if(counter - counterEncoder >= _20MS && !encoderServed){
if(encoderDir == 1){
if(effectSetting < 9) effectSetting++;
} else {
if(effectSetting > 1) effectSetting--;
}
counterEncoder = counter;
encoderServed = 1;
}
if(sampleReady){
if(effect < ECHO) buffer[bufferIndex] = adcVal; //zapisanie aktualnej próbki na najnowszą pozycję w buforze
//pod warunkiem że efekt to REVERB, CHORUS lub FLANGER
if(effect == TESTING){
valueToSend = adcVal;
PORTD &= ~((1<<PORTD7) | (1<<PORTD6) | (1<<PORTD5));
//PORTC &= ~(1<<PORTC5);
PORTC |= 1<<PORTC5;
} else if(effect == REVERB){
PORTC |= 1<<PORTC5;
valueToSend = reverb(buffer, bufferIndex, effectSetting);
PORTD |= (1<<PORTD5);
PORTD &= ~((1<<PORTD7) | (1<<PORTD6));
} else if(effect == CHORUS){
PORTC |= 1<<PORTC5;
valueToSend = chorusFlanger(buffer, bufferIndex, sine[sineIndex] + _40MS, 10 - effectSetting);
chorusCounter++;
if(chorusCounter >= 200){
if(chorusDirChange == 0){
sineIndex++;
if(sineIndex >= 100) chorusDirChange = 1;
} else {
sineIndex--;
if(sineIndex == 0) chorusDirChange = 0;
}
chorusCounter = 0;
}
PORTD |= (1<<PORTD6);
PORTD &= ~((1<<PORTD7) | (1<<PORTD5));
} else if(effect == FLANGER){
PORTC |= 1<<PORTC5;
valueToSend = chorusFlanger(buffer, bufferIndex, flangerDelayTime, 10 - effectSetting);
flangerCounter++;
if(flangerCounter >= 100){
if(flangerDirChange == 0){
flangerDelayTime++;
if(flangerDelayTime >= _10MS) flangerDirChange = 1;
} else {
flangerDelayTime--;
if(flangerDelayTime == _0MS) flangerDirChange = 0;
}
flangerCounter = 0;
}
PORTD |= (1<<PORTD5) | (1<<PORTD6);
PORTD &= ~(1<<PORTD7);
} else if(effect == ECHO){
PORTC |= 1<<PORTC5;
echoIndex = bufferIndex + (_16MS * effectSetting) + _41MS; //obliczenie miejsca zapisu próbki "na później"
if(echoIndex > BUFFER_LENGTH) echoIndex -= BUFFER_LENGTH;
buffer[echoIndex] = (adcVal - 128)/2 + 2*(buffer[bufferIndex] - 128)/3 + 128; // wpisanie z opóźnieniem i tłumieniem aktualnej wartości
valueToSend = buffer[bufferIndex];
PORTD |= (1<<PORTD7);
PORTD &= ~((1<<PORTD6) | (1<<PORTD5));
} else if (effect == DISTORTION){
if(effectOn){
PORTC &= ~(1<<PORTC5);
} else {
PORTC |= (1<<PORTC5);
}
valueToSend = distortion(164 - effectSetting*4, adcVal);
PORTD |= (1<<PORTD7) | (1<<PORTD5);
PORTD &= ~((1<<PORTD6));
} else if(effect == TREMOLO){
if(effectOn){
PORTC &= ~(1<<PORTC5);
} else {
PORTC |= (1<<PORTC5);
}
valueToSend = tremolo(adcVal, sine[sineIndex]);
tremoloCounter++;
if(tremoloCounter >= 1 + effectSetting){
if(tremoloDirChange == 0){
sineIndex++;
if(sineIndex >= 100) tremoloDirChange = 1;
} else {
sineIndex--;
if(sineIndex == 0) tremoloDirChange = 0;
}
tremoloCounter = 0;
}
PORTD |= (1<<PORTD7) | (1<<PORTD6);
PORTD &= ~((1<<PORTD5));
} else {
effect = REVERB;
}
//implementacja przesterowania programowego
if(valueToSend > 255){
valueToSend = 255;
} else if(valueToSend < 0){
valueToSend = 0;
}
if (effectOn) {
analogWrite(valueToSend);
} else {
analogWrite(128);
}
sampleReady = 0;
//zmiana indeksu dla kolejnej próbki
bufferIndex++;
if(bufferIndex >= BUFFER_LENGTH) bufferIndex = 0;
}
}
}
//Wykorzystana pamięć
//sampleReady = 1 B
//adcVal = 1 B
//effect = 1 B
//counter = 4 B
//sine = 101 B
//effectSetting = 1 B
//effectSettingVector = 9 B
//encoderLeft = 1 B
//encoderRight = 1 B
//encoderServed = 1 B
//valueToSend = 2 B
//bufor = 1851 B
//bufferIndex = 2 B
//counterButton1 = 4 B
//buttonPressed1 = 1 B
//counterButton2 = 4 B
//buttonPressed2 = 1 B
//effectOn = 1 B
//counterEncoder = 4 B
//chorusSineIndex = 1 B
//chorusCounter = 1 B
//chorusDirChange = 1 B
//flangerDelayTime = 1 B
//flangerCounter = 1 B
//flangerDirChange = 1 B
//echoIndex = 2 B
//14 zmiennych w reverbie
//echo#Index... = 14 B (7x2 B)
//echo#Temp... = 14 B (7x2 B)
//3 zmienne w chorusie/flangerze
//echoIndex = 2 B
//echoTemp = 2 B
//W najgorszym przypadku łącznie: 2031 B
//Pozostaje: (2048 - 2031) B = 17 B

63
spi.h Normal file
View File

@ -0,0 +1,63 @@
#include <avr/io.h>
#include <avr/interrupt.h>
#define SS_LOW PORTB &= ~(1<<PORTB2)
#define SS_HIGH PORTB |= (1<<PORTB2)
//Jaki stan musi być na SS żeby DAC odbierał dane??
//musi być to stan niski (zarówno wg kursu jak i danycha katalogowych)
/*"CS is the Chip Select input pin, which requires an
active low to enable serial clock and data functions."*/
/*CPOL (polaryzacja) - mówi o tym jaki ma być stan zegara w bezczynności
W przypadku użytego DAC, w trybie 0,0 (Mode 0, 0), powinien być to stan niski, a więc CPOL = 0 */
/*CPHA (faza) - określa kiedy są odczytywane, a kiedy wystawiane bity na linii danych DAC.
Ponieważ bity wystawiane na zboczu opadającym, to CPHA = CPOL = 0 (czyli DAC pracuje w trybie 0, 0)*/
/*W ogólności MCP4xxx wspierają tryb 0,0 i 1,1*/
/*Wg kursu inne piny odpowiadają za SPI niż w rzeczywistości (PB4, 5, 6, 7 zamiast PB2, 3, 4, 5)*/
//inicjalizacja
void spiInit(void) {
// /SS, MOSI, SCK jako wyjścia
DDRB |= (1<<PORTB2) | (1<<PORTB3) | (1<<PORTB5);
// /SS w stanie wysokim
SS_HIGH;
//SPI włączone, tryb master, podział przez 2 (najwyższa możliwa prędkość)
SPCR = (1<<SPE) | (1<<MSTR);
SPSR |= (1<<SPI2X);
}
//wysłanie jednego bajtu
/*uint8_t*/ void spiTransfer(uint8_t data) {
//rozpocznij transmisję
SPDR = data;
//poczekaj na koniec
while(!(SPSR & (1<<SPIF))); //wstrzymanie programu trwa do chwili zakończenia transmisji (flaga SPIF = 0 w rejestrze statusowym)
//możliwe jest też wywołanie przerwania w takim przypadku: można by zmieniać w nim własną flagę
//wysyłanie i tak następuje co 1600 cykli (f = 10 000 Hz), więc oczekiwanie raczej nie jest do niczego potrzebne
//BŁĄD!! Żeby wysłać jedna wartość do DAC wysyłane są dwa bajty jeden za drugim
//Czas jaki mija między zakończeniem wysyłki pierwszego i rozpoczęcia transmisji
//drugiego pakietu wynosi ok. 600 ns - ok. 10 cykli zegarowych (powinno wyjść dokładnie 10 * 62,5 ns = 625 ns).
//Można stworzyć dodatkową funkcję - wysyłającą drugi bajt bez czekania
//NIE DZIAŁA! Z jakiegoś powodu, wykorzystując taką funkcję wysyła się tylko pierwszy bajt
//Dzieje się tak, ponieważ natychmiast po przekazaniu danych do wysyłki następuje rozłaczenie (bit SS = 1)
//Aktywacja DAC SPI na stałe (SS = 0) nic nie daje. Nadal bez while'a, wysyłka drugiego bajtu nie następuje
//Obsługa przez przerwania prawdopodobnie byłaby nieopłacalna - więcej procesor musiałby się napracować niż marnuje czasu w while'u
//Zmiany stanu wyjscia SS są konieczne do poprawnej pracy przetwornika CA, stąd:
//- usunięcie while'a po pierwszym bajcie jest niemożliwe, bo natychmiast nastąpiłoby wprowadzanie
//kolejnych danych do rejestru SPDR,
//- usunięcie while'a po drugim bajcie jest niemożliwe, bo zanim nastąpiłaby wysyłka, doszłoby do
//podniesienia linii SS
//Cała wysyłka jednego bajtu trwa (1940 ns / 62,5 ns) ok. 31 cykli (czas kiedy SS = 0)
//Zanim zostanie wysłany pierwszy bit, mijają (1490 ns / 62,5 ns) ok. 24 cykle
//While kończy się zanim na MOSI kończy się wysyłka
//Przy wysyłaniu dwóch bajtów całość trwa dłużej (4640 ns / 62,5 ns): ok. 74 cykle
//W tym przypadku while kończy się po przekazaniu drugiego bajtu (470 ns / 62,5 ns): ok. 7,5 cykli
//zwróć odebraną wartość
//return SPDR;
}