dodanie plikow
This commit is contained in:
commit
8d3482ec6f
21
MCP4xxx.h
Normal file
21
MCP4xxx.h
Normal 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
109
effects.h
Normal 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
356
main.cpp
Normal 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
63
spi.h
Normal 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 są 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user