textadv/src/dac.c

208 lines
4.8 KiB
C

/* Copyright © 2020 tyrolyean
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "avr.h"
#include "dac.h"
#include "structures.h"
#include <stdbool.h>
uint8_t dac_mode = DAC_MODE_SINE;
int16_t dac_frequency_deviation = 0;
void write_to_dac(uint8_t addr, uint8_t data){
set_addr(addr);
DATA_DDR_REG = 0xFF;
DATA_REG = data;
CTRL_REG &= ~(1<<WR_SHIFT) & ~(1<<CS_DAC_SHIFT);
_delay_us(BUS_HOLD_US); /*Wait for the data and signal lanes to become stable*/
CTRL_REG |= 1<<CS_DAC_SHIFT;
CTRL_REG |= 1<<WR_SHIFT;
return;
}
uint8_t read_from_dac(uint8_t addr){
uint8_t data = 0x00;
set_addr(addr);
DATA_DDR_REG = 0x00;
/* Sometimes the avr "forgets" to alter the port register for one cycle
* which usually isn't a problem but has lead to instability. Worthy
* one clock cycle */
DATA_REG = 0x00;
CTRL_REG &= ~(1<<RD_SHIFT) & ~(1<<CS_DAC_SHIFT);
_delay_us(BUS_HOLD_US); /* Wait for the data and signal lanes to become
* stable*/
data = PINF;
CTRL_REG |= 1<<CS_DAC_SHIFT | 1<<RD_SHIFT;
return data;
}
void feed_dac(){
/* Internal counter for positioning inside the currently playing
* waveform */
static uint8_t threash = 0x00;
/* Used to generate the desired frequency offset if the waveform should
* be made "longer" --> the frequency made lower from baseline
*/
static int16_t freq_delay_cnt = 0x00;
switch(dac_mode){
default:
case DAC_MODE_SILENT:
for(uint8_t i = 0; i < 0xFF; i++){
write_to_dac(i%2, 0);
}
break;
case DAC_MODE_SINE:
/* Generates a sine from a predetermined sine table in program
* space */
for(uint8_t i = 0; i < (0xFF/2); i++){
write_to_dac(1,
pgm_read_byte(&sine_table[threash]));
write_to_dac(0,
pgm_read_byte(&sine_table[threash]));
if(dac_frequency_deviation >=0){
freq_delay_cnt++;
if(freq_delay_cnt >=
dac_frequency_deviation){
freq_delay_cnt = 0;
threash++;
}
}else{
threash -= dac_frequency_deviation;
}
}
break;
case DAC_MODE_SQUARE:
/* Generates a square wave tone */
for(uint8_t i = 0; i < (0xFF/2); i++){
if(threash > (0xFF/2)){
write_to_dac(0, 0xFF);
write_to_dac(1, 0xFF);
}else{
write_to_dac(0, 0);
write_to_dac(1, 0);
}
if(dac_frequency_deviation >=0){
freq_delay_cnt++;
if(freq_delay_cnt >=
dac_frequency_deviation){
freq_delay_cnt = 0;
threash++;
}
}else{
threash -= dac_frequency_deviation;
}
}
break;
case DAC_MODE_SAW:
/* Generates a saw wave tone */
for(uint8_t i = 0; i < (0xFF/2); i++){
write_to_dac(0, threash);
write_to_dac(1, threash);
if(dac_frequency_deviation >=0){
freq_delay_cnt++;
if(freq_delay_cnt >=
dac_frequency_deviation){
freq_delay_cnt = 0;
threash++;
}
}else{
threash -= dac_frequency_deviation;
}
}
break;
case DAC_MODE_NOISE:
/* Generates white noise from a predetermined LUT
*/
for(uint8_t i = 0; i < (0xFF/2); i++){
static uint16_t noise_cnt = 0;
write_to_dac(1,
pgm_read_byte(&noise_table[noise_cnt]));
write_to_dac(0,
pgm_read_byte(&noise_table[noise_cnt]));
noise_cnt++; /* Doesn't have frequency diversion
*/
if(noise_cnt >= 1024){
noise_cnt = 0;
}
}
break;
case DAC_MODE_TRIANGLE:
/* Generates a triangle wave tone */
for(uint8_t i = 0; i < (0xFF/2); i++){
static int8_t direction = 1;
if((threash == 0xFF) | !threash){
direction = -direction;
}
write_to_dac(0, threash);
write_to_dac(1, threash);
if(dac_frequency_deviation >=0){
freq_delay_cnt++;
if(freq_delay_cnt >=
dac_frequency_deviation){
freq_delay_cnt = 0;
threash += direction;
}
}else{
if((dac_frequency_deviation *
direction) >
(0xFF - threash)){
threash = 0xFF;
continue;
}
threash = (dac_frequency_deviation *
direction);
}
}
break;
}
return;
}
void routine_dac(){
uint8_t received = read_from_dac(0x00);
if(!(received & (0x01<<0))){
feed_dac();
}
return;
}