[Aporte] Generador de Señales (DDS)

El paso debe ser del mismo tamaño que el acumulador, es decir en este caso 24bits.

A ver, sin saber mucho del PIC (posible errores en el código), traducionedo mí código bajás a 10 ciclos:

Código:
ACUMULADOR_INF	EQU	10h 
ACUMULADOR_SUP 	EQU	11h
PASO_BYTE_1 	EQU	12h
PASO_BYTE_2 	EQU	13h
PASO_BYTE_3 	EQU	14h
DIR_BUFF_ALTA 	EQU	15h   
PORT_SALIDA     EQU   	PORTB

funcion_assembler:
  CLRF 		FSR0L			;Limpio la parte baja del puntero que apuntará al buffer de datos
  MOVF		DIR_BUFF_ALTA,W		;Copio a WREG la parte alta del puntero donde apuntará al buffer de datos		
  MOVWF		FSR0H			;Paso de WREG a FSR0H la parte alta del puntero donde apuntará al buffer de datos		
  CLRF 		ACUMULADOR_INF
  CLRF 		ACUMULADOR_SUP
  
loop_principal:
  MOVF 		PASO_BYTE_1,W		;Copio el Paso byte 1 en WREG
  ADDWF 	ACUMULADOR_INF,F        ;Sumo el Paso byte 1 en el Acumulador INF
  MOVF 		PASO_BYTE_2,W		;Copio el Paso byte 2 en WREG - El carry no se toca!	
  ADDWFC	ACUMULADOR_SUP,F        ;Sumo el Paso byte 2 en el Acumulador SUP teniendo en cuenta el carry	 		
  MOVF		PASO_BYTE_3,W		;Copio el Paso byte 3 en WREG - El carry no se toca!	
  ADDWFC	FSR0L,F        		;Sumo la parte baja del puntero con el Paso byte 3 del buffer y lo almaceno en FSR0L
  MOVF		INDF0,W			;Copia el contenido del puntero del Buffer en WREG
  MOVF		PORT_SALIDA,F		;Manda al puerto el contenido del puntero del Buffer
  GOTO		loop_principal		;Empieza el ciclo de nuevo
  
  ;8 ciclos + 2 ciclos del GOTO = 10 ciclos.

De alguna manera tenés que pasarle los datos de:

- PASO_BYTE_1,2 y 3
- DIR_BUFF_ALTA, será la parte alta del puntero donde se encuentren los datos de la señal. Es importante que la parte baje de dicho puntero sea 0, de esa forma podés direccionar 256 elementos.

Las direcciones que impuse a la variables fue lo que se me ocurrió, habría que ver si son válidas.

Creo que se asimila bastante a tú código.

Ahora, el problema que tienen los PICs (por lo poco que leí), es que solo tienen un registro de trabajo, el resto es trabajar todo sobre la RAM, a diferencia de los AVR que tienen 32 registros, es un proceso muy tedioso y lento :(.

De esos de 10 ciclos x 4 (según decís) te dan 40clk's (la verdad que bastante), dandote un fsampling de 1,2MHz un poco menos de los 1,7MHz que obtuve con el AVR (replanteate a futuro el hecho de cambiarte de familia de uC, más allá de PIC o AVR, sino algo distinto a PIC).

Y todavía en ese código no tuve en cuenta un flag para preguntar si saltó una interrupción externa para salir de la rutina.

Sobre las soluciones en hard, estoy muy óxidado con lógica convencional, el que te puede dar una mano por ese lado es Mr. Carlos. Tal vez otra opción siguiendo con lógica convencional es usar FPGA.
 
A ver, sin saber mucho del PIC (posible errores en el código), traducionedo mí código bajás a 10 ciclos:

Muchas gracias! voy a pasarlo y corregirlo. Lo que me queda duda si funcionara es la parte donde accediste al vector de datos pero voy a hacer una prueba con ese codigo antes que nada.

Ahora, el problema que tienen los PICs (por lo poco que leí), es que solo tienen un registro de trabajo, el resto es trabajar todo sobre la RAM, a diferencia de los AVR que tienen 32 registros, es un proceso muy tedioso y lento :(.

Sip! decepcionante en ese sentido, solo tienen el registro WREG. No sabia que AVR tenia 32 registros de trabajo! wow.

Y todavía en ese código no tuve en cuenta un flag para preguntar si saltó una interrupción externa para salir de la rutina.

Bueno, en el codigo que te puse antes tampoco esta aplicado esto. Mi idea era jugar con lo siguiente: Declaro una instruccion NOP fuera del bucle (pero dentro de la funcion de generar bucle) y en la interrupcion externa, luego de ajustar las variables, limpiar banderas, etc, lo que haria seria un BRA (que es un salto incondicional) a la posicion de memoria de ese NOP. Si tengo suerte y el stack no me jode (porque no salgo de la interrupcion "bien" aunque su llamada creo que no es un CALL) entonces con eso estoy cortando el bucle de una forma fea y abrupta, pero efectiva. Hay que ver si anda.

Sobre las soluciones en hard, estoy muy óxidado con lógica convencional, el que te puede dar una mano por ese lado es Mr. Carlos. Tal vez otra opción siguiendo con lógica convencional es usar FPGA.

Bueno, mi primer diseño que puse la imagen al principio se basaba en un PLD GAL22v10 (del año del ñaupa) que seria el abuelito de los fpga. Con esto anda bien pero tengo solo 64 divisiones del reloj principal, por eso me incline a tu solucion. De hecho, con ese PLD chiquito pude hasta "almacenar" las muestras dentro de sus compuertas logicas en forma de maquinas de estado. Llegue a hacer 3 formas de onda con 4-8 samples cada una si mal no recuerdo con solo un chip (que responde en 5ns!!!). Con esto podria generar señales de algunos MHZ. Lo abandone porque se veian horribles las ondas con tan pocos samples, pero ahora pensando, quiza si utilizo tu filtro puede mejorar mucho :cool: aunque las pocas divisiones (64) lo hacen poco atractivo.

Bueno, igualmente te comento que hice otra prueba hoy:
- El pic graba, al inicializar, en una SRam externa (de lectora de CD) los 256 samples que tenga en su ROM. Estos samples pueden ser los 256 para 1 ciclo de senoidal, o 1 ciclo cada 4 de esos 256 samples (255,127,0,127...) en maxima frecuencia. A partir de ahi pone su puerto de datos en HI-Z y la SRAM OE = true
- El pic a 48mhz lo que hace es acumular el prescaler de 24 bits, sacando en cada vuelta del bucle el valor del MSB al puerto B. El bucle es mucho mas chico porque no hace lookup del dato. El mismo toma 666.66ns a 24 bits y 500ns a 16 bits.
- Como la SRAM responde en 25ns, de esta forma logre generar una senoidal de 500khz con prescaler a 16bits (8 en realidad, el byte alto es direccion)
- Para frecuencias mas bajas, cambio todos esos parametros y voy teniendo mejor resolucion.

O sea, el PIC aca solo hace de prescaler e interfaz de usuario con LCD, con la ventaja que la comunicacion PC puede ser USB

La unica duda de esto es que con 4 samples se ve muy fea, pero entendi que vos haces lo mismo y que el filtro de salida es el que te la deja "bonita"
 
Bueno, igualmente te comento que hice otra prueba hoy:
- El pic graba, al inicializar, en una SRam externa (de lectora de CD) los 256 samples que tenga en su ROM. Estos samples pueden ser los 256 para 1 ciclo de senoidal, o 1 ciclo cada 4 de esos 256 samples (255,127,0,127...) en maxima frecuencia. A partir de ahi pone su puerto de datos en HI-Z y la SRAM OE = true
- El pic a 48mhz lo que hace es acumular el prescaler de 24 bits, sacando en cada vuelta del bucle el valor del MSB al puerto B. El bucle es mucho mas chico porque no hace lookup del dato. El mismo toma 666.66ns a 24 bits y 500ns a 16 bits.
- Como la SRAM responde en 25ns, de esta forma logre generar una senoidal de 500khz con prescaler a 16bits (8 en realidad, el byte alto es direccion)
- Para frecuencias mas bajas, cambio todos esos parametros y voy teniendo mejor resolucion.

O sea, el PIC aca solo hace de prescaler e interfaz de usuario con LCD, con la ventaja que la comunicacion PC puede ser USB

O sea vos con el PIC modificás el acumulador y sacás por el puerto no el dato de las muestras, sino la dirección que vas a leer con la SRAM, ¿es así?

No veo que ventaja sacás con manejar la SRAM externa en comparación a una interna, ya que el acumulador lo sigue manejando el PIC.

La unica duda de esto es que con 4 samples se ve muy fea, pero entendi que vos haces lo mismo y que el filtro de salida es el que te la deja "bonita"

Exactamente, pero dependés mucho del fsampling, mientras más cerca estes del fsamplig, más abrupto debe ser el filtro.
 
O sea vos con el PIC modificás el acumulador y sacás por el puerto no el dato de las muestras, sino la dirección que vas a leer con la SRAM, ¿es así?

No veo que ventaja sacás con manejar la SRAM externa en comparación a una interna, ya que el acumulador lo sigue manejando el PIC.

Exactamente pero para que veas donde aumenta el rendimiento: En los pines de datos de la SRAM externa es donde esta el DAC conectado, no en el micro, por lo tanto, el PIC no tiene que hacer todo el lookup del dato, encargandose solamente del prescaler.

De esta forma, el bucle en el pic es de 666.66ns a 24bits (porque es solo incrementar el acumulador y no mas) A partir de que la direccion esta en el puerto del PIC, en la salida de datos de la SRAM esta el dato. (que quiero dejar claro, dicho dato NO lo lee el pic para nada)

En tu programa para AVR, de esta forma te ahorras solamente 2 clocks en
Código:
ld		DATA_REG,X

Pero en PIC te ahorras muchisimo
 
Exactamente pero para que veas donde aumenta el rendimiento: En los pines de datos de la SRAM externa es donde esta el DAC conectado, no en el micro, por lo tanto, el PIC no tiene que hacer todo el lookup del dato, encargandose solamente del prescaler.

Hasta ahí entendí, pero de todas formas el loop en el PIC lo vas a necesitar hacer para ir modificando los valores del acumulador.

De esta forma, el bucle en el pic es de 666.66ns a 24bits (porque es solo incrementar el acumulador y no mas) A partir de que la direccion esta en el puerto del PIC, en la salida de datos de la SRAM esta el dato. (que quiero dejar claro, dicho dato NO lo lee el pic para nada)

Eso es lo que podés hacer en la rutina del PIC, para frecuencias muy altas dejar de lado la resolución y usar un acumulador de 8 bits.

En tu programa para AVR, de esta forma te ahorras solamente 2 clocks en
Código:
ld		DATA_REG,X

Pero en PIC te ahorras muchisimo

¿Cómo lo hacés en el PIC? ¿no funcionó como mencioné?

Código:
...
ADDWFC	FSR0L,F        		;Sumo la parte baja del puntero con el Paso byte 3 del buffer y lo almaceno en FSR0L
MOVF		INDF0,W			;Copia el contenido del puntero del Buffer en WREG
MOVF		PORT_SALIDA,F		;Manda al puerto el contenido del puntero del Buffer
...

Se supone que cuando hago "MOV INDF0,W", muevo el dato al que apunta FSR0 (FSR0H:FSR0L => 12 bits, no 16, cosas raras del uC) a WREG, para luego sacarlo por la dirección del puerto, en este caso B. Faltó hacer que el puerto funcione como salida creo que es usando el registro TRISB o algo por el estilo.

Por otro lado, seguís en la misma, necesitás usar un puerto del PIC para fijar la dirección que debe leer la RAM externa.
 
Excelentes noticias,

Logre hacer la implementacion de tu codigo resumido. Lamentablemente antes estaba usando el acceso al vector tal cual lo generaba el compilador. Leyendo tu codigo y despues de estudiar sobre el indirect addressing (aprendi algo nuevo para mi!) pude implementar lo siguiente:

Código:
void bucle_generar(void) {

   paso_hi = 255;
   paso_lo = 255;

   #ASM ASIS
   CLRF  0xFE9 //FSR0L
   MOVLW 0x01
   MOVWF 0xFEA //FSR0H
   
   buclesenal:
   MOVF   paso_lo,W
   ADDWF  prescaler_lo,F
   MOVF   paso_hi,W
   ADDWFC prescaler_hi,F
   MOVLW  00
   ADDWFC 0xFE9,F //FSR0L
   MOVF   0xFEF,W //INDF0
   MOVWF  0xF8A   //LATB
   GOTO buclesenal
   NOP
   #ENDASM

}

El cual es el prescaler en 24bits ocupando 10 instrucciones (40 clocks). Esto me da un sample cada 833.33ns lo cual es bastante aceptable (1.2MHZ) que a 4 samples son 300 khz y si uso otra rutina que sea con un prescaler de menos resolucion, aumento la velocidad. Puedo llegar a realizar algunas tecnicas de soft para llegar a 416.65ns lo que me daria 2.4MHZ que a 4 samples son 600 khz.

Esto ultimo seria que, para cuando quiero generar frecuencias muy altas, modifico el vector en RAM para poner (senoidal) 255,127,0,127 periodicos y el bucle seria algo como:

Código:
void bucle_generar_max(void) {

   #ASM ASIS
   CLRF  0xFE9 //FSR0L
   MOVLW 0x01
   MOVWF 0xFEA //FSR0H
   
   buclesenal:
   INCF    0xFE9,F //FSR0L
   MOVF   0xFEF,W //INDF0
   MOVWF  0xF8A   //LATB
   GOTO buclesenal
   NOP
   #ENDASM

}

En estas rutinas no se haria necesaria una bandera para saber si hubo interrupcion, porque o bien puedo modificar los parametros dentro de la interrupcion y que el bucle siga su curso luego, o bien puedo hacer un salto forzado en la interrupcion para que salga del bucle.

A grandes rasgos, creo que podriamos decir que es posible hacer este proyecto con PIC de una forma aceptable y entendiendo sus limitaciones.

Por otro lado, ahora tengo que resolver como voy a manejar la interfaz del usuario, ya que para este pic en particular aparecen los siguientes problemas:

- Al puerto A le falta 1 pin de los 8 bits (no se puede usar como salida al DAC)
- Al puerto C lo mismo que el A
- Solo queda el puerto B disponible entero para el DAC
- Las interrupciones externas (cambio de estado de pin) solo se dan en el puerto B (mas limitaciones!!!!)

Creo que lo puedo resolver por medio de las interrupciones de:
- Pin CCP (Puerto C)
- Interrupciones de comparador (Puerto A)
- Interrupcion RS232 (Puerto A) Pero esto requeriria una conexion serial a otro pic (una locura!)

Mas alla de estos problemas a resolver (son solucionables) me queda disponible la conexion USB para manejarlo desde la computadora :)

Luego de ello queda implementar el filtro que diseñaste pero quiza modificandolo para fuente simple... no se.

A esta altura, yo sigo posteando aqui por varias razones:

- Este es un proyecto que anda bien y esta bien pensado
- Creo que puede haber un interes para hacerlo con PIC, para aquellos que no pueden acceder a AVR como yo.

Sin embargo, como va tomando un rumbo separado de tu proyecto (ya son mas que cambios de micro) y para no caer en offtopic, quisiera pedir permiso para seguir posteando aca en vez de crear un nuevo thread. Ustedes diran!

PD: Adjunto el codigo C para CCS como lo vengo realizando. Esta super sucio y verde aun pero sirve para hacer una simulacion
 

Adjuntos

  • siggen2550.txt
    5.8 KB · Visitas: 8
Última edición:
Ojo que te quedó mal la parte donde modificás en índice, ya que en tu rutina:

Código:
MOVLW  00
ADDWFC 0xFE9,F //FSR0L

Estás todo el tiempo asignando al índice el valor "0" + carry anterior. Ahí deberías cargar el byte del paso 3, acordate lo que te dije arriba, tanto el paso como acumulador deben tener la misma cantidad de bits. Entonces eso queda:

Código:
MOVF   paso_ultra_hi,W
ADDWFC 0xFE9,F //FSR0L

Otra cosa muy importante y no lo aclaré, es necesario que la parte baja de la dirección de la primera muestra del buffer de datos sea "0", para esto es necesario imponer dicha dicha dirección, eso lo hacés desde "C" usando un puntero, pero a sabiendas de que las 255 direcciones de RAM que le siguen estén liberadas para su uso.

Yo en mí código utilizo esta dirección:

Código:
unsigned char * buffer;
...
buffer=(u8 *)(0x300);	//Impongo que el vector de datos empiece en la posición de memoria 0x300 => los datos irán de 0x300 a 0x3FF
...

Como dato interesante, en un principio le asigné la dirección 0x200, funcionaba perfecto hasta que agregué el control a través del puerto serie, a partir de ahí cuando generaba la señal, las primeras muestras de la señal eran pisados por el programa, con lo cual la señal salía mal.

A lo que voy con esto, vas a tener que ubicar bien ese buffer de memoria, analizar donde resulta más conveniente su dirección.

seaarg dijo:
El cual es el prescaler en 24bits ocupando 10 instrucciones (40 clocks). Esto me da un sample cada 833.33ns lo cual es bastante aceptable (1.2MHZ) que a 4 samples son 300 khz y si uso otra rutina que sea con un prescaler de menos resolucion, aumento la velocidad. Puedo llegar a realizar algunas tecnicas de soft para llegar a 416.65ns lo que me daria 2.4MHZ que a 4 samples son 600 khz.

Perfecto, eso va ayudar al filtro.

seaarg dijo:
Por otro lado, ahora tengo que resolver como voy a manejar la interfaz del usuario, ya que para este pic en particular aparecen los siguientes problemas:

- Al puerto A le falta 1 pin de los 8 bits (no se puede usar como salida al DAC)
- Al puerto C lo mismo que el A
- Solo queda el puerto B disponible entero para el DAC
- Las interrupciones externas (cambio de estado de pin) solo se dan en el puerto B (mas limitaciones!!!!)

Creo que lo puedo resolver por medio de las interrupciones de:
- Pin CCP (Puerto C)
- Interrupciones de comparador (Puerto A)
- Interrupcion RS232 (Puerto A) Pero esto requeriria una conexion serial a otro pic (una locura!)

Mas alla de estos problemas a resolver (son solucionables) me queda disponible la conexion USB para manejarlo desde la computadora

Controlalo por serie/usb desde una PC, similar al proyecto de Jesper (proyecto que usé como ejemplo) o al mío.

seaarg dijo:
A esta altura, yo sigo posteando aqui por varias razones:

- Este es un proyecto que anda bien y esta bien pensado
- Creo que puede haber un interes para hacerlo con PIC, para aquellos que no pueden acceder a AVR como yo.

Sin embargo, como va tomando un rumbo separado de tu proyecto (ya son mas que cambios de micro) y para no caer en offtopic, quisiera pedir permiso para seguir posteando aca en vez de crear un nuevo thread. Ustedes diran!

PD: Adjunto el codigo C para CCS como lo vengo realizando. Esta super sucio y verde aun pero sirve para hacer una simulacion

Por mí está bien, la idea es hacer un generador a partir de un uC sin importar su familia.

Si el PLL no molesta, este proyecto estaría piola hacerlo con un ARM Cortex que pueden trabajar a 120MHz y encima al ser de 32bits, podés armar un acumulador de 32bits y trabajarlo en una sola instrucción :cool:.
 
Aca es donde se me crea confusion y es precisamente en lo que marcas de mover el literal 0 a W

MOVLW 00

Resulta que si yo acumulo un entero de 32 bits en un acumulador de 32 bits el compilador genera esto:

Código:
....................    unsigned int32 paso = 0; 
....................    unsigned int32 acum = 0; 
....................    acum = acum + paso; 
0166:  MOVF   paso,W
0168:  ADDWF  acum,F
016A:  MOVF   paso+1,W
016C:  ADDWFC acum+1,F
016E:  MOVF   paso+2,W
0170:  ADDWFC acum+2,F
0172:  MOVF   paso+3,W
0174:  ADDWFC acum+3,F

Donde paso es el byte mas bajo y paso+2 es el byte mas alto

Ahora bien, si sumo un entero de 16 bits (el paso) en un acumulador de 24 bits (32 en rigor, con el compilador. Con ASM a mano lo hago en 3 variables distintas para formar 24 bits)

Código:
....................    unsigned int16 paso = 0; 
....................    unsigned int32 acum = 0; 
....................    acum = acum + paso; 
0162:  MOVF   paso,W
0164:  ADDWF  acum,F
0166:  MOVF   paso+1,W
0168:  ADDWFC acum+1,F
016A:  MOVLW  paso+-9
016C:  ADDWFC acum+2,F
016E:  ADDWFC acum+3,F    ;Descartemos esta linea para 24 bits

Donde la linea

Código:
016A:  MOVLW  paso+-9

Sacando el modo simbolico de CSS que te pone los nombres de variables, en el HEX es esto:

Código:
016A:  MOVLW  00

Es decir, poner un 0 en W antes de adicionar ese registro a acum+2 teniendo en cuenta el carry.

Entonces, si en mi bucle saco esa linea directamente no anda, nunca cambia de 0 el valor del byte mas alto. (obvio!)

El compilador hace eso porque el paso es menor en bytes que el acumulador.

De hecho, acabo de implementar un 3r byte para paso (quedando en 24bits) y forzosamente tengo que establecer el byte mas alto en cero.

Cuando defino el paso de esta forma:

Código:
   paso_hi = 0;
   paso_mi = 255;
   paso_lo = 255;

Obtengo 1 sample de los 256 en cada vuelta del bucle (maxima frecuencia para la onda seno completa almacenada tal cual en ram)

Al hacer esto:

Código:
   paso_hi = 2;
   paso_mi = 255;
   paso_lo = 255;

Empiezo a tener 1 sample cada 2 de los 256, lo cual logicamente aumenta la frecuencia.

Analizando esto, creo entender ahora porque me sugeris esto:

- Vos tenes almacenado en ram un vector con la onda seno completa distribuida en las 256 posiciones.
- El paso lo haces a 24 bits, cosa de saltear muestras de esa onda cuando paso_hi > 0

Como yo lo habia encarado, iba a adaptar el vector en RAM haciendo que para frecuencias bajas tuviera 1 ciclo senoidal en las 256 posiciones, y para frecuencias mas altas, mas ciclos senoidales en esas mismas 256 posiciones.

Resumiendo, y atento a tus sugerencias, mi codigo quedo asi:

Código:
void bucle_generar(void) {

   paso_hi = 0;
   paso_mi = 255;
   paso_lo = 255;

   #ASM ASIS
   CLRF  0xFE9 //FSR0L
   MOVLW 0x01
   MOVWF 0xFEA //FSR0H
   
   buclesenal:
   MOVF   paso_lo,W
   ADDWF  prescaler_lo,F
   MOVF   paso_mi,W
   ADDWFC prescaler_mi,F
   MOVF   paso_hi,W
   ADDWFC 0xFE9,F //FSR0L
   MOVF   0xFEF,W //INDF0
   MOVWF  0xF8A   //LATB
   GOTO buclesenal
   NOP
   #ENDASM

}

Donde FSR0L viene a ser el byte mas alto del prescaler. Esta forma es ventajosa porque puedo ir olvidandome de tener que hacer una rutina que adapte el vector en RAM a distintos rangos de frecuencia, gracias a que el byte mas alto del paso me da cuantos samples se saltean.

Muchas Gracias!!

Otra cosa muy importante y no lo aclaré, es necesario que la parte baja de la dirección de la primera muestra del buffer de datos sea "0", para esto es necesario imponer dicha dicha dirección, eso lo hacés desde "C" usando un puntero, pero a sabiendas de que las 255 direcciones de RAM que le siguen estén liberadas para su uso.

Exacto, esto lo tuve en cuenta aqui:

Código:
unsigned int8  vector_senal[256];
#LOCATE vector_senal=0x0100

Elegi la direccion base para el byte alto del buffer en 0x01 "por ahora" hasta que vea que pasa al adicionar rutinas de usb. El byte bajo siempre es cero para poder direccionar directo con el acumulador.

Como curiosidad, te sugiero que pruebes en tu programa de declarar un vector con solo 4 valores para senoidal: 255,127,0,127 repetidos en las 256 posiciones y lo recorras en otro bucle a maxima velocidad, simplemente incrementando el puntero de indireccion al dato (sin acumulador). Con tu micro vas a lograr una velocidad muy interesante. Esto lo usarias solamente en caso de querer generar una frecuencia mayor a la que te da actualmente con tus 9 clocks. De esta forma pensaba llegar a los 416ns por sample en mi caso. En el tuyo sera mucho mejor!.

Si el PLL no molesta, este proyecto estaría piola hacerlo con un ARM Cortex que pueden trabajar a 120MHz y encima al ser de 32bits, podés armar un acumulador de 32bits y trabajarlo en una sola instrucción

Que lindo seria eso, tengo que actualizarme a micros mas modernos :unsure:
 
Última edición:
Aca es donde se me crea confusion y es precisamente en lo que marcas de mover el literal 0 a W

MOVLW 00

Ahora bien, si sumo un entero de 16 bits (el paso) en un acumulador de 24 bits (32 en rigor, con el compilador. Con ASM a mano lo hago en 3 variables distintas para formar 24 bits)

Código:
....................    unsigned int16 paso = 0; 
....................    unsigned int32 acum = 0; 
....................    acum = acum + paso; 
0162:  MOVF   paso,W
0164:  ADDWF  acum,F
0166:  MOVF   paso+1,W
0168:  ADDWFC acum+1,F
016A:  MOVLW  paso+-9
016C:  ADDWFC acum+2,F
016E:  ADDWFC acum+3,F    ;Descartemos esta linea para 24 bits

Donde la linea

Código:
016A:  MOVLW  paso+-9

Sacando el modo simbolico de CSS que te pone los nombres de variables, en el HEX es esto:

Código:
016A:  MOVLW  00

Es decir, poner un 0 en W antes de adicionar ese registro a acum+2 teniendo en cuenta el carry.

Entonces, si en mi bucle saco esa linea directamente no anda, nunca cambia de 0 el valor del byte mas alto. (obvio!)

El compilador hace eso porque el paso es menor en bytes que el acumulador.

Exacto, por eso remarque, el paso debe ser de 24 bits al igual que el acumulador, de esta forma te permite trabajar con altas frecuencias, por ej. 75KHz solo requiere de 23 muestras, por lo tanto el 3 byte del paso deberá valer 11 + el carry del byte 2 del acumulador.

De hecho, acabo de implementar un 3r byte para paso (quedando en 24bits) y forzosamente tengo que establecer el byte mas alto en cero.

Cuando defino el paso de esta forma:

Código:
   paso_hi = 0;
   paso_mi = 255;
   paso_lo = 255;

Obtengo 1 sample de los 256 en cada vuelta del bucle (maxima frecuencia para la onda seno completa almacenada tal cual en ram)

Al hacer esto:

Código:
   paso_hi = 2;
   paso_mi = 255;
   paso_lo = 255;

Empiezo a tener 1 sample cada 2 de los 256, lo cual logicamente aumenta la frecuencia.

Analizando esto, creo entender ahora porque me sugeris esto:

- Vos tenes almacenado en ram un vector con la onda seno completa distribuida en las 256 posiciones.
- El paso lo haces a 24 bits, cosa de saltear muestras de esa onda cuando paso_hi > 0

Ese valor del paso saldrá de la fórmula que puse en el 1er mensaje, primero calculás la resolución y en base a ese valor y la frecuencia de salida, calculás el valor del paso.

Resumiendo, y atento a tus sugerencias, mi codigo quedo asi:

Código:
void bucle_generar(void) {

   paso_hi = 0;
   paso_mi = 255;
   paso_lo = 255;

   #ASM ASIS
   CLRF  0xFE9 //FSR0L
   MOVLW 0x01
   MOVWF 0xFEA //FSR0H
   
   buclesenal:
   MOVF   paso_lo,W
   ADDWF  prescaler_lo,F
   MOVF   paso_mi,W
   ADDWFC prescaler_mi,F
   MOVF   paso_hi,W
   ADDWFC 0xFE9,F //FSR0L
   MOVF   0xFEF,W //INDF0
   MOVWF  0xF8A   //LATB
   GOTO buclesenal
   NOP
   #ENDASM

}

Donde FSR0L viene a ser el byte mas alto del prescaler. Esta forma es ventajosa porque puedo ir olvidandome de tener que hacer una rutina que adapte el vector en RAM a distintos rangos de frecuencia, gracias a que el byte mas alto del paso me da cuantos samples se saltean.

Muchas Gracias!!

De ese código yo modificaría la función de C, para que puedas pasarle por argumentos el valor del paso calculado, es decir:

Código:
void bucle_generar(unsigned char paso_lo,unsigned char paso_mi,unsigned char paso_hi) {
   ...
}

Y esos tres valores los calculás con la fórmula que mencioné arriba.

Exacto, esto lo tuve en cuenta aqui:

Código:
unsigned int8  vector_senal[256];
#LOCATE vector_senal=0x0100

Elegi la direccion base para el byte alto del buffer en 0x01 "por ahora" hasta que vea que pasa al adicionar rutinas de usb. El byte bajo siempre es cero para poder direccionar directo con el acumulador.

Perfecto, cualquier cosa, simplemente modificás la parte alta como mencionás.

Como curiosidad, te sugiero que pruebes en tu programa de declarar un vector con solo 4 valores para senoidal: 255,127,0,127 repetidos en las 256 posiciones y lo recorras en otro bucle a maxima velocidad, simplemente incrementando el puntero de indireccion al dato (sin acumulador). Con tu micro vas a lograr una velocidad muy interesante. Esto lo usarias solamente en caso de querer generar una frecuencia mayor a la que te da actualmente con tus 9 clocks. De esta forma pensaba llegar a los 416ns por sample en mi caso. En el tuyo sera mucho mejor!.

No es mala idea, si bien conseguí mejorar bastante la fsampling haciendo que el acumulador para frecuencias mayores a 300kHz sea de solo 8bits, haciendo lo que decís supongo que puedo tirar todavía un poco más arriba esa fsampling mejorando el desempeño del filtro.
 
De ese código yo modificaría la función de C, para que puedas pasarle por argumentos el valor del paso calculado, es decir:

Lo que pasa es que en mi programa, el bucle con acumulador planeo hacerlo bucle principal (el while dentro del main() sera) Cosa de modificar externamente los valores desde la interrupcion y que al salir de la misma, el bucle sigue. No tengo que re-llamar a una funcion de bucle y con esto me ahorro tener que comprobar una bandera dentro del bucle (para salir del mismo) a costa de tener que deshabilitar interrupciones una vez que entro en la interrupcion para poder hacer un proceso largo (entrada de teclado y ajuste de parametros)


No es mala idea, si bien conseguí mejorar bastante la fsampling haciendo que el acumulador para frecuencias mayores a 300kHz sea de solo 8bits, haciendo lo que decís supongo que puedo tirar todavía un poco más arriba esa fsampling mejorando el desempeño del filtro.

Me alegro de poder aportar algo al tema principal (y)
 
Hola! Perdon por tardar en responder, pero estoy en medio de época de exámenes jaja
La verdad tengo ya medio terminado el menu del generador, pero claro, aun no tengo muy claro como insertar la frecuencia y cómo funciona...
Se supone que tenemos 3 registros de 1 byte cada uno, no? Es decir, en r24, r25 y r26 se almacena el valor de la frecuencia, no? Entonces, una de las preguntas es, el valor de la frecuencia se almacena linealmente? Es decir...
Frecuencia 1Hz=00000000(r26)00000000(r25)00000001(r24)
Frecuencia 2Hz=00000000(r26)00000000(r25)00000010(r24)
..........
Frecuencia 100Hz=00000000(r26)00000000(r25)01100100(r24) etc

Y si es así, se supone que en los registros r28,r29 y r30 se almacena lo que se llama como acumulador de fase, que aun no entiendo muy bien lo que es :confused:
En el ejemplo que puso antes cosmefulanito, se supone que la frecuencia era de 100Hz, y que entonces el paso del acumulador sería de unos 943. Siendo así,

Frecuencia 100Hz=00000000(r26)00000000(r25)01100100(r24)
Acumulador 943=00000000(r30)00000011(r29)10101111(r28)

De esta forma, el bucle sumaría el acumulador un total de 256 veces, y cada vez que lo suma, saca un valor de la tabla de la onda, no? Es decir, en la primera vuelta, el acumulador vale 943, en la segunda vuelta 1886, etc. Y va sacando valores... Pero entonces para que usamos el valor de los registros r24, r25 y r26?

Aun no se como implementarlo, pero simplemente he pensado lo siguiente, si tenemos una frecuencia X la que sea, para hallar el periodo tenemos T=1/X, ese sería el tiempo que tarda un ciclo de una onda. De esta forma, ese tiempo se debe dividir entre 256, entonces, se deberia hacer una rutina de retardo de forma que entre cada salida del valor establezca t=1/(X*256), pero no se si es eficaz :S que me responden?
 
Para que te cierre el concepto del acumulador y el paso que se le va dando a medida que se suma, te doy un ejemplo.

En base a mí generador, el acumulador es de 24bits, por lo tanto a la rutina en assembler le toma 9 clk y el cristal utilizado es de 16MHz, dando una fsampling máxima es de 16MHz/9clk=1,77MHz

Del cálculo loco que mencioné arriba llegás a que:

Resolución acumulador 24 bits = fsampling/2^(24) = 0,10596... Hz

Paso que se sumará al acumulador = Fsalida/Resolución acumulador 24 bits = 9,437184 (1/Hz) . Fsalida

Supongamos que a la salida se desea una frecuencia de 400kHz:

Paso que se sumará al acumulador = 9,437184 (1/Hz) . Fsalida = 3774873

Si pasamos a hexadecimal ese Nº nos da 0x399999, esto implica que:

Paso byte 1 = 153
Paso byte 2 = 153
Paso byte 3 = 57

Si usamos el byte 3 del acumulador como índice de un vector que contiene 256 elementos, el muestreo a partir de las sumas será el siguiente:

Acum byte 1=0
Acum byte 2=0
Acum byte 3=0 (índice)

. Muestra número 1:

Acum byte 1 + Paso byte 1 = 153 → Acum byte 1
Acum byte 2 + Paso byte 2 = 153 → Acum byte 2
Acum byte 3 + Paso byte 2 = 57 → Acum byte 3

Muestreo el elemento 57 que se encuentra en el vector de datos.

. Muestra número 2:

Acum byte 1 + Paso byte 1 = 153 + 153 = 50 y hubo carry→ Acum byte 1
Acum byte 2 + Paso byte 2 = 153 + 153 + carry (1) = 51 y hubo carry→ Acum byte 2
Acum byte 3 + Paso byte 2 = 57 + 57 + carry (1) =115 → Acum byte 3

Muestreo el elemento 115 que se encuentra en el vector de datos.

. Muestra número 3:

Acum byte 1 + Paso byte 1 = 50 + 153 = 203→ Acum byte 1
Acum byte 2 + Paso byte 2 = 51 + 153 = 204→ Acum byte 2
Acum byte 3 + Paso byte 2 = 115 + 57 = 172 → Acum byte 3

Muestreo el elemento 172 que se encuentra en el vector de datos.

. Muestra número 4:

Acum byte 1 + Paso byte 1 = 203 + 153 = 100 y hubo carry→ Acum byte 1
Acum byte 2 + Paso byte 2 = 204 + 153 + carry (1) = 102 y hubo carry→ Acum byte 2
Acum byte 3 + Paso byte 2 = 172 + 57 + carry (1) = 230 → Acum byte 3

Muestreo el elemento 230 que se encuentra en el vector de datos.

. Muestra número 5:

Acum byte 1 + Paso byte 1 = 100 + 153 = 253 → Acum byte 1
Acum byte 2 + Paso byte 2 = 102 + 153 = 255→ Acum byte 2
Acum byte 3 + Paso byte 2 = 230 + 57 = 31 y hubo desborde → Acum byte 3

Acá se ve como el acumulador desborda los 256 elementos y empieza de nuevo a muestrear otro periodo.

En resumen para 400kHz solo es posible tomar 4 muestras, lo cual suena lógico si uno hace fsampling/fsalida= 4,44; solo 4 muestras posibles.

Si ahora hacés lo mismo con 100Hz, vas a ver que la cantidad de muestras (repetidas) que haces es enorme, 17777 muestras.
 
Muchas gracias por la aclaracion cosmefulanito! Ya consegui entenderlo :LOL: lo único malo es que casi casi no es factible hacerlo con PIC, por una razón muy sencilla... no permite sumar dos registros con el acarreo, es decir, se debe comprobar aparte el acarreo, y si existe, sumarlo. He logrado hacer una pequeña simulación con proteus a 1Hz, pero evidentemente existe algún fallo :S os dejo el programa para que le echeis un vistazo.

HTML:
Bucle			movfw	0x40      ;Movemos el valor del Paso 1 a W
					addwf	0x43,1   ;Sumamos el valor de W al Acumulador 1
					movfw	0x41      ;Movemos el valor del Paso 2 a W
					btfsc	0x03,0           ;Comprobamos el acarreo
					call	Limpiar           ;Si hay acarreo, llamamos a 'Limpiar'
					addwf	0x44,1   ;Sumamos el valor de W al Acumulador 2
					movfw	0x42     ;Movemos el valor del Paso 3 a W
					btfsc	0x03,0           ;Comprobamos el acarreo
					call	Limpiar           ;Si hay acarreo, llamamos a 'Limpiar'
					addwf	0x45,1   ;Sumamos el valor de W al Acumulador 3
					movfw	0x45      ;Movemos el valor del Acumulador 3 a W
					call	OndaSenoidal   ;Llamamos a la Onda Senoidal
					movwf	0x08      ;Movemos el valor de W al puerto de salida
					goto	Bucle             ;Repetimos el bucle
					
Limpiar				bcf		0x03,0     ;Limpiamos el bit de acarreo
					addlw	0x01                ;Sumamos 1 al valor de W
					return                      ;Volvemos al bucle
					
					
OndaSenoidal		addwf	0x02,1             ;Sumamos al PCL el valor de W
					retlw	0x80                ;Retorna con el valor seleccionado
					retlw	0x83
					retlw	0x86
					retlw	0x89
					retlw	0x8C
					retlw	0x8F
					retlw	0x92
					retlw	0x95
					retlw	0x98
					retlw	0x9C
					retlw	0x9F
					retlw	0xA2
					retlw	0xA5
					retlw	0xA8
					retlw	0xAB
					retlw	0xAE
					retlw	0xB0
					retlw	0xB3
					retlw	0xB6
					retlw	0xB9
					retlw	0xBC
					retlw	0xBF
					retlw	0xC1
					retlw	0xC4
					retlw	0xC7
					retlw	0xC9
					retlw	0xCC
					retlw	0xCE
					retlw	0xD1
					retlw	0xD3
					retlw	0xD5
					retlw	0xD8
					retlw	0xDA
					retlw	0xDC
					retlw	0xDE
					retlw	0xE0
					retlw	0xE2
					retlw	0xE4
					retlw	0xE6
					retlw	0xE8
					retlw	0xEA
					retlw	0xEC
					retlw	0xED
					retlw	0xEF
					retlw	0xF0
					retlw	0xF2
					retlw	0xF3
					retlw	0xF5
					retlw	0xF6
					retlw	0xF7
					retlw	0xF8
					retlw	0xF9
					retlw	0xFA
					retlw	0xFB
					retlw	0xFC
					retlw	0xFC
					retlw	0xFD
					retlw	0xFE
					retlw	0xFE
					retlw	0xFF
					retlw	0xFF
					retlw	0xFF
					retlw	0xFF
					retlw	0xFF
					retlw	0xFF
					retlw	0xFF
					retlw	0xFF
					retlw	0xFF
					retlw	0xFF
					retlw	0xFF
					retlw	0xFE
					retlw	0xFE
					retlw	0xFD
					retlw	0xFC
					retlw	0xFC
					retlw	0xFB
					retlw	0xFA
					retlw	0xF9
					retlw	0xF8
					retlw	0xF7
					retlw	0xF6
					retlw	0xF5
					retlw	0xF3
					retlw	0xF2
					retlw	0xF0
					retlw	0xEF
					retlw	0xED
					retlw	0xEC
					retlw	0xEA
					retlw	0xE8
					retlw	0xE6
					retlw	0xE4
					retlw	0xE2
					retlw	0xE0
					retlw	0xDE
					retlw	0xDC
					retlw	0xDA
					retlw	0xD8
					retlw	0xD5
					retlw	0xD3
					retlw	0xD1
					retlw	0xCE
					retlw	0xCC
					retlw	0xC9
					retlw	0xC7
					retlw	0xC4
					retlw	0xC1
					retlw	0xBF
					retlw	0xBC
					retlw	0xB9
					retlw	0xB6
					retlw	0xB3
					retlw	0xB0
					retlw	0xAE
					retlw	0xAB
					retlw	0xA8
					retlw	0xA5
					retlw	0xA2
					retlw	0x9F
					retlw	0x9C
					retlw	0x98
					retlw	0x95
					retlw	0x92
					retlw	0x8F
					retlw	0x8C
					retlw	0x89
					retlw	0x86
					retlw	0x83
					retlw	0x80
					retlw	0x7C
					retlw	0x79
					retlw	0x76
					retlw	0x73
					retlw	0x70
					retlw	0x6D
					retlw	0x6A
					retlw	0x67
					retlw	0x63
					retlw	0x60
					retlw	0x5D
					retlw	0x5A
					retlw	0x57
					retlw	0x54
					retlw	0x51
					retlw	0x4F
					retlw	0x4C
					retlw	0x49
					retlw	0x46
					retlw	0x43
					retlw	0x40
					retlw	0x3E
					retlw	0x3B
					retlw	0x38
					retlw	0x36
					retlw	0x33
					retlw	0x31
					retlw	0x2E
					retlw	0x2C
					retlw	0x2A
					retlw	0x27
					retlw	0x25
					retlw	0x23
					retlw	0x21
					retlw	0x1F
					retlw	0x1D
					retlw	0x1B
					retlw	0x19
					retlw	0x17
					retlw	0x15
					retlw	0x13
					retlw	0x12
					retlw	0x10
					retlw	0x0F
					retlw	0x0D
					retlw	0x0C
					retlw	0x0A
					retlw	0x09
					retlw	0x08
					retlw	0x07
					retlw	0x06
					retlw	0x05
					retlw	0x04
					retlw	0x03
					retlw	0x03
					retlw	0x02
					retlw	0x01
					retlw	0x01
					retlw	0x00
					retlw	0x00
					retlw	0x00
					retlw	0x00
					retlw	0x00
					retlw	0x00
					retlw	0x00
					retlw	0x00
					retlw	0x00
					retlw	0x00
					retlw	0x00
					retlw	0x01
					retlw	0x01
					retlw	0x02
					retlw	0x03
					retlw	0x03
					retlw	0x04
					retlw	0x05
					retlw	0x06
					retlw	0x07
					retlw	0x08
					retlw	0x09
					retlw	0x0A
					retlw	0x0C
					retlw	0x0D
					retlw	0x0F
					retlw	0x10
					retlw	0x12
					retlw	0x13
					retlw	0x15
					retlw	0x17
					retlw	0x19
					retlw	0x1B
					retlw	0x1D
					retlw	0x1F
					retlw	0x21
					retlw	0x23
					retlw	0x25
					retlw	0x27
					retlw	0x2A
					retlw	0x2C
					retlw	0x2E
					retlw	0x31
					retlw	0x33
					retlw	0x36
					retlw	0x38
					retlw	0x3B
					retlw	0x3E
					retlw	0x40
					retlw	0x43
					retlw	0x46
					retlw	0x49
					retlw	0x4C
					retlw	0x4F
					retlw	0x51
					retlw	0x54
					retlw	0x57
					retlw	0x5A
					retlw	0x5D
					retlw	0x60
					retlw	0x63
					retlw	0x67
					retlw	0x6A
					retlw	0x6D
					retlw	0x70
					retlw	0x73
					retlw	0x76
					retlw	0x79
					retlw	0x7C

El problema que existe al intentar calcular la resolución del acumulador de 24 bits es que existen dos comandos 'btfsc' en el bucle, los cuales, cuando existe acarreo, usan 1 ciclo de reloj, y cuando no lo hay, usan 2 ciclos... asi que es dificil determinar la resolucion :confused:
 

Adjuntos

  • Generador DDS.bmp
    89.9 KB · Visitas: 6
  • Onda.jpg
    Onda.jpg
    126.5 KB · Visitas: 8
Tenés una instrucción que es la "ADDWFC" que te permite hacer esa suma con carry, fijate en el mensaje #21, ahí le dejé el código a seaarg que después él hizo algo parecido.

Y como decís, con el PIC vas a tener que trabajar directamente sobre la memoria RAM y el registro WREG.
 
Según el datasheet del PIC que estoy usando (16F877A), la instrucción que permite es ADDWF, además, intenté usar la instrucción que mencionas en el MPlab, pero me da error (Illegal Character), asi que no se si habrá forma de sumar el acarreo sin usar el registro de Status.
 
Según el datasheet del PIC que estoy usando (16F877A), la instrucción que permite es ADDWF, además, intenté usar la instrucción que mencionas en el MPlab, pero me da error (Illegal Character), asi que no se si habrá forma de sumar el acarreo sin usar el registro de Status.

Con es microcontrolador (uC), te quedás muy corto en velocidad, ya que solo puede trabajar hasta 20MHz y c/ciclo requiere de 4 clks.

Y es como decís, no tiene la instrucción "ADDWFC", por lo tanto tenés que pedalear mucho más quedándote una rutina muy larga.

Vas a tener que pasarte a la familia 18Fxx, algo similar a lo que está usando seaarg o al lado oscuro y usar un Atmega 16 :LOL:.

Perdoná por no darme cuenta de eso en tu esquemático :oops:.
 
Última edición:
Según el datasheet del PIC que estoy usando (16F877A), la instrucción que permite es ADDWF, además, intenté usar la instrucción que mencionas en el MPlab, pero me da error (Illegal Character), asi que no se si habrá forma de sumar el acarreo sin usar el registro de Status.

Efectivamente, la serie 16F no tiene ADDWFC, ni MOVFF

De todos modos, podrias hacer el tiempo de bucle constante intercalando algun que otro NOP pero de por si es larguisima asi que con ese pic solo vas a poder generar frecuencias bajas.

Yo logre con el 18F reducir bastante y acercarme al AVR, pero antes de seguir, estoy estudiando la factibilidad de hacer algo asi:

prescaler-hard.png

Semejante cantidad de integrados vendrian a ser lo mismo que hizo cosme dentro del AVR, solamente que implementados totalmente en hardware. El micro solo establece los datos de la forma de onda, controla interfaz de usuario y nada mas (es un 16F88).

Con esto puedo tener una F Sample de 40mhz o 32mhz en el peor de los casos. La cuestion problematica va a ser el filtro analogico, que tendra que responder a frecuencias de 10mhz sin distorsionar... pero si anda... y si puedo hacer la PCB de semejante maraña sin volverme loco... podria generar una senoidal de hasta 10mhz con un prescaler de 24 bits.

Son seis sumadores full-adder de 4 bits (74LS283), 3 latches 74LS374, 5 serial shifter de 8 bits (74HC4094), una SRAM IS61C256AH de 25ns y el 16F88

Tambien se puede reemplazar la RAM por una EPROM, simplificando el circuito pero la Fsample cae bastante porque paso de 25ns por sample a 150ns por sample.
 
Última edición:
Si eso lo hacés con un FPGA, seguro que te ahorrás un montón de problemas, el problema es saber usar un FPGA :LOL:.

Les ando con muchas ganas a los FPGA, el problema es conseguirlos.

Ahora reemplace todos los adders + latches con unas gal 22v10 para facilitarme el armado del PCB. De 16 integrados lo reduje a 11 y estoy trabajando para reducirlo a 10 (3 serial paralelo, 5 gal, 1 memoria y el pic) siempre hablando de la parte de sintesis de señal, luego viene la parte analogica.

Como las gal responden en 10ns y la sram en 25ns, podria en teoria tirar un sample cada 25ns. Como me va a costar hacer el filtro para esas velocidades!!!
 
Muchachos, tengo unas dudas que quiza me podrian despejar.

Pude hacer el generador DDS a puro hardware (adjunto el esquematico) es decir, haciendo un prescaler de 24 bits con sumadores 74LS283, que manejan una SRAM donde estan almacenadas las 256 muestras.

Del bus de datos de la SRAM sale el conversor A/D por lo tanto, y el ultimo conjunto de sumadores (MSB) es el que maneja la direccion de la ram.

Es basicamente lo mismo que hizo cosme en soft con un micro mas potente como el atmel pero hecho en hardware para subir un poco los limites.

El reloj de sistema es un oscilador a cristal de 8mhz (probe 20mhz y los sumadores se quedan lentos, 16mhz anda pero lo baje a 8 para mas seguridad al principio de mis pruebas).

La cuestion es que funciona bastante bien, la señal esta bastante bien en casi todo el rango, con la salvedad que voy a tener que mejorar el filtro para frecuencias bajas.

Genero sin problemas senoidales de hasta 4mhz aunque necesito ajustas algunos parametros para que salga un poco mas bonita a tan altas frecuencias. (hasta 1mhz esta barbara)

Ahora mis preguntas: Estoy teniendo algunos problemas con el prescaler. Por ej:

Si elijo que genere una frecuencia de tal forma que el prescaler se acerque a 65535 (por ej: 30000 hz) el generador no tira una frecuencia exacta sino 31.25khz (que casualmente es la frecuencia que deberia tener con prescaler = 65535)

Hice los siguientes calculos:

8mhz / 2^24 = 0.476837158203125 de resolucion.

por lo tanto, para sacar 30 khz:

30000 / 0.476837158203125 = 62914.56 prescaler. Parte entera = 62914

62915 00000000 1111010111000011
62915 + 62915 = 125830 00000001 1110101110000110
125830 + 62915 = 188745 00000010 1110000101001001
188745 + 62915 = 251660 00000011 1101011100001100

De aqui se ve que por cada ciclo de reloj cambiara en 1 la direccion de sample, dandome:

8mhz = 125ns * 256 samples = 32000 ns = 32 us = 31.25 khz

Si, dentro de las 256 muestras en vez de un periodo de la senoidal almaceno 2, tengo que para ese prescaler estoy generando 62.5 khz

Otra prueba:

15000 hz / 0.476837158203125 = 31457.28 prescaler value

31457 00000000 0111101011100001
62914 00000000 1111010111000010
94371 00000001 0111000010100011
125828 00000001 1110101110000100
157285 00000010 0110011001100101
188742 00000010 1110000101000110

Aqui se ve que cambia de sample cada 2 ciclos de reloj

Si, por ejemplo, quiero generar 40 khz se queda en 31.25 khz. Solamente cuando pido algo como 60 khz queda en 60 y pico.

Puse dentro de las 256 muestas, 4 ciclos de senoidal y multiplique la resolucion para las cuentas por 4 = 1.9073486328125 Sin embargo los resultados son similares (eso si, de esta forma se pone mas exacto en frecuencias bajas, con la curiosidad de que si pido 1 khz pone 2 khz por ej. Siempre duplica).

Puede ser que le este errando, y que si pongo 4 ciclos de senoidal en 256 muestras, la resolucion tenga que dividirla por 4 en vez de multiplicarla? (es lo proximo que voy a probar).

¿Alguna otra idea de que puede pasar? No me resigno a que con 24 bits tenga esa poca precision, creo que deberia tener mas.

Adjunto una foto del bicho. Es placa doble faz (mi primera doble faz!!), del lado de abajo esta la SRAM smd y mucho ruteo.


Alguna idea de que me puede estar fallando en los calculos?
 

Adjuntos

  • siggen-hardware.pdf
    47.3 KB · Visitas: 20
  • siggen-hard1024.jpg
    siggen-hard1024.jpg
    94.3 KB · Visitas: 35
Atrás
Arriba