perdona, no recordaba que había tenido que registrarme.
Código: Seleccionar todo
//------------------------------------------------------------
// DIY DATA RECORDER
// for ATmega328 5V 16MHz(Arduino Pro)
// by takuya matsubara
// 3 modos
// ・Modo PLAY: Convierta el archivo binario (CAS/P6) en la tarjeta SD para emitir un pitido y enviar
// ・Modo REMOTE PLAY: Reproduce un pitido cuando REMOTE está ON
// ・Modo REC: convierte el sonido recibido en un archivo binario y lo guarda en la tarjeta SD
// Terminal de casete de PC-6001mkII(4)CMTOUT --> DATIN D2(PD2)
// Terminal de casete de PC-6001mkII(5)CMTIN <-- DATOUT D3(PD3)
// Terminal de casete de PC-6001mkII(6)REMOTE+ --> REMOTE D9(PB1)
// Terminal de casete de PC-6001mkII(7)REMOTE- --- GND
// Terminal de casete de PC-6001mkII(2)GND --- GND
// Terminal de casete de PC-6001mkII(8)GND --- GND
// El REMOTO no tiene polaridad, por lo que +/- inversa está bien
// SDCARD SDI <-- D11(PB3)
// SDCARD SDO --> D12(PB4)
// SDCARD CLK <-- D13(PB5)
// SDCARD CS <-- D4(PD4)
// AQM1602XA SCL <-- D19(PC5)
// AQM1602XA SDA <-- D18(PC4)
// SPEAKER <-- D14(PC0)
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#define DATIN 2 //pin number:data input "activada la función de grabación"
//#define DATIN 0 //pin number:desactivada la función de grabación
#define REMOTE 9 //pin number:remote "terminal"
//#define REMOTE 0 //pin number:remote "desactivar terminal"
#define SPEAKER 14 //pin number:speaker
//#define SPEAKER 0 //pin number:"desactivar altavoz"
#define DATOUT 3 //pin number:data output
#define BTN_UP 5 //pin number:up button
#define BTN_DOWN 6 //pin number:down button
#define BTN_PLAY 7 //pin number:play button
#define BTN_STOP 8 //pin number:stop button
#define CPUHZ 16000000
#define PRS1 8
#define PRS2 64
char seq = 0; //menu seq(Comience con la selección de modo)
char targetfile[14]; //nombre de archivo 8+.1+extensión 3+null1
unsigned int targetsize; //tamaño archivo
unsigned int pion; //contador de pitidos
unsigned int tape_counter; //contador de cinta
unsigned int pulse_work;
unsigned char pulse_waveform;
#define RINGBUFMAX 64
unsigned char ringbuff[RINGBUFMAX]; //buffer de anillo o "ring"
volatile char ring_a = 0; //RINGBUFFER contador A
volatile char ring_b = 0; //RINGBUFFER contador B
unsigned int idx = 0; //read file index
//------------------------------------------------------------
//Interrupción cambio pin DATIN subida/bajada
SIGNAL (PCINT2_vect)
{
char onebit;
int hz;
if(digitalRead(DATIN)==HIGH){ //rising
#if SPEAKER!=0
digitalWrite(SPEAKER, HIGH);
#endif
TCNT1 =0;
}else{
hz = ((CPUHZ/PRS1)/2)/TCNT1; //falling
#if SPEAKER!=0
digitalWrite(SPEAKER, LOW);
#endif
if(hz<600)return;
if(hz>1800){
pulse_waveform <<= 2;
pulse_waveform |= 0b10; //2400Hz(1)
}else{
pulse_waveform = 0b1100; //1200Hz(0)
}
if(pulse_waveform & 0b1000){//1bit recepcion
if(pulse_waveform == 0b1100) onebit=0; else onebit=1;
if(pulse_work == 0xFFFF){ //startbit espera
if(onebit==0)pulse_work = 0x7FFF; //startbit deteccion
}else{
//0b111dddddddd01111
pulse_work >>= 1;
if(onebit)pulse_work |= 0x8000;
if((pulse_work & (1<<4))==0){//12bit envio terminado
ringbuff_push((pulse_work >> 5) & 0xFF);
pulse_work = 0xFFFF;
}
}
pulse_waveform = 0;
}
}
}
//------------------------------------------------------------
// Interrupción del temporizador 4800 veces/seg.
SIGNAL (TIMER1_OVF_vect)
{
char state;
TCNT1 = 0x10000-((CPUHZ/PRS1)/4800);
if(tape_counter == 0xffff)return;
if(pulse_work == 0x0000){ //Transmisión de 1 byte (12 bits) completada
#if REMOTE!=0
if(seq==5){
if( digitalRead(REMOTE) == HIGH){
pion = 0; // contador de pitidos (o tiempo de pitido)
return; //REMOTE terminal OFF
}
}
#endif
if(((pion>=0)&&(pion<340))||((pion>=356)&&(pion<456))){
//Pite durante 3,4 segundos antes de enviar datos 3,4 segundos/(12bit/1200hz)=340
//Pitido durante 1 segundo después de enviar 16 bytes de datos 1/(12/1200)=100
pulse_work = 0b111111111111; //12bit
}else{
if(ringbuff_datacnt()==0){ //si el bufer circular (anillo) está vacio
return;
}
pulse_work = ringbuff_pop(); //los siguientes datos
pulse_work = (pulse_work | 0b11100000000)<< 1; //12bit
tape_counter++; //incrementa contador de cinta
}
if(pion < 0xffff)pion++;
}
if(pulse_waveform==0){ //1bit=1/1200sec progreso
if(pulse_work & 1){
pulse_waveform = 0b1010; // 2400Hz(1)
}else{
pulse_waveform = 0b1100; // 1200Hz(0)
}
pulse_work >>=1;
}
if((pulse_waveform & 1)==0) state=HIGH; else state=LOW;
digitalWrite(DATOUT, state);
#if SPEAKER!=0
digitalWrite(SPEAKER, state);
#endif
pulse_waveform >>= 1;
}
//------------------------------------------------------------
//RING BUFFER:PUSH numero de datos
int ringbuff_datacnt(void)
{
if(ring_a < ring_b){
return((ring_a + RINGBUFMAX) - ring_b);
}else{
return(ring_a - ring_b);
}
}
//------------------------------------------------------------
//RING BUFFER:PUSH "hacer"
void ringbuff_push(unsigned char pushdata)
{
ringbuff[ring_a] = pushdata;
ring_a = (ring_a+1) & (RINGBUFMAX-1);
}
//------------------------------------------------------------
//RING BUFFER:POPする
unsigned char ringbuff_pop(void)
{
unsigned char work;
if (ring_a==ring_b)return(0x00); //no data
work = ringbuff[ring_b];
ring_b = (ring_b+1) & (RINGBUFMAX-1);
return(work);
}
//------------------------------------------------------------
//AQM1602XA:init
void lcd_init(void)
{
#define LCDCNTR 12 //contrast(0-63)
Wire.begin();
delay(150);
lcd_cmdwrite(0x38); //function set(normal instruction)
lcd_cmdwrite(0x39); //function set(extension instruction)
lcd_cmdwrite(0x14); //internal osc freq.
lcd_cmdwrite(0x70+(LCDCNTR & 0xF)); //contrast set
lcd_cmdwrite(0x54+(LCDCNTR >> 4)); //power/icon/contrast control
lcd_cmdwrite(0x6C); //follower control
lcd_cmdwrite(0x38); //function set(normal instruction)
lcd_cmdwrite(0x01); //clear display
lcd_cmdwrite(0x08+4+2+1); //display on/off control(disp=on/cursor=on/blink=on)
}
//------------------------------------------------------------
//AQM1602XA
void lcd_write(unsigned char temp1,unsigned char temp2)
{
#define LCDADDR (0x7C >> 1) //slave address // canviar por el puerto del display I2C (segun fabricante es el puerto 0x3C)
Wire.beginTransmission(LCDADDR);
Wire.write(temp1);
Wire.write(temp2);
Wire.endTransmission();
}
//------------------------------------------------------------
//AQM1602XA:locate
void lcd_locate(char x,char y){
unsigned char cmd=0x80;
if((x>=16)||(y>=2))return;
cmd += x;
cmd += (0x40*y);
lcd_cmdwrite(cmd);
}
//------------------------------------------------------------
//AQM1602XA:write character
void lcd_cmdwrite(unsigned char command)
{
lcd_write(0x00,command);
delay(1);
}
//------------------------------------------------------------
//AQM1602XA
void lcd_datawrite(unsigned char data)
{
lcd_write(0x40,data);
delay(1);
}
//------------------------------------------------------------
//AQM1602XA
void lcd_cls(void)
{
char i;
lcd_locate(0,0);
for(i=0;i<16;i++){
lcd_datawrite(' ');
}
lcd_locate(0,1);
for(i=0;i<16;i++){
lcd_datawrite(' ');
}
}
//------------------------------------------------------------
//AQM1602XA
void lcd_putstr(char x,char y,char *p)
{
lcd_locate(x,y);
while(*p!=0){
lcd_datawrite(*p++);
}
}
//------------------------------------------------------------
//AQM1602XA:pantalla decimal de 5 digitos
void lcd_putnum(char x,char y,unsigned int num)
{
unsigned int keta=10000;
char temp;
lcd_locate(x,y);
while(keta>0){
temp = (num/keta)% 10;
lcd_datawrite('0'+temp);
keta /= 10;
}
}
//------------------------------------------------------------
PROGMEM const unsigned char message[12][16]={
"DATA RECORDER", //0
"SD CARD ERROR", //1
"FILE OPEN ERROR", //2
"WRITE ERROR", //3
"MENU", //4
"PLAY MODE", //5
"REC MODE", //6
"PLAY LIST", //7
"SELECT NUMBER", //8
"PLAYING...", //9
"RECORDING..", //10
"REMOTE PLAY" //11
};
void lcd_putmessage(int x,int y,int number)
{
PGM_P pSrc;
unsigned char ch;
pSrc = message[number];
lcd_locate(x,y);
while(1){
ch = pgm_read_byte(pSrc++);
if(ch==0)break;
lcd_datawrite(ch);
}
}
//------------------------------------------------------------
//"sin usar o sin uso" send 1byte
void pulsesend(int work){
char bitcnt;
char pulse;
int hz;
work <<= 1; //start bit(0)
work |= (0b111 << 9); // stop bit
digitalWrite(DATOUT, 1);
TCNT2 = 0;
for(bitcnt=0; bitcnt<12; bitcnt++){
if(work & (1<<bitcnt)){
hz = 2400; //2400Hz(1)
pulse = 4;
}else{
hz = 1200; //1200Hz(0)
pulse = 2;
}
while(pulse>0){
if(TCNT2 >= (((CPUHZ/PRS2)/2)/hz)){
TCNT2 = 0;
digitalWrite(DATOUT, digitalRead(DATOUT) ^ 1);
pulse--;
}
}
}
digitalWrite(DATOUT, 0);
}
//------------------------------------------------------------
// receive 1byte de "sin uso"
// +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
// pulsecnt| -1 |00 01|02 03|04 05|06 07|08 09|10 11|12 13|14 15|16 17|18 19|20 21|
// +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
// |start| bit0| bit1| bit2| bit3| bit4| bit5| bit6| bit7| stop| stop| stop|
int pulserecv2(void){
char pulsecnt=-1;
unsigned char work=0;
unsigned int hz;
static int edge=0;
int edgenow;
unsigned long timeout;
timeout=0;
hz = 0;
while(pulsecnt < 22){
edgenow = digitalRead(DATIN);
if(edge != edgenow){ //find pin change
edge = edgenow;
if(edge){
TCNT1 = 0; //pulse start
}else{
hz = ((CPUHZ/PRS1)/2)/TCNT1; //pulse end
}
}else{
timeout ++;
if(timeout == 0xfffff)return(-1);
}
if(hz==0)continue;
if(pulsecnt ==-1){ //find start bit(1200Hz)
if(hz > 1800){
pulsecnt = -1; //error
continue;
}
if(hz < 600){
pulsecnt = -1; //error
continue;
}
pulsecnt = 0;
}else if(pulsecnt < 16){ // b0-b7
if(hz > 1800){
work |= (1<<(pulsecnt>>1)); //2400Hz
pulsecnt += 1;
}else{
pulsecnt += 2; //1200Hz
}
}else{ //stop bit
pulsecnt += 1;
}
hz = 0;
}
return(work);
}
//------------------------------------------------------------
//sin usar receive
void recvtest(void) {
int cmd;
char pos=0;
while(1){
if (pos==0) Serial.write('\n');
cmd = pulserecv2(); //get command
if (cmd==-1){ //timeout
pos = 0;
}else{
Serial.print(cmd >> 4, HEX);
Serial.print(cmd & 0x0f, HEX);
Serial.write(' ');
pos = (pos+1)% 16;
}
}
}
//------------------------------------------------------------
// record image file
void recimagefile(void) {
File myFile;
unsigned char tempbyte;
char gomiflag=1; //Indicador de eliminación de polvo (1 = detección de polvo) supongo que se refiere a "ruido"
lcd_cls();
myFile = SD.open(targetfile, FILE_WRITE);
if (myFile==0) { //error
lcd_putmessage(0,0,3);
delay(1000);
return;
}
lcd_putmessage(0,0,10);
PCMSK2 |= (1<<PCINT18); //Interrupción de cambio de pin DATIN
PCICR |= (1<<PCIE2); // Interrupción de cambio de pin enable
pulse_work = 0xFFFF;
pulse_waveform = 0;
tape_counter = 0;
sei();
while(1){
if (digitalRead(BTN_STOP) == LOW)break; //cancelar con el botón STOP
if(ringbuff_datacnt() == 0){
lcd_putnum(0,1,tape_counter); //contador de cinta
continue;
}
tempbyte = (unsigned char)ringbuff_pop();
if(gomiflag){//deteccion de ruido (o polvo/basura)
if(tempbyte != 0xD3)continue; //si no comienza con D3 se considera basura
gomiflag = 0;
}
// Serial.print(tempbyte >> 4, HEX); //debug
// Serial.print(tempbyte & 0x0f, HEX); //debug
// Serial.write(' '); //debug
myFile.write(&tempbyte, 1); //write sd card
tape_counter++;
}
myFile.close();
// cli();
PCICR &= ~(1<<PCIE2); // interrupción de cambio de pin "disable"
targetsize = tape_counter;
lcd_putnum(0,1,tape_counter); //contador de cinta
delay(1000);
}
//------------------------------------------------------------
// SD CARD FILENAME
void getfilename(int index) {
File myFile;
str_copy(targetfile,"");
myFile = SD.open("/");
if(myFile==0){
lcd_putmessage(0,1,2);//ERROR
delay(1000);
return;
}
while(targetfile[0]==0) {
File entry = myFile.openNextFile();
if (! entry) {
// no more files
break;
}
// Serial.println(entry.name()); //debug
if (entry.isDirectory()) { //skip
} else {
if (index==0){
str_copy(targetfile,entry.name()); //copy file name
targetsize = entry.size();
}
index--;
}
entry.close();
}
myFile.close();
}
//------------------------------------------------------------
void str_num2str(char *p,unsigned int num)
{
unsigned int keta=10000;
char temp;
while(keta > 0){
temp = (num/keta)% 10;
*p++ = ('0'+temp);
keta /= 10;
}
}
//------------------------------------------------------------
// string:copy
//pSrc:origen de la copia
//pDst:destino de la copia
void str_copy(char *pDst,char *pSrc)
{
while(1){
*pDst = *pSrc;
if(*pDst == 0)break;
pDst++;
pSrc++;
}
}
//--------------------------------------------------------------
// string:Compare
char str_ncmp(char *p1,char *p2,char cnt)
{
while(*p1 == *p2)
{
cnt--;
if(cnt <= 0)return(0); //equal
p1++;
p2++;
}
return(1); //not equal
}
//------------------------------------------------------------
// string:upper case
unsigned char str_ucase(unsigned char chrcode)
{
if((chrcode >= 'a')&&(chrcode <= 'z'))
chrcode -= 0x20;
return(chrcode);
}
//------------------------------------------------------------
//play Tape image
void playimagefile(void)
{
File myFile;
char i;
lcd_cls();
myFile = SD.open(targetfile);
if (myFile == 0) {
lcd_putmessage(0,0,2);//ERROR
delay(1000);
return;
}
lcd_putmessage(0,0,9); //PLAYING
lcd_putnum(11,1,targetsize);
for(i=0;i<16;i++){
if (myFile.available()) {
ringbuff_push(myFile.read()); //lectura de tarjetas SD
}
}
pion = 0; // contador de pitidos
pulse_work = 0;
pulse_waveform = 0;
tape_counter = 0;
TCNT1 = 0x10000-((CPUHZ/PRS1)/4800);
TIMSK1 |= (1<<TOIE1); // Interrupción de desbordamiento del temporizador 1 "enable"
sei();
while(1){
if(ringbuff_datacnt() < RINGBUFMAX-3){
if(myFile.available()){
ringbuff_push(myFile.read());
}
}else{
lcd_putnum(0,1,tape_counter); //contador de cinta
}
if(ringbuff_datacnt()==0)break; //buffer anillo vacio
if(tape_counter >= targetsize)break; //end of tape
if(digitalRead(BTN_STOP) == LOW)break; //cancelar con el botón STOP
}
myFile.close();
lcd_putnum(0,1,tape_counter); //contador de cinta
tape_counter = 0xffff;
// cli();
TIMSK1 &= ~(1<<TOIE1); // Interrupción de desbordamiento del temporizador 1 desactivado (disable)
delay(1000);
}
//------------------------------------------------------------
void swoffwait(void)
{
while(1){
delay(10);
if (digitalRead(BTN_UP) == HIGH){
if (digitalRead(BTN_DOWN) == HIGH){
if (digitalRead(BTN_PLAY) == HIGH){
if (digitalRead(BTN_STOP) == HIGH){
break;
}
}
}
}
}
}
//------------------------------------------------------------
void setup() {
pinMode(BTN_UP, INPUT_PULLUP);
pinMode(BTN_DOWN, INPUT_PULLUP);
pinMode(BTN_PLAY, INPUT_PULLUP);
pinMode(BTN_STOP, INPUT_PULLUP);
#if DATIN!=0
pinMode(DATIN, INPUT); //Con función de grabación
#endif
pinMode(DATOUT, OUTPUT);
digitalWrite(DATOUT, LOW);
#if SPEAKER!=0
pinMode(SPEAKER, OUTPUT); //con altavoz
digitalWrite(SPEAKER, LOW);
#endif
#if REMOTE!=0
pinMode(REMOTE, INPUT_PULLUP); //con REMOTE
#endif
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
lcd_init();
if (!SD.begin(4)) {
lcd_putmessage(0,0,1); //ERROR
delay(1000);
return;
}
TCCR1A = 0;
TCCR1B = 2;
// 0 No clock source (Timer/Counter stopped).
// 1 clkI/O/1 (No prescaling)
// 2 clkI/O/8 (From prescaler)
// 3 clkI/O/64 (From prescaler)
// 4 clkI/O/256 (From prescaler)
// 5 clkI/O/1024 (From prescaler)
// TCNT1 = 0;
// TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
// TIFR1 |= (1<<TOV1);
TCCR2A = 0;
TCCR2B = 4;
// 1 clk/(No prescaling)
// 2 clk/8 (From prescaler)
// 3 clk/32 (From prescaler)
// 4 clk/64 (From prescaler)
// 5 clk/128 (From prescaler)
// 6 clk/256 (From prescaler)
// 7 clk/1024 (From prescaler)
lcd_putmessage(0,0,0);
delay(1000);
}
//------------------------------------------------------------
void loop() {
switch(seq){
case 0: //mode select 1
case 1: //mode select 2
case 2: //mode select 3
#if DATIN==0
seq=4; //Si no hay función de grabación, se verá forzado al PLAY MODE
break;
#endif
lcd_cls();
lcd_putmessage(0,0,4);
if(seq==0) lcd_putmessage(0,1,5); //PLAY MODE
if(seq==1) lcd_putmessage(0,1,11); //remote PLAY MODE
if(seq==2) lcd_putmessage(0,1,6); //rec MODE
swoffwait();
while(1){
delay(100);
if(digitalRead(BTN_UP) == LOW){
seq++;
if(seq>2)seq=0;
break;
}
if(digitalRead(BTN_DOWN) == LOW){
if(seq==0)seq=2; else seq--;
break;
}
if(digitalRead(BTN_PLAY) == LOW){
if(seq==0)seq=4; //start play mode
if(seq==1)seq=5; //start remote play mode
if(seq==2)seq=6; //start rec mode
break;
}
}
break;
case 4: //play mode
getfilename(idx);
delay(200);
swoffwait();
if(targetfile[0]==0){//Error al obtener el nombre del archivo
idx=0;
break; //retry
}else{
lcd_cls();
lcd_putmessage(0,0,7); //PLAY LIST
lcd_putnum(10,0,idx); //index
lcd_putstr(0,1,targetfile);
}
while(1){
delay(100);
if (digitalRead(BTN_UP) == LOW){ //index+1
idx++;
break;
}
if (digitalRead(BTN_DOWN) == LOW){ //index-1
idx--;
break;
}
if (digitalRead(BTN_PLAY) == LOW){
playimagefile(); //play image file
break;
}
if( digitalRead(BTN_STOP) == LOW){
seq = 0; //back to mode select
break;
}
}
break;
case 5: //remote play mode
#if REMOTE==0
seq=4; //Fuerza PLAY MODE si no hay función REMOTE
break;
#endif
getfilename(idx);
delay(200);
swoffwait();
if(targetfile[0]==0){//error al obtener el nombre de archivo
idx=0;
break; //retry
}else{
lcd_cls();
lcd_putmessage(0,0,7); //PLAY LIST
lcd_putnum(10,0,idx); //index
lcd_putstr(0,1,targetfile);
}
while(1){
delay(100);
if (digitalRead(BTN_UP) == LOW){ //index+1
idx++;
break;
}
if (digitalRead(BTN_DOWN) == LOW){ //index-1
idx--;
break;
}
if (digitalRead(REMOTE) == LOW){ //REMOTE es ON
playimagefile(); //play image file
break;
}
if( digitalRead(BTN_STOP) == LOW){
seq = 1; //back to mode select
break;
}
}
break;
case 6: //rec mode
#if DATIN==0
seq=4; //Si no hay función de grabación, se verá forzado al PLAY MODE
break;
#endif
str_copy(targetfile,"SAV*****.CAS");
str_num2str(targetfile+3,idx);
lcd_cls();
lcd_putmessage(0,0,8); //SELECT NUMBER
lcd_putstr(0,1,targetfile);
delay(100);
swoffwait();
while(1){
if (digitalRead(BTN_UP) == LOW){
idx++;
break;
}
if (digitalRead(BTN_DOWN) == LOW){
idx--;
break;
}
if (digitalRead(BTN_PLAY) == LOW){
recimagefile(); //record image file
break;
}
if( digitalRead(BTN_STOP) == LOW){
idx = 0;
seq = 2; //back to mode select
break;
}
}
break;
}
}
//hasta aqui.