# [Aporte] Generador de Señales (DDS)



## cosmefulanito04 (Oct 26, 2013)

*ADVERTENCIA* muchas fotos.

Una de las herramientas que me faltaban en mi "tallercito" era un generador de señales, buscando en internet que proyecto interesante había, me tope con este:

AVR DDS signal generator V2.0 

Que a su vez se basó en este otro:

Mini DDS (Direct Digital Synthesis)

Ambos basados en la línea de uC AVR.

Resumiendo un poco, la idea es usar un puerto de un uC conectado a una red R-2R que funcione como DAC (en mi caso el puerto es de 8 pines => resultado DAC de 8 bits) y permitir generar una señal analógica en función de los datos almacenados en uC.

Evidentemente la velocidad del uC influirá y mucho en la frecuencia máxima a obtener e incluso el código tiene que ser muy a la medida para ahorrar el mayor tiempo posible en las rutinas de muestreo, por lo tanto por un lado es necesario trabajar con el uC al mayor clock posible y otro las rutinas de muestreo deberán realizarse si o si en Assembler para obtener el mejor desempeño posible.

Si se ponen a leer la última página que mencioné, ahí te dan el "truco" necesario para muestrear la señal almacenada en el uC mediante una rutina en assembler muy sencilla:

*Código de Jesper:*


```
; main loop
;
;    r28,r29,r30 is the phase accumulator
;      r24,r25,r26 is the adder value determining frequency
;
;     add value to accumulator
;    load byte from current table in ROM
;    output byte to port
;    repeat 
;
LOOP1:
        add        r28,r24        ; 1
        adc        r29,r25        ; 1
        adc        r30,r26        ; 1
        lpm                       ; 3
        out        PORTB,r0       ; 1
        rjmp       LOOP1          ; 2 => 9 cycles
```
Este será el corazón del generador. Si bien el código no parece decir demasiado, la traducción es la siguiente:


 El puntero "Z" está compuesto por el r31-r30, por lo tanto r30 será el byte inferior de dicho puntero, que se encargará de apuntar a 256 direcciones distintas.
 Mediante el uso de 2 registros más (r28 y r29) en conjunto con r30 se arma un acumulador de 24bits.
 En r24, r25 y r26 estará almacenado el paso del muestreo, por c/loop se r30 se irá incrementando mediante el paso, teniendo en cuenta el carry de los bytes inferiores,
 Mediante la instrucción "lpm" se lee la dirección donde se encuentran almacenados los datos y se lo guarda en r0. Para facilitar el muestreo es necesario que dicho "buffer" sea de 256 elementos, que justamente es la misma cantidad de direcciones que puede direccionar r30.
 Por último se almacena el valor de r0 en el puerto B (8 bits) que será el puerto conectado al DAC.
 Luego se repite el ciclo una y otra vez.

Como resultado, por c/loop es necesario 9 clk's. Entonces, sin tener en cuenta los acumuladores, la frecuencia máxima de muestreo daría Fosc/9.

Mediante el uso del acumulador (que funciona como un pre-escaler), podemos definir la resolución de muestreo, es decir con que frecuencia se muestrea al buffer de datos:

[LATEX]Resolucion=\frac{F_{osc}}{Acumulador-cuenta-total_{24bits}.9clk}=\frac{F_{osc}}{150994944}[/LATEX]

En base a la resolución, la frecuencia de salida estará dada por:

[LATEX]F_{out}=Acumulador_{cuenta-parcial}.Resolucion[/LATEX]

Si bien la solución resulta sencilla, les aseguro que a mi en un primer momento no se me ocurrió pensarlo de esa forma, lo cual insisto que esa parte del código resulta ser el corazón del proyecto. 

Como opción para reducir los clk's del ciclo loop para obtener un fsampling mayor, se puede reducir el acumador a 16bits, lo que a la larga implica menor resolución.

En mi caso, el código varía un poco, ya que pretendía hacer un control para que el usuario pudiera cambiar el tipo y la frecuencia de la señal, esto traía como inconvenientes agregar una salto condicional y por ende agregar más clk's por c/loop (menor frecuencia de muestreo), sin embargo el gran problema de la rutina de *Jesper* era que trabajaba directamente sobre la memoria de código del uC, haciendo 1clk más lento su lectura respecto a la memoria RAM (supongo que estaba limitado en RAM). En consecuencia, saque de un lado y agregué del otro, quedando en 9clk's al igual que su rutina.

*Mi código:*


```
.global generar_senial
    #define DATA_REG        r31
    #define ACUMULADOR_INF    r24
    #define ACUMULADOR_SUP    r25
    #define X_INF            r26
    #define PASO_BYTE_1        r19
    #define PASO_BYTE_2        r20
    #define PASO_BYTE_3        r22
    #define PORT_SALIDA        PORTC


    generar_senial:            
        movw    X_INF,r24        ;En r27:r26 => PUNTERO "X" => Puntero de la senial                                    
        mov        PASO_BYTE_1,r18    ;En r18 se encuentra el paso más bajo
        clr        DATA_REG        ;Lo uso para almacenar el dato momentaneo almacenado en el vector de la senial
        clr        ACUMULADOR_INF    ;Lo uso de acumulador del "Paso" byte inferior
        clr        ACUMULADOR_SUP    ;Lo uso de acumulador del "Paso" byte superior
        clr        FLAG_INT_REG    ;Limpio el flag de salida!
                                ;Valor del "Paso" de muestreo => en funcion de "f" y los clk's 
    
        loop_senial:
            add        ACUMULADOR_INF,PASO_BYTE_1            ;Sumo el 1er byte del "Paso"                                                                        => 1 Clk    +
            adc        ACUMULADOR_SUP,PASO_BYTE_2            ;Sumo el 2do byte del "Paso" teniendo en cuenta el carry producido por la suma del 1er acumulador    => 1 Clk    +
            adc        X_INF,PASO_BYTE_3                     ;Sumo el 3er byte del "Paso" teniendo en cuenta el carry producido por la suma del 2do acumulador    => 1 Clk    +
            ld         DATA_REG,X                            ;Obtengo el dato almacenado en RAM mediante el puntero "X" y lo llevo al registro 20                => 2 Clk    +    
            out        _SFR_IO_ADDR(PORT_SALIDA),DATA_REG    ;Modifico el valor del PuertoC en función del dato leído previamente                                => 1 Clk    +
            cpi        FLAG_INT_REG,1                        ;Usando el r18 como flag de interrupcion externa 0, comparo dicho registro con "1"                    => 1 Clk    +
            brne       loop_senial                           ;Si no es igual Z=0 (Flag Zero) => salta                                                            => 2 Clk    
                                                             ;                                                                                                    ------------                                                
            ret
```
*Lo más destacado:*


 Trabajo con el puntero X (r27:r26) sobre una posición de memoria RAM.
 Comparo un flag que será el registro r18, el cual se modificará en dos rutina de interrupción distintas, la externa y la del puerto serie.
 El resto se mantiene igual, salvo la inicialización que hago de los registros.

A diferencia de *Jesper*, yo combino el lenguaje Assembler para generar la señal y C para el resto de las funciones que no requieren tanta optimización.

Termine usando un cristal de 16MHz en un Atmega16, de esta forma consigo una fsampling=1,77MHz, permitiendo *idealmente* una frecuencia máxima de salida de 888kHz. Como el filtro pasa bajos no es ideal, esa frecuencia máxima de salida la fijé en 500kHz usando un filtro pasa-bajos de 4to orden lineal (mas detalles ver este hilo).

*Como resultado en base a las mediciones obtuve:*


 Señales:
 Senoidal.
 Triangular.
 Diente de sierra (pendiente "+" y "-")
 PWM.
 TTL
 
 Resolución de un 1Hz.
 Tonos puros hasta 400kHz (senoidal) [a discutir]. Hasta 20kHz, el THD resultaba despreciable, "medición" realizada con el FFT del OCR (hay que tomarlo con pinzas).
 Señales poliarmónicas limitadas a 100kHz (en 500kHz empieza actuar el filtro).
 En señales PWM, se obtiene una resolución del 1% en el duty.
 Señales TTL manejadas digitalmente, de 10kHz hasta 1,6MHz.
 4 Vp de salida [a discutir].
 La posibilidad de almacenar en la EEPROM, dos señales que el usuario necesite, mediante el uso del puerto serie [característica más que interesante para aumentar la funcionalidad del generador].
 Al igual que la opción anterior, enviar una señal en forma temporal (solo almacenada en RAM), mediante el puerto serie.
 Controlar el generador mediante el puerto serie, esto abre la posibilidad de hacer barridos en frecuencia y en duty [otra característica interesante para aumentar la funcionalidad del generador].
 Control del puerto serie desde PC mediante un programa hecho en Java.
*Fotos del armado del proyecto:*
- PCB sin soldar (Top layer):



- PCB sin soldar (Bottom layer):



- PCB a contra luz (Top layer):



- PCB a contra luz (Bottom layer):



- Soldaduras (Top layer):



- Soldaduras 1 (Bottom layer - lado de los componentes):



- Soldaduras 2 (Bottom layer - lado de los componentes):



- Soldaduras 3 (Bottom layer - lado de los componentes):



De momento al proyecto le falta un gabinete y terminar con el filtro de línea (no consigo el inductor de modo común ).

*Les dejo una fotos del generador funcionando:*





En un rato para no hacer demasiado largo el mensaje, subo en otro las distintas mediciones que fuí haciendo del generador. 

Luego voy a detallar bien el circuito, los inconvenientes que tengo de momento y por último subiré el proyecto completo, con los PCB, el código del uC y el programa de PC para hacer enviar una señal y controlar el generador mediante el puerto serie.


----------



## Fogonazo (Oct 26, 2013)

Tiene varios puntos que fluctúan entre "Muy Buenos" y "Excelentes", me gustó la posibilidad de almacenar una forma de onda es muy interesante.
El rango de frecuencias y señales cubre una enorme cantidad de posibilidades de uso.
Estamos hablando de prestaciones similares a las de un instrumento de unos *500U$*, y tal vez más.

(Parte Cholula) El armado me "Guta", te quedó "Remonono"

Gracias por compartirlo


----------



## cosmefulanito04 (Oct 26, 2013)

Fogonazo dijo:


> Tiene varios puntos que fluctúan entre "Muy Buenos" y "Excelentes", me gustó la posibilidad de almacenar una forma de onda es muy interesante.
> El rango de frecuencias y señales cubre una enorme cantidad de posibilidades de uso.
> Estamos hablando de prestaciones similares a las de un instrumento de unos *500U$*, y tal vez más.
> 
> ...



Gracias *Fogo*.

Si, la posibilidad de almacenar la forma de onda creo que es el "agregado" más destacado.
*Dejo las mediciones que mencioné:*
- Senoidal 1kHz:





- Senoidal 10kHz:



- Senoidal 20kHz:



- Senoidal 400kHz:



- Triangular 10kHz:



- Triangular 100kHz:



En azul la salida del DAC, en amarillo la salida luego del filtro.

- Diente de sierra 10kHz:



En azul la salida del DAC, en amarillo la salida luego del filtro.

- Diente de sierra 100kHz:



En azul la salida del DAC, en amarillo la salida luego del filtro. El filtro hace que la pendiente descendente sea más lenta y tenga un gran pico.

Las imágenes de las señales PWM son del soft anterior, luego de la modificación utilizando la señal TTL para generarlas, por tal motivos las imágenes viejas que tengo carecen de sentido.

A diferencia de las señales analógicas, las TTL se toman directamente desde un pin y su resolución es de un acumulador de 8bits (poca resolución). *La resolución en el modo TTL fue mejorado con la actualización del soft, permitiendo una resolución de 24 bits en bajas frecuencias.*

- TTL de casi 10kHz:



- TTL 1MHz:



- TTL 1,6MHz:



*Ahora los problemas:*

- La idea original era llegar a 500kHz, poniendo la frecuencia de corte del filtro cercana a esa frecuencia. Sin embargo a la hora de generar los 500kHz aparece el siguiente inconveniente:



No sé porque, .....

Sin embargo ese armónico de 277kHz ...del amplificador no inversor):


----------



## Fogonazo (Oct 26, 2013)

¿ Y agregar un filtro escalonado según la frecuencia ?


----------



## cosmefulanito04 (Oct 27, 2013)

Fogonazo dijo:


> ¿ Y agregar un filtro escalonado según la frecuencia ?



O sea, ¿ir tirando la frecuencia de corte más arriba según la señal?

*A continuación dejo el esquemático del circuito con su explicación según la etapa:*

*- Rectificación y filtrado:*

Ver el archivo adjunto 100502

Uso un transformador 6v+6v de 300mA, que luego de de rectificar obtengo casi +/-9v (un poco más de 8,5v). 

El capacitor principal de filtrado es de 1000uF, que para una corriente estimada de 50mA (uC + LCD) provocará un ripple de 500mV. 

En las mediciones el ripple obtenido fue de 280mV ramal positivo:

Ver el archivo adjunto 100503

Y 320mV entre ramal positivo y ramal negativo (lo que le llega de alimentación al operacional):

Ver el archivo adjunto 100504

C1 y C2 bis están para reforzar el filtrado de alta frecuencia y C1 analógico fue colocado ni bien comienza el plano de masa analógico.

Como mejora, se podría agregar capacitores en palalelo a c/diodo del puento para eliminar el ruido RF que se mete por la linea de alimentación y pueda mezclarse con los diodos.

*- Alimentación digital y filtrado según el fabricante del uC:*



Se alimenta con un 7805 con los capacitores que recomienda la hoja de datos. Mientras que el uC se alimenta a través de filtros L-C que recomienda el fabricante.

*- El uC Atmega16:*




 Conector P2 para programar el uC.
 Pull-up de 4k7Ohms para el pin del reset.
 Conexiones para un display LCD 2x16.
 Conexiones para el uso de la USART.
 Conexiones para los dos pulsadores del usuario conectados a las interrupciones externas.
 Conexiones para el manejo de dos leds que indicarán que salida está funcionando, si la analógica o la TTL.
 Salida TTL a un conector BNC para PCB.
 Conexión a un potenciómetro de cursor para el usuario.
 Los 8 bits que irán conectados a la red R-2R para generar la señal analógica.
 Conexiones de alimentación.
 C10, filtro recomendado por el fabricante para el ADC, solo utilizado para el cursor. 

*- Cristal y pulsador del reset:*



Para el oscilador usé un cristal de 16MHz, yo conseguí capacitores de 22pF (el fabricante recomienda 15pF hasta 22pF).

*- La conexión del LCD (usado en modo 4bits):*




 C11, capacitor de desacople.
 Backlight conectado mediante una resistencia de 47Ohms para obtener casi 17mA de corriente de polarización. No está controlado, para este proyecto no ví la necesidad de agregar un transistor para controlar su habilitación.
 R21 1kOhms y R20 no fue colocado, en esta configuración se consigue un excelente brillo.

*- Pulsadores para el usuario:*




 Pulsador /INT1 directo a masa, con una rutina de antirrebote por soft alcanza.
 Pulsador /INT0, requiere de un antirrebote por hard, ya que será el pulsador encargado de sacar al uC de la rutina en assembler que mencioné en el 1er mensaje.

El filtro del antirebote /INT0 lo pensé para un Tao de 100mS, bastante elevado para garantizar que esa interrupción externa solo salte cuando el usuario presiona el pulsador. Según la hoja de datos del uC, la entrada será tomada como nivel bajo cuando la tensión en el pin sea menor a 0,2.Vcc=1v (para 5v) y la resistencia de pull-up será de 50kOhms.

Como resultado, al presionar el pulsador, luego del transistorio en el pin debería haber 5v*10k/(10k+50k)=833mV, bastante al límite de ser tomado como un nivel lógico bajo.

En la práctica no tuve problema con eso, pero tal vez sea recomendable reducir el valor de R32 a 4k7 por ejemplo, dando un Tao de 47mS.

*- Potenciómetro (cursor) y leds:*



Los leds están polarizados con un poco menos de 10mA.

*- Conversor RS232 a TTL (Max232):*



Circuito tal como recomienda su hoja de datos.

*- Red R-2R (DAC):*



Para más detalles buscar en el foro que hay un buen tutorial sobre este tema. En mi caso terminé usando resistencias con tolerancias del 1%, de 1kOhms y 2kOhms. Valores muy chicos cargarían el puerto, valores muy grandes harían que el rise-time de lo pines sea demasiado lento.

*- Seguidor y pasa altos de 1er orden:*




 Filtros de 10uF cerca de la alimentación amplificador.
 El seguidor sirve para no cargar la red R-2R.
 El pasa altos con fc cercana en 16Hz, sirve para eliminar la continua (el offset).
 Amplificador no inversor con ganancia de 2,5 veces => se consigue casi 6,25 Vp.
 El control de amplitud se hace con un simple potenciómetro usado como atenuador.

*- Filtro pasa bajos de 4to orden lineal 0,5º:*




 Opté por un filtro que sea lineal para no generar tanta distorsión, pero en vez de ir por un filtro Bessel (el tipo de filtros más lineal), preferí uno que sea de +/-0,5º con mayor Q.
 Frecuencia de corte cercana a 530kHz, que varía según el operacional. Por ej. un TL071 tiene un ancho de banda menor a un TL081, por ende, este último conseguirá un polo dominante a mayor frecuencia que el primero.
 El control de offset se hace mediante otro potenciómetro conectado a la entrada "+" del último filtro para agregar un valor de continua. Mediante una llave se habilita o no ese offset, en caso de deshabilitarlo, la entrada "+" queda con un pull-down a GND.

Eso sería el circuito completo del proyecto, después subo un par de videos de como funciona el generador.


----------



## Fogonazo (Oct 27, 2013)

cosmefulanito04 dijo:


> O sea, ¿ir tirando la frecuencia de corte más arriba según la señal?. . .



Satamente.

Pero *NO* en forma continua sino por pasos.


----------



## cosmefulanito04 (Nov 8, 2013)

*Al generador le hice un par de modificaciones:*


 Hard:
 Agregué una resistencia de 680 Ohms en serie con el pote (del lado de la señal, para atenuar más) de amplitud para no llegar a la saturación. De esta forma la amplitud máxima queda cerca de 4,8Vp.
 Agregué una resistencia de 1 kOhms en serie con el pote del offset, cuando llegaba a -V, el offset se daba vuelta y se hacía positivo  . 

 Soft:
 Modifiqué la resolución en las frecuencias altas, reemplazando el acumulador de 24bits, por uno de 8 bits, esto me permite muestrear a 2,28MHz. Como resultado, mejoro la salida en 500kHz, pero sigo teniendo una armónica molesta a muy baja frecuencia.
 Descarté realizar el PWM con la salida analógica, en su lugar lo reemplacé por una señal cuadrada. No tenía sentido modificar el duty sin la componente de continua.
 Agregué una señal TTL-PWM, de esta forma variar el duty con la componente de continua toma mayor sentido. Por otro lado, esta señal la limito a 100kHz, debido a que depende de un preescaler y la resolución de duty en alta frecuencia no es taaan bueno (ya van a ver).


*Por otro lado hice un barrido en frecuencia (por octavas) a máxima amplitud, midiendo solo la salida (es decir, no hice Vout/Vin, ya que no me interesaba saber la transferencia del filtro) obtuve la siguiente curva en dB:*




 Cerca de 15Hz se encuentra la frecuencia de corte inferior. 
 A partir de 200Hz la respuesta es de 0dB.
 En 100kHz se empieza a notar un aumento en la señal.
 En 200kHz se alcanza ese pico de 0,3dB (muy poco).
 Cerca de 475kHz se alcanza los -3dB, frecuencia de corte superior.

*Acá una previa de como está quedando el generador con el gabinete:*



*Elementos del gabinete:*


 Pulsador Rojo: sirve para desplazarse en el menú y salir de la señal actual que se esté generando.
 Pulsador Negro: funciona como "Ok".
 Potenciómetro inferior izquierdo: amplitud.
 Potenciómetro inferior derecho: offset.
 Potenciómetro superior: cursor para el menú.
 LLave: para habilitar el offset.
 LEDs: indican que salida tiene señal.
 BNC izquierdo: salida TTL.
 BNC derecho: salida analógica.

*Videos:*

*- Señales analógicas*






*- Señales TTL*






*- Enviar señal a EEPROM*






*- Control vía puerto serie usando señales analógicas - Barrido en frecuencia*






*- Control vía puerto serie usando señales TTL - Barrido en duty*






Mañana subo el proyecto completo.


----------



## cosmefulanito04 (Nov 9, 2013)

*Subo los siguientes archivos:*


 *Generador de Señal - Proyecto Altium:* se encuentra el esquemático y el PCB del generador.
 *Filtro de línea - Proyecto Altium:* se encuentra el esquemático y el PCB del filtro de línea.
 *Firmware:* todos los archivos fuente del software para el uC (tanto C como Assembler), junto con el ejecutable ".hex".
 *Generador de Señal - Java:* el ejecutable Jar para controlar el generador desde la Pc. También incluye la librería RxTx para 32/64 bits que deberán ser instalados en el directorio Java Jre de la Pc.
 *Señales:* incluyo un par de señales y una función para Matlab que sirve para pasar un vector a un archivo ".txt".

Cualquier duda me avisan.


----------



## Xapas (Ene 26, 2014)

Hola, buenos dias!
Hace un par de semanas vi este estupendo post, que por cierto, gracias a cosmefulanito04 por compartirlo, me decidi a hacer uno parecido, ya que estoy armando un amplificador y me vendria genial tener un generador de ondas para probarlo.
Pues bien, me propuse diseñarlo, y he conseguido hacer una especie de menú. Tengo que resaltar que estoy trabajando con PIC, no con AVR, asi que cambian todas las instrucciones. También estoy haciendo el programa entero en ensamblador, ya que hace poco aprendi a programar en este código.
Bien, como dijo el compañero cosmefulanito, la información fue sacada de otra página, que también consulté. Estuve viendo el código máquina, y por lo visto existe una tabla de valores preconfigurados que permiten la salida de la señal, ya sea senoidal, cuadrada, etc. Sin embargo, no consigo entender cómo actúa el acumulador de fase y cómo se determina la frecuencia  Según he supuesto, los 3 registros que suman 24 bits determinan qué valor de la tabla debe salir por el puerto, de forma que a la siguiente vuelta del bucle cogen al siguiente, pero no consigo saber cómo. :S Les adjunto mi programa y mi diseño en proteus (lo que llevo hasta ahora):
Si alguien pudiera explicarme exactamente cómo funciona se lo agradecería 


```
;Configuración de los puertos
			bcf		03h,6		;Seleccionamos Banco 1
			bsf		03h,5		;Seleccionamos Banco 1
			movlw	00h			;Colocamos valor '0' en el registro W
			movwf	85h			;Configuramos Puerto A como salida
			movwf	86h			;Configuramos Puerto B como salida
			movlw	b'11110000'
			movwf	87h
			bcf		83h,5		;Seleccionamos Banco 0
					
;Configurar LCD
			clrf	05h				;Limpiamos Puerto A
			clrf	06h				;Limpiamos Puerto B
			clrf	07h
			call 	Retardo1s		;Llamamos a Retardo para iniciar correctamente el LCD
			movlw	b'00001100'		;Pantalla encendida, cursor apagado, intermitencia apagada
			call	Instruccion		;Llamamos a Instruccion para enviar una instrucción al LCD
			movlw	b'00000001'		;Borramos pantalla y colocamos cursor al inicio
			call 	Instruccion		;Llamamos a Instruccion para enviar una instrucción al LCD
			movlw	b'00111000'		;Bus 8 bits, activación de dos líneas de texto, matriz de caracteres de 5x7
			call	Instruccion		;Llamamos a Instruccion para enviar una instrucción al LCD
			movlw	b'10000000'		;Colocación al inicio de la primera línea
			call	Instruccion		;Llamamos a Instruccion para enviar una instrucción al LCD
			movlw	b'00000110'		;Incrementa posición del cursor, no desplaza texto
			call	Instruccion		;Llamamos a Instruccion para enviar una instrucción al LCD

;Mensaje de inicio del LCD			
			movlw	' '				;Colocamos caracter ' ' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'G'				;Colocamos caracter 'G' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'e'				;Colocamos caracter 'e' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'n'				;Colocamos caracter 'n' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'e'				;Colocamos caracter 'e' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'r'				;Colocamos caracter 'r' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'a'				;Colocamos caracter 'a' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'd'				;Colocamos caracter 'd' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'o'				;Colocamos caracter 'o' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'r'				;Colocamos caracter 'r' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	' '				;Colocamos caracter ' ' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	' '				;Colocamos caracter ' ' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'D'				;Colocamos caracter 'D' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'D'				;Colocamos caracter 'D' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'S'				;Colocamos caracter 'S' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	' '				;Colocamos caracter ' ' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	b'11000000'		;Colocación al inicio de la segunda línea
			call	Instruccion		;Llamamos a Instruccion para enviar una instrucción al LCD
			movlw	' '				;Colocamos caracter ' ' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	' '				;Colocamos caracter ' ' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'I'				;Colocamos caracter 'I' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'n'				;Colocamos caracter 'n' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'i'				;Colocamos caracter 'i' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'c'				;Colocamos caracter 'c' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'i'				;Colocamos caracter 'i' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'a'				;Colocamos caracter 'a' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'n'				;Colocamos caracter 'n' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'd'				;Colocamos caracter 'd' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'o'				;Colocamos caracter 'o' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'.'				;Colocamos caracter '.' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'.'				;Colocamos caracter '.' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'.'				;Colocamos caracter '.' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	' '				;Colocamos caracter ' ' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	' '				;Colocamos caracter ' ' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			call	Retardo1s
			call	Retardo1s
			
Senoidal	movlw	0xC0
			call	Instruccion
			movlw	'F'				;Colocamos caracter 'F' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'u'				;Colocamos caracter 'u' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'n'				;Colocamos caracter 'n' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'c'				;Colocamos caracter 'c' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'i'				;Colocamos caracter 'i' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'o'				;Colocamos caracter 'o' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'n'				;Colocamos caracter 'n' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	' '				;Colocamos caracter ' ' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'S'				;Colocamos caracter 'S' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'e'				;Colocamos caracter 'e' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'n'				;Colocamos caracter 'n' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'o'				;Colocamos caracter 'o' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'i'				;Colocamos caracter 'i' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'd'				;Colocamos caracter 'd' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'a'				;Colocamos caracter 'a' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'l'				;Colocamos caracter 'l' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
SSenoidal	movlw	09h
			movwf	07h
			btfsc	07h,4
			goto	ARS1
			btfsc	07h,5
			goto	ARS2
			btfsc	07h,7
			goto	ARS3
			goto 	SSenoidal

ARS1		btfss	07h,4
			goto	Sierra
			goto	ARS1
ARS2		btfss	07h,5
			goto	Cuadrada
			goto	ARS2
ARS3		btfss	07h,7
			goto 	Senoidal
			goto	ARS3
			return	
		
Cuadrada	movlw	0xC0	
			call	Instruccion
			movlw	'F'				;Colocamos caracter 'F' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'u'				;Colocamos caracter 'u' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'n'				;Colocamos caracter 'n' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'c'				;Colocamos caracter 'c' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'i'				;Colocamos caracter 'i' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'o'				;Colocamos caracter 'o' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'n'				;Colocamos caracter 'n' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	' '				;Colocamos caracter ' ' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'C'				;Colocamos caracter 'C' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'u'				;Colocamos caracter 'u' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'a'				;Colocamos caracter 'a' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'd'				;Colocamos caracter 'd' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'r'				;Colocamos caracter 'r' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'a'				;Colocamos caracter 'a' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'd'				;Colocamos caracter 'd' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'a'				;Colocamos caracter 'a' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
SCuadrada	movlw	09h
			movwf	07h
			btfsc	07h,4
			goto	ARC1
			btfsc	07h,5
			goto	ARC2
			btfsc	07h,7
			goto	ARC3
			goto 	SCuadrada

ARC1		btfss	07h,4
			goto	Senoidal
			goto	ARC1
ARC2		btfss	07h,5
			goto	Triangular
			goto	ARC2
ARC3		btfss	07h,7
			goto 	Cuadrada
			goto	ARC3
			return	
			
Triangular	movlw	0xC0
			call	Instruccion
			movlw	'F'				;Colocamos caracter 'F' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'u'				;Colocamos caracter 'u' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'n'				;Colocamos caracter 'n' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'c'				;Colocamos caracter 'c' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'.'				;Colocamos caracter '.' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	' '				;Colocamos caracter ' ' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'T'				;Colocamos caracter 'T' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'r'				;Colocamos caracter 'r' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'i'				;Colocamos caracter 'i' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'a'				;Colocamos caracter 'a' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'n'				;Colocamos caracter 'n' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'g'				;Colocamos caracter 'g' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'u'				;Colocamos caracter 'u' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'l'				;Colocamos caracter 'l' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'a'				;Colocamos caracter 'a' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'r'				;Colocamos caracter 'r' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
STriangular	movlw	09h
			movwf	07h
			btfsc	07h,4
			goto	ART1
			btfsc	07h,5
			goto	ART2
			btfsc	07h,7
			goto	ART3
			goto 	STriangular

ART1		btfss	07h,4
			goto	Cuadrada
			goto	ART1
ART2		btfss	07h,5
			goto	Sierra
			goto	ART2
ART3		btfss	07h,7
			goto 	Triangular
			goto	ART3
			return	
			
Sierra		movlw	0xC0
			call	Instruccion
			movlw	' '				;Colocamos caracter ' ' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'F'				;Colocamos caracter 'F' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'u'				;Colocamos caracter 'u' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'n'				;Colocamos caracter 'n' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'c'				;Colocamos caracter 'c' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'i'				;Colocamos caracter 'i' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'o'				;Colocamos caracter 'o' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'n'				;Colocamos caracter 'n' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	' '				;Colocamos caracter ' ' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'S'				;Colocamos caracter 'S' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'i'				;Colocamos caracter 'i' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'e'				;Colocamos caracter 'e' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'r'				;Colocamos caracter 'r' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'r'				;Colocamos caracter 'r' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	'a'				;Colocamos caracter 'a' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
			movlw	' '				;Colocamos caracter ' ' en registro W
			call	Caracter		;Llamamos a Caracter para enviar un caracter al LCD
SSierra		movlw	09h
			movwf	07h
			btfsc	07h,4
			goto	ARSAW1
			btfsc	07h,5
			goto	ARSAW2
			btfsc	07h,7
			goto	ARSAW3
			goto 	SSierra

ARSAW1		btfss	07h,4
			goto	Triangular
			goto	ARSAW1
ARSAW2		btfss	07h,5
			goto	Senoidal
			goto	ARSAW2
ARSAW3		btfss	07h,7
			goto 	Sierra
			goto 	ARSAW3
			return	
			
;Introducir caracter
Caracter	movwf	06h
			movlw	03h
			movwf	05h
			call	RetardoLCD
			movlw	01h
			movwf	05h
			call	RetardoLCD
			return

;Introducir instrucción
Instruccion	movwf	06h
			movlw	02h
			movwf	05h
			call 	RetardoLCD
			movlw	00h
			movwf	05h
			call	RetardoLCD
			return
		
;Retardo para LCD de 1ms
RetardoLCD	movlw	87h
			movwf	20h
DelayLCD1	movlw	0Bh
			movwf	21h
DelayLCD2	decfsz	21h,1
			goto	DelayLCD2
			decfsz	20h,1
			goto	DelayLCD1
			return
			
;Retardo general 1s
Retardo1s	movlw	0xA5
			movwf	20h
Delay1		movlw	0x29
			movwf	21h
Delay2		movlw	0xF5
			movwf	22h
Delay3		decfsz	22h,1
			goto	Delay3
			decfsz	21h,1
			goto	Delay2
			decfsz	20h
			goto	Delay1
			return		
			
			end
```


----------



## cosmefulanito04 (Ene 26, 2014)

El acumulador de 24 bits sirve para fijar el paso de muestreo que se le dará a la señal y funciona como un preescaler variable. 

¿Por qué es de 24bits? 

Debido a que mientras mayor sea el tamaño del acumulador, mayor resolución en el paso de muestreo que vas a tener, esto muy importante para poder fijar el "deltaF" mínimo.

Entonces de esos 24 bits, solo te quedás con los últimos 8 bits más significativos y los usás como índice para el vector de la señal. El truco está en que ese vector de datos que tendrá la señal almacenada sea de 256 elementos (muestras) para que de esta forma con 8 bits puedas fácilmente esos elementos y cuando haya desborde en el índice (es decir el acumulador de 24bits desborde), vuelva al primer elemento (o muestra) del vector de datos. Fijate que de esta forma armas una cola circular de datos que siempre se repite al paso de la frecuencia que vos elegistes.

Para que lo termines de entender, volviendo al código que subí, al uC le toma 9clks en completar un bucle para actualizar el valor de la señal (eso mismo vas a tener que analizar vos con el PIC y la rutina en assembler que hagas) y como base de tiempo usé un cristal de 16MHz, entonces mi resolución queda definida por:

[LATEX]Resolucion=\frac{F_{osc}}{Tamanio_acumulador.9}= \frac{16MHz}{2^{24}.9}=0,105.... Hz[/LATEX]

Es decir que soy capaz de imponerle al generador una frecuencia de por ej. 99,92 Hz aproximadamente. 

Si en cambio el acumulador solo fuera de 16bits, la resolución pasa a ser de:

[LATEX]Resolucion=\frac{F_{osc}}{Tamanio-acumulador.9}= \frac{16MHz}{2^{16}.9}=27,12 Hz[/LATEX]

Esto quiere decir que no tendría forma de fijar 100Hz clavados y como mucho me acercaría a los 108Hz.

Volviendo al acumulador de 24bits, si deseara obtener 100Hz, dicho acumulador debería valer:

[LATEX]Cuenta acumulador=\frac{F_{salida}}{Resolucion}= \frac{100Hz}{0,105.... Hz}=943[/LATEX]

Esto significa que c/vez que el bucle complete una vuelta, al acumulador se le sumarán 943 cuentas y siempre la salida del generador estará dado por el vector de datos que índica los últimos 8 bits más significativos de ese acumulador.

Por último recién vas a notar un cambio en el índice después de completar 70 vueltas al bucle (momento en que se le suma uno a los 8 bits más significativo del acumulador, 66010).


----------



## seaarg (Ene 27, 2014)

Cosme, muchas gracias por tu aporte. Yo estaba haciendo algo similar, solo que con una memoria ram para almacenar las formas de onda (ram por la velocidad, sino usaria una eprom), en cuya salida tengo la escalera R2R y un PLD que hace de contador y prescaler a partir de un cristal de 32mhz.

Todo ese lio para poder obtener la mayor frecuencia de salida posible y asi no depender de la velocidad del micro, que incluso a 48mhz se me hacia muy lenta por la cantidad de instrucciones (no recuerdo pero creo que lograba una frecuencia maxima de 200khz o algo asi, encima con pocos samples, 8) al final, con este diseño habia logrado frecuencias de algunos mhz, solamente que no graduales sino con saltos, estilo Fosc / 1, Fosc / 2, Fosc / 4 etc.

Sin embargo, voy a estudiar tu desarrollo a ver si se puede implementar lo mismo en un PIC y que limitaciones me encuentro.

Para el filtro de salida variable, en mi diseño intercambiaba 8 distintos capacitores con un 74HC4051 y selecciono cual de ellos dependiendo de la frecuencia que estoy generando. En simulacion anda muy bien, aclaro que no lo probe en la practica pero puede ser una idea que te sirva.


----------



## cosmefulanito04 (Ene 27, 2014)

seaarg dijo:


> Cosme, muchas gracias por tu aporte. Yo estaba haciendo algo similar, solo que con una memoria ram para almacenar las formas de onda (ram por la velocidad, sino usaria una eprom), en cuya salida tengo la escalera R2R y un PLD que hace de contador y prescaler a partir de un cristal de 32mhz.



Claro yo también decidí usar la RAM del uC, en forma permanente se encuentran en la flash/eeprom, pero una vez que se elige la señal a generar se pasa dicha señal a la RAM y se trabaja directamente desde ella, de esta forma ahorrás 1clk en los AVR.



seaarg dijo:


> Todo ese lio para poder obtener la mayor frecuencia de salida posible y asi no depender de la velocidad del micro, que incluso a 48mhz se me hacia muy lenta por la cantidad de instrucciones (no recuerdo pero creo que lograba una frecuencia maxima de 200khz o algo asi, encima con pocos samples, 8) al final, con este diseño habia logrado frecuencias de algunos mhz, solamente que no graduales sino con saltos, estilo Fosc / 1, Fosc / 2, Fosc / 4 etc.



Te recomiendo que leas la rutina de assembler que publiqué o ponete con *Xapas* para desarrollar esa parte que utiliza el acumulador de 24 bits y generar una resolución bastante alta. 

La rutina es muuy simple y universal, es decir la podés emplear con cualquier uC. 

Sobre la velocidad del uC, es justamente la base de tiempo que tenés que tomar y resulta vital, por eso en la generación el código debe estar hecho en assembler, el resto por ej. podés hacerlo en C, como hice yo.

Para darte una idea, con 48MHz y una rutina en assembler similar a la mía que requiere de 9clks, implica una fsampling máxima de 5,3MHz. Si armás bien el filtro, hasta 2MHz podrías llegar.



seaarg dijo:


> Para el filtro de salida variable, en mi diseño intercambiaba 8 distintos capacitores con un *74HC4051* y selecciono cual de ellos dependiendo de la frecuencia que estoy generando. En simulacion anda muy bien, aclaro que no lo probe en la practica pero puede ser una idea que te sirva.ccc



Ojo que te puede meter mucha distorsión a la salida. A lo sumo creo que es preferible hacerlo con una llave selectora con manejo manual.


----------



## seaarg (Ene 27, 2014)

cosme,

Me referia a una ram externa, para poder tener cada sample en la salida a una F maxima de 32mhz (dividido la cantidad de samples, que creo que use 8 ahora no recuerdo)

Algo asi: 


En la captura no se ven los codigos de componentes pero, de izq a derecha son:
PIC16F873, GAL22v10, y una RAM. Lo de abajo al medio es el 4051

De todos modos, mi anterior comentario era para darte la idea del 4051 pero si, puede que tenga distorsion y sea mas factible un selector manual.

Voy a estudiar tus rutinas porque evidentemente tu sistema es mas simple y efectivo.


----------



## seaarg (Ene 28, 2014)

Aca se ve una diferencia de potencia interesante entre avr y pic, ya que al pic le toma 4 ciclos de reloj cada instruccion, en cambio al avr cada ciclo de reloj = 1 instruccion.

Este es el motivo principal por el cual deje de hacer la sintesis por medio del pic, ya que toma demasiados ciclos de reloj hacer un acumulador, buscar en la tabla de lookup y transferir al puerto (solo la transferencia al puerto ocupa 2 instrucciones, o sea 8 ciclos de reloj!)

Es super interesante el modo de trabajar con ese acumulador, aunque no termino de entenderlo: Para preguntarte voy a simplificarlo a 16 bits:

supongamos que tengo una variable "acum" de esta forma:

H 00000000 L 00000000

y un vector en memoria de 256 posiciones con la señal almacenada

ahora bien, si yo hago:

acum++;

y uso el byte alto como puntero para buscar en la tabla de lookup, esto significaria que cada 256 ciclos, tendria 1 salto en el vector.

Esto actuaria como un divisor de frecuencia bastante grande.

Si acumulo de esta forma:

acum = acum + 256

Esto haria que en cada ciclo, el puntero cambia de "sample", dandome la F maxima en la salida del generador.

¿Asi es mas o menos como funciona tu rutina? (desconozco codigo AVR y se me complica interpretarla bien)

Si fuera asi, entonces tu rutina seria algo como (pseudocodigo):

while(not(interrupcionteclado)) {
  acum = acum + divisor_8_bits;
  puntero = byte_alto_de(acum);
  out = vector[puntero];
  escribirpuerto(out);
}

¿Esta mas o menos interpretado o entendi cualquier cosa?

Desde ya, muchas gracias, me interesa mucho ver los limites de esta posibilidad, ya que ese pseudocodigo, si lo hago en ASM debe ocupar un minimo de 16 instrucciones (y creo que me quedo muy corto), o sea 64 ciclos de reloj, que si fuera de 48mhz seria: 0.75mhz, los que en la practica seguro que se reducen a la mitad o menos


----------



## cosmefulanito04 (Ene 28, 2014)

seaarg dijo:


> ...
> 
> Es super interesante el modo de trabajar con ese acumulador, aunque no termino de entenderlo: Para preguntarte voy a simplificarlo a 16 bits:
> 
> ...



Exacto, así funciona el acumulador, es simplemente un preescaler variable que estará fijado por el paso que le des.



seaarg dijo:


> ...Si fuera asi, entonces tu rutina seria algo como (pseudocodigo):
> 
> while(not(interrupcionteclado)) {
> acum = acum + divisor_8_bits;
> ...



Si con la diferencia que el divisor_8_bits será el paso y no será de 8bits sino de 16 bits.



seaarg dijo:


> Desde ya, muchas gracias, me interesa mucho ver los limites de esta posibilidad, ya que ese pseudocodigo, si lo hago en ASM debe ocupar un minimo de 16 instrucciones (y creo que me quedo muy corto), o sea 64 ciclos de reloj, que si fuera de 48mhz seria: 0.75mhz, los que en la practica seguro que se reducen a la mitad o menos



¿16 instrucciones dentro del bucle? es demasiado.

Yo conseguí 7 instrucciones, en tu caso supongamos que serán 28 clk's dandote un fsampling de 1,7MHz valor similar al mío . 

Hacé una cosa, fijate si podés encontrar el set de instrucciones del PIC que estas usando con los clk's detallados y yo te ayudo a traducir esa rutina.

Por otro lado, ya que sabes C, te recomiendo que busques como podés pasarle por argumentos a una función hecha en assembler desde C, que es lo que terminé haciendo yo, de esa forma el resto del código (menu, LCD, puerto serie o lo que sea, etc) lo hacés fácilmente en C.


----------



## seaarg (Ene 28, 2014)

Perfecto entonces, ya sabiendo como funciona puedo empezar a hacer algunas pruebitas.

Usando CCS C es trivial usar variables declaradas "en C" desde bloques #ASM, vamos a ver si consigo reducir la cantidad de instrucciones (fue un estimativo rapido nomas) No tengo ningun drama con assembler, e incluso CCS C te genera un assembler donde se puede revisar para optimizaciones.

Voy a hacer un borrador basado en PIC 18F2550 que es lo mas "pulenta" que hay en la gama que puede programar mi pickit. Si hay un interes se puede hacer una version de lo tuyo (que esta excelente) basada en PIC para los que, como yo, nunca tocaron otro micro 

Muchas gracias!


----------



## cosmefulanito04 (Ene 28, 2014)

seaarg dijo:


> Perfecto entonces, ya sabiendo como funciona puedo empezar a hacer algunas pruebitas.
> 
> Usando CCS C es trivial usar variables declaradas "en C" desde bloques #ASM, vamos a ver si consigo reducir la cantidad de instrucciones (fue un estimativo rapido nomas) No tengo ningun drama con assembler, e incluso CCS C te genera un assembler donde se puede revisar para optimizaciones.



No se como será en ese caso, pero trata de asegurarte de no usar RAM sino un registro de datos para que la cantidad de clk's sea menor. En AVR trabajar sobre RAM implican 2 clk's, en cambio sobre un registro de uso genérico solo 1 clk.

Otra cosa que se me pasó, seguramente ese PIC para que trabaje en 48MHz requiere usar el PLL, hay que ver que tan estable es ese clock, ya que en un PLL hay una zona llamada de captura en la cual sigue habiendo una cierta variación, ejemplo:







Tal vez esa variación te afecte a la salida y obtenés una pequeña variación, será cuestión de hacer la prueba y ver que pasa.


----------



## seaarg (Ene 29, 2014)

Interesante lo que explicas del PLL, jamas lo hubiera pensado porque se usa para USB (sea cual sea el cristal externo, internamente lo divide para llevarlo a 4mhz y desde 4mhz lo levanta hasta 96mhz y de ahi deriva para la CPU y USB a 48mhz)

Entiendo que el USB no es muy tolerante a derivas pero no estoy seguro.

Sobre lo de no usar RAM, supongo que por "registro de datos" te referis a una tabla de lookup en memoria de programa? Comento que hice 3 pruebas distintas (todavia a 16 bits):

Lo siguiente toma 2.75us x sample:


```
// Esto es una tabla en la ROM de programa, accesible con la tecnica de hacer saltar el program counter
const unsigned int8 sine[256] = {
   128,131,134,137,140,143,146,149,152,155,
   158,162,165,167,170,173,176,179,182,185, etc.etcetc }

   while(TRUE) {
      prescaler = prescaler + paso;
      
      // Hacer obtencion de HIbyte + lookup + salida a puerto en una sola linea hace que el compilador optimize mejor, ahorrando 2 instrucciones.
      output_b(sine[prescaler >> 8]);
   }
```

Otra forma (en RAM) toma 1.75us x sample:


```
// Esto es un vector en RAM, precargado con los valores de la tabla ROM
unsigned int8  vector_senal[256];

   while(TRUE) {
      prescaler = prescaler + paso;
      i = prescaler >> 8;
      output_b(vector_senal[i]);
   }
```

Y por ultimo, esta forma toma 1.5833us x sample (ahorra 2 instrucciones = 8 clk, cada instruccion son 83.33ns excepto saltos y ciertos MOV)


```
// Esto es un vector en RAM, precargado con los valores de la tabla ROM
unsigned int8  vector_senal[256];

   while(TRUE) {
      prescaler = prescaler + paso;
      
      // Hacer obtencion de HIbyte + obtencion dato vector + salida a puerto en una sola linea hace que el compilador optimize mejor, ahorrando 2 instrucciones.
      output_b(vector_senal[prescaler >> 8]);
   }
```

Por lo tanto, en C "puro" hasta ahora logre 1.5833us x sample, tengo que ver si puedo ajustar mas eso mediante ASM, pero por lo que vi en el assembler generado por el compilador ya es muy optimizado... quiza cambiando de forma de hacer la rutina pueda optimizar mas (se me va a poner mucho mas lento si quiero llevarlo a 24bits)

Aceptando esta limitacion de velocidad, quiza pudiera aumentar la frecuencia maxima que se puede generar haciendo que, en vez de tener un ciclo de senoidal en 256 samples, tenga 2 o 4 o quiza hasta 8 en esa cantidad. De esta forma tendria algo parecido a mi generador de señal original (que era una sintesis de 16 u 8 samples solamente para todos los tipos de señal) y con esto, esos 1.5833us se pueden llevar a /2 /4 o /8. ¿Que opinan?

Otra pregunta: En tu diseño para el OPAMP usas fuente partida. Yo pensaba usar un capacitor de desacople y usar fuente simple. (veo que vos tenes C21-BIS) ¿Hay alguna razon importante que no este pensando para usar fuente partida? Descartando amplitud de señal, ya que no seria impedimento usar fuente simple de, digamos, 15v para tener 10vpp por dar un ejemplo. Esto te lo pregunto por curioso nomas, ya que no cuesta nada poner 2 baterias de 9v en serie con punto medio y listo (si se quiere evitar el trafo, que tampoco tendria mucho sentido evitarlo)


----------



## cosmefulanito04 (Ene 29, 2014)

seaarg dijo:


> Sobre lo de no usar RAM, supongo que por "registro de datos" te referis a una tabla de lookup en memoria de programa?



No, hablo de los registros que poseen los uC en sus ALU para trabajar más rápido los datos, por ej. en un uP 8086 esos registros se llaman ax,bx,cx,dx, etc (eso en 16 bits, en 32 bits es eax).

Por lo que ví muy por arriba, en la familia PIC18, esos registros no se encuentran en la ALU, sino que están en la RAM y son varios. Sin embargo hay un registro que si se encuentra en la ALU y podés manipular que es el WREG y tal vez usando ese registro como indice te podés ahorrar un par de clk's. 



seaarg dijo:


> Comento que hice 3 pruebas distintas (todavia a 16 bits):
> 
> Lo siguiente toma 2.75us x sample:
> 
> ...



No está tan mal, pero fijate que en assembler y usando un acumulador de 24 bits yo conseguí un fsampling de 1,77MHz (o sea 562 nS). 

Si bien los compiladores de C te tratan de optimizar el assembler, yo creo que lo mejor es remangarse y meter uno mismo mano en assembler.



seaarg dijo:


> Aceptando esta limitacion de velocidad, quiza pudiera aumentar la frecuencia maxima que se puede generar haciendo que, en vez de tener un ciclo de senoidal en 256 samples, tenga 2 o 4 o quiza hasta 8 en esa cantidad. De esta forma tendria algo parecido a mi generador de señal original (que era una sintesis de 16 u 8 samples solamente para todos los tipos de señal) y con esto, esos 1.5833us se pueden llevar a /2 /4 o /8. ¿Que opinan?



Con el uso del acumulador no tenés que pensar en la cantidad de muestras que tenés almacenadas (bah en realidad si, 256 sería lo ideal por lo que ya mencioné, justo es la cantidad de datos que pueden direccionar 8 bits). Pero debería ser exactamente lo mismo tener 128 muestras que 256, ya que es el acumulador en encargarse de barrer dichas muestras, para que se entienda cuando trabajas a fsampling (máxima frecuencia) de las 256 muestras, solo tomás "1", por eso mi generador está limitado a 500kHz, ya que en esa frecuencia solo tengo 3 muestras y por eso requiero de bruto filtro.

Aunque a la larga decidí hacer una pequeña modificación en el programa, y para frecuencias mayores a 300kHz usar un acumulador de solo 8bits para obtener un fsamplig de 2,28MHz obteniendo así 4 muestras en 500kHz.



seaarg dijo:


> Otra pregunta: En tu diseño para el OPAMP usas fuente partida. Yo pensaba usar un capacitor de desacople y usar fuente simple. (veo que vos tenes C21-BIS) ¿Hay alguna razon importante que no este pensando para usar fuente partida? Descartando amplitud de señal, ya que no seria impedimento usar fuente simple de, digamos, 15v para tener 10vpp por dar un ejemplo. Esto te lo pregunto por curioso nomas, ya que no cuesta nada poner 2 baterias de 9v en serie con punto medio y listo (si se quiere evitar el trafo, que tampoco tendria mucho sentido evitarlo)



Ninguna, simplemente tenía un trafo de 6+6 sin uso y decidí utilizarlo para este proyecto.


----------



## seaarg (Ene 31, 2014)

Bueno, optimizando al maximo y usando el pic a 48mhz (serian mas o menos como tu AVR a 12mhz y aun menos optimo por el tipo de instrucciones menos avanzadas que tiene)

Es una mezcla de C con assembler y va asi:


```
while(TRUE) {
      #ASM ASIS
// Lo siguiente hace: prescaler24bits = prescaler24bits + paso16bits
      MOVF   paso_lo,W
      ADDWF  prescaler_lo,F
      MOVF   paso_hi,W
      ADDWFC prescaler_mi,F
      MOVLW  00
      ADDWFC prescaler_hi,F
//   En este punto, tengo en prescaler_hi la posicion del vector que tengo que sacar por el puerto.
//   Lo siguiente es comparativo a: output_b(vector_senal[prescaler_hi]); pero un poquito mas optimizado
      MOVF   prescaler_hi,W   //MOVF   prescaler_hi,W
      ADDLW  vector_senal     //ADDLW  vector_senal
      MOVWF  0xFE9              //MOVWF  FSR0L
      MOVLW  00               //MOVLW  vector_senal+-9
      ADDWFC 0x03,W             //ADDWFC @03,W
      MOVWF  0xFEA              //MOVWF  FSR0H
      MOVFF  0xFEF,0x109          //MOVFF  INDF0,@@109
      MOVFF  0x109,0xF8A          //MOVFF  @@109,LATB
      #ENDASM
   }
```

Todas esas instrucciones usan 1 ciclo de micro (4 ciclos de cristal) excepto las MOVFF y el salto del loop que usan 2 ciclos de micro, totalizando 18 ciclos o 1.5us x sample y esto es lo MINIMO que logre a 24bits. Si el prescaler lo hago de 16bits me ahorro tan solo 2 ciclos del micro, o sea, 1.33us por sample.

Incluso pensando en overclockear el pic a 64mhz (que seria algo mas cercano a tu AVR) el bucle toma 1.125us y encima perderia comunicacion usb con la PC (aunque aun se podria mantener una comunicacion RS232 quiza)

He probado con proteus de que, usando la escalera R2R y solo 1 capacitor de 1nf como filtro, necesito 8 muestras de una senoidal de 256 muestras para poder representar una senoidal con forma en el osciloscopio. Ya con 4 puntos se ve muy triangular. Si no me equivoco esto vos lo solucionaste con un filtro muy bien diseñado.

Aun si usara 4 muestras, a 1.5us por muestra, tengo una senoidal de 166khz como maximo.

Entonces, me puse a pensar que pasaria si voy por una solucion hardware, de la siguiente forma:

- Un reloj base de alta frecuencia, no se, 80mhz
- Un micro chico, cuya unica funcion sera manejar interfaz y transferir una forma de onda a una SRAM externa
- La misma red R2R y filtro aplicados a esa SRAM
- Y (aca viene la clave) una serie de contadores que hagan por hardware exactamente el mismo prescaler que estoy intentando hacer por soft.

Si pudiera hacer esto, el limite ya no estaria en la velocidad del micro, puesto que este no hace nada mas que transferir los samples a la SRAM cuando quiero cambiar de señal.

Sin embargo aca se me queman los libros: Si pongo 3 contadores (digamos 74HC4040 aunque es ripple no sync) en cascada, usando 8 bits de cada uno y unos selectores como el 74HC151.

Los contadores serian prescaler_lo, prescaler_mi, prescaler_hi y la SRAM seria el vector, algo asi:



¿Lo que obtengo con esto no es el prescaler que hacemos por soft, sino simplemente un divisor de mucha menor resolucion? No hice una prueba completa pero me da la sensacion de que este esquematico no esta formando un acumulador de 24bits

Si esto tuviera sentido, entonces se complica el hard, pero da la posibilidad de llegar a frecuencias mucho mayores. ¿Que opinan?

Y por ultimo, la 3ra alternativa:

Misma mecanica que la solucion hardware, pero sin contatores y usando el micro como prescaler unicamente, es decir, sacar por el puerto B la direccion de la SRAM externa donde esta el dato. De esta forma, el bucle en el micro usa 666.66ns lo que me da una Fsample de 1.5 MHZ (a 24 bits) o de 500ns - 2MHZ con prescaler de 16bits.

La verdad, bastante lio debido a las limitaciones de los PIC 

Edito: el esquema que puse con los contadores es un divisor 8x8, o sea, podria dividir la frecuencia de reloj en 64 nada mas... muy pobre


----------



## cosmefulanito04 (Ene 31, 2014)

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:


```
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.


----------



## seaarg (Ene 31, 2014)

cosmefulanito04 dijo:


> 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.



cosmefulanito04 dijo:


> 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.



cosmefulanito04 dijo:


> 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.



cosmefulanito04 dijo:


> 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  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"


----------



## cosmefulanito04 (Ene 31, 2014)

seaarg dijo:


> 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)
> ...



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.



seaarg dijo:


> 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.


----------



## seaarg (Feb 1, 2014)

cosmefulanito04 dijo:


> 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 

```
ld		DATA_REG,X
```

Pero en PIC te ahorras muchisimo


----------



## cosmefulanito04 (Feb 1, 2014)

seaarg dijo:


> 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.



seaarg dijo:


> 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.



seaarg dijo:


> En tu programa para AVR, de esta forma te ahorras solamente 2 clocks en
> 
> ```
> ld		DATA_REG,X
> ...



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


```
...
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.


----------



## seaarg (Feb 2, 2014)

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:


```
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:


```
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


----------



## cosmefulanito04 (Feb 2, 2014)

Ojo que te quedó mal la parte donde modificás en índice, ya que en tu rutina:


```
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:


```
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:


```
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
> ...



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.
> ...



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 .


----------



## seaarg (Feb 3, 2014)

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:


```
....................    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)


```
....................    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


```
016A:  MOVLW  paso+-9
```

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


```
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:


```
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:


```
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:


```
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!!



cosmefulanito04 dijo:


> 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:


```
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!.



cosmefulanito04 dijo:


> 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


----------



## cosmefulanito04 (Feb 3, 2014)

seaarg dijo:


> Aca es donde se me crea confusion y es precisamente en lo que marcas de mover el literal 0 a W
> 
> MOVLW 00
> 
> ...



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.



seaarg dijo:


> 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:
> 
> ...



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.



seaarg dijo:


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



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:


```
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.



seaarg dijo:


> Exacto, esto lo tuve en cuenta aqui:
> 
> 
> ```
> ...



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



seaarg dijo:


> 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.


----------



## seaarg (Feb 3, 2014)

cosmefulanito04 dijo:


> 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)




cosmefulanito04 dijo:


> 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


----------



## Xapas (Feb 4, 2014)

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 
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?


----------



## cosmefulanito04 (Feb 4, 2014)

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.


----------



## Xapas (Feb 7, 2014)

Muchas gracias por la aclaracion cosmefulanito! Ya consegui entenderlo  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.


```
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


----------



## cosmefulanito04 (Feb 7, 2014)

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.


----------



## Xapas (Feb 7, 2014)

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.


----------



## cosmefulanito04 (Feb 7, 2014)

Xapas dijo:


> 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 .

Perdoná por no darme cuenta de eso en tu esquemático .


----------



## seaarg (Feb 7, 2014)

Xapas dijo:


> 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:



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.


----------



## cosmefulanito04 (Feb 8, 2014)

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 .


----------



## seaarg (Feb 8, 2014)

cosmefulanito04 dijo:


> 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 .



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!!!


----------



## seaarg (Jun 3, 2014)

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?


----------



## cosmefulanito04 (Jun 3, 2014)

Bien ahí. 



			
				seaarg dijo:
			
		

> 8mhz / 2^24 = 0.476837158203125 de resolucion.



8MHz es la frecuencia de clk, pero ¿es también la frecuencia de sampling?, es decir ¿cuántos clks se necesitan para tener una muestra a la salida?.

En base a ese fsampling, supongamos que realmente te lleva solo un clk como decís, te queda:

fsampling= 8MHz => Resolución usando acumulador 24 bits = fsampling/2^(24) = 0,47... Hz

Paso que se sumará al acumulador = Fsalida/Resolución usando acumulador 24 bits = 2,0971 (1/Hz) x Fsalida

Entonces para obtener 30kHz tus pasos deberían ser:

Paso (30kHz)= 2,0971 (1/Hz) x 30kHz= 62914,56= 0x00F5C2 

Hasta acá lo mismo que obtuviste vos, siguiendo con la explicación que dí en el mensaje #32, ese paso lo divido en 3 bytes distintos:

1er byte (menor peso) = 0xC2= 194
2do byte (mediano peso)= 0xF5= 245
3er byte (mayor peso)= 0x00= 0

Esos son los 3 bytes que vas a tener que ir sumando teniendo en cuenta el carry del byte anterior.

Tu circuito debería hacer esto:

Paso byte 1 = 194
Paso byte 2 = 245
Paso byte 3 = 0

Acum byte 1=0 
Acum byte 2=0 
Acum byte 3=0 (índice que usarás para sacar la muestra en tu RAM)

. Muestra número 1:

Acum byte 1 + Paso byte 1 = 194 → Acum byte 1
Acum byte 2 + Paso byte 2 = 245→ Acum byte 2
Acum byte 3 + Paso byte 2 = 0 → Acum byte 3 

Muestreo el elemento "0" (de nuevo, el índice lo impone Acum byte 3) que se encuentra en la memoria RAM.

. Muestra número 2:

Acum byte 1 + Paso byte 1 = 194 + 194 = 132 y hubo carry→ Acum byte 1
Acum byte 2 + Paso byte 2 = 245 + 245 + carry (1) = 235 y hubo carry→ Acum byte 2
Acum byte 3 + Paso byte 2 = 0 + 0 + carry (1) = 1 → Acum byte 3 

Muestreo el elemento "1" que se encuentra en la memoria RAM.

. Muestra número 3:

Acum byte 1 + Paso byte 1 = 132 + 194 = 70 y hubo carry→ Acum byte 1
Acum byte 2 + Paso byte 2 = 235 + 245 + carry (1) = 225 y hubo carry→ Acum byte 2
Acum byte 3 + Paso byte 2 = 1 + 0 + carry (1) = 2 → Acum byte 3 

Muestreo el elemento "2" que se encuentra en la memoria RAM.

Bue... así se repite y se vé que tiene tiempo de sobra para muestrear los 256 elementos, pero ojo, habrá veces en los cuales un elemento se repetirá, a medida que se irán sumando los acumuladores, Acum byte 2 + Paso byte 2 + carry (1) dará menor o igual a 255 evitando que haya un carry y por lo tanto Acum byte 3 mantendrá su valor.

Ese juego del carry, hace posible que se muestreen los 256 elementos, incluyendo veces en los cuales se repite un elemento, esa repetición será la que sirva de ajuste para que justo se den los 30kHz y no los 31,25 khz que decís.


----------



## seaarg (Jun 3, 2014)

cosmefulanito04 dijo:


> 8MHz es la frecuencia de clk, pero ¿es también la frecuencia de sampling?, es decir ¿cuántos clks se necesitan para tener una muestra a la salida?.



Dependiendo del prescaler. Supongamos que es &H00FFFF: Ante cada clk voy a incrementar en 1 posicion el MSB, o sea la direccion de la RAM.

Con ese prescaler, tengo 8 MHz / 256 posiciones de ram. Asumiendo que en esas 256 posiciones tengo almacenado un ciclo de la senoidal completo, tengo una Fout de 31.25 khz

O sea, la respuesta es SI, la Fsample en este caso es = al oscilador que le ponga y que se aguanten los sumadores. Como comente antes, se bancan OK hasta 16 mhz (cada uno por separado es mas rapido pero la cascada los limita) Le toma a un grupo de 2 sumadores de 4 bits 60 ns aprox para hacer una suma de 2 numeros de 8 bits (segun datasheet). 60 x 3 = 180 ns pero en la practica se ve que pueden ir un poco mas rapido porque anduvieron OK a 16 mhz.



cosmefulanito04 dijo:


> Paso que se sumará al acumulador = Fsalida/Resolución usando acumulador 24 bits = 2,0971 (1/Hz) x Fsalida



Esta parte no la comprendi. ¿ De donde sale el 2,0971 ?



cosmefulanito04 dijo:


> Tu circuito debería hacer esto:
> 
> . Muestra número 2:
> 
> ...



Estoy un 99% seguro de que hace precisamente eso, Te explico el esquematico:

Tengo un grupo de 3 serial shifters en los cuales programo el valor de paso para el prescaler (el numero 62914)

Estos 24 bits forman los 3 bytes que son la entrada de los sumadores. Fijate que cada grupo de 2 sumadores suma los 8 bits de paso (entradas B)  + el resultado de la suma previa (entradas A, que esta almacenado en la salida del latch 74LS374)

Ante cada clock, los latches almacenan el resultado de la suma.

El carry se traspasa desde el pin 9 (carry out) hacia el pin 7 (carry in) de cada sumador, en toda la cascada de 24 bits.

Esto forma un acumulador, donde vemos que el ultimo latch LS374 (que tiene el MSB del resultado) esta conectado al puerto de direccion de la RAM. (ignora el 4094 que esta conectado alli, porque en este momento tiene las salidas en 3 state. Solo se usa para almacenar la forma de onda en la ram al inicializar o cambiar la misma)

Ahora bien, en el PIC hay una rutina para poner a cero el acumulador. Lo que hace es poner en 3state las salidas de los latches y el valor de paso a traves de los 4094. De esta forma, los sumadores hacen 0 + 0 (por el pack de resistores a masa). Esto se hace durante unos cuantos ciclos de reloj para asegurar la operacion.

Luego de resetear los sumadores a cero, se transfiere a traves de los 4094 el numero de paso. Esto es trasferido todo a la vez porque se actua sobre la linea de STROBE, que hace que, una vez que he transferido serialmente todos los bits, al actuar strobe, pasan todos juntos a los latch de salida internos del 4094.

A partir de ahi, se baja la linea OE de los latches LS374, empezando el ciclo de acumulacion, activando la salida de la RAM y desactivando los 2 ultimos 4094 de "programacion" (los 3 primeros fijate que su OE esta siempre activa, mantienen fijo el numero de paso)

En fin, la cuestion principal era si estaba pasando el carry de cada suma al siguiente grupo de sumadores y si, lo estoy haciendo asi que por ahi no viene la cosa 

Voy a seguir haciendo pruebas. Me confunde que en tu caso, multiplicas 2^24 x 9 clocks y gracias a eso, obtenes un numero mucho mas chico en la resolucion. Matematicamente tiene sentido pero me confunde el hecho de que en mi caso, al usar solo 1 clock por sample, deberia tener mas resolucion (o mas velocidad y quiza son mutuamente excluyentes)

De todos modos, estoy muy contento con el resultado obtenido!! Yo creo que solamente faltan ajustes de soft. Quiza estoy "programando" una frecuencia, obtengo esa frecuencia a traves de la matematica de punto flotante en el display del PIC, pero en la realidad estoy transfiriendo otro numero como paso de prescaler sin saberlo (haria falta un analizador logico!!!)

Voy a depurar por ese lado a ver que ocurre.

Saludos y gracias! Cualquier tip o info sera muy bienvenida!

Agrego:

Es absurdo que, con semejante hardware, tenga un limite de 31.25 khz por lo tanto, tengo 2 caminos:

opcion 1:  Permito que el paso de prescaler sea superior a &H00FFFF, haciendo que el MSB "saltee" muestras de las 256 (como es tu caso)

opcion 2: Limito el paso de prescaler a &H00FFFF pero hago un algoritmo para que, cuando el valor sea inferior, transfiera UN solo ciclo senoidal sintetizado a las 256 muestras en RAM (maxima precision), cuando es superior, almaceno 2 ciclos (128x2) en las 256 muestras y recalculo el valor de prescaler... y asi sucesivamente.

Esto ultimo lo empece a implementar, tengo en el programa 3 vectores de senoidal: 1 ciclo en 256 muestras, 2 ciclos, 4 ciclos. Transfiero a RAM el que necesite segun frecuencia de salida deseada.

¿Alguna recomendacion acerca de que opcion elegir?

Gracias!


----------



## cosmefulanito04 (Jun 3, 2014)

Me puse analizar el hard y es como decís, estás haciendo exactamente la misma operación, pero en hard. La verdad alto laburo te mandaste .



			
				seaarg dijo:
			
		

> Esta parte no la comprendi. ¿ De donde sale el 2,0971 ?



Es exactamente lo mismo que hacés vos 1/0,476837158203125 = 2,0971

Me parece que el problema pasa por el soft (poner mal los pasos) y el no saber bien cual es tu fsampling. La fsampling es constante siempre, no importa en que frecuencia te muevas (salvo que le quites resolución al acumulador, complicado en tu caso).

La fsampling será la frecuencia máxima en la que tardás en cambiar una muestra en otra a la máxima velocidad posible, en mi caso, esa fsampling estaba dada por la frecuencia del clock (cristal) dividido por la cantidad de instrucciones que me tomaba cambiar el valor de la muestra (9 clk, de ahí el 9).

Como yo estoy oxidado con electrónica lógica por hard (  ), tal vez lo que ocurra es que en realidad no te tome 1clk, sino dos o más en cambiar esa muestra. Vos tenés los 8MHz como clk al sumador y al latch, la pregunta es: ¿cuántos clocks necesita el sumador para obtener el resultado? ¿el latch para retener el dato a la salida? ¿la memoria en leer el dato? Todo eso, más que nada la RAM, te tiene que sacar algún clk. Verificá eso y después discutimos el resto.


----------



## seaarg (Jun 3, 2014)

Ahora comprendo de donde sale ese 2,0971 gracias! de hecho yo hago division de punto flotante (Fout / 0.47...) cuando vos haces en tu cuenta multiplicacion (Fout x 2,09...) No se si habra diferencia en el micro entre hacer division y multiplicacion cuando son de punto flotante, pero es un buen punto para probar cambiarlo. Quiza la precision se este perdiendo ahi.

La Fsampling es de 8mhz siempre. Por cada clock tenes a la salida de los latches el resultado de la suma de cada uno de los 3 bytes, donde el MSB determina la direccion de RAM.

Numeros:

8mhz = 125ns de periodo

Del datasheet del LS283:

Typical add times
Two 8-bit words 25 ns
Two 16-bit words 45 ns

Lo presentan como suma de 8 bits cuando son de 4 bits, supongo que ya asumen la cascada de 2 integrados, o de 4 en el segundo caso.

Entonces, yo tengo 24 bits, en el peor de los casos: 25 + 25 + 25ns = 75ns

El 74LS374 tiene un tiempo de propagacion en el peor de los casos de 25ns + 75ns = 100 ns

A partir de que el dato esta en el latch, no me tendria que importar el tiempo de la RAM, pero vamos a sumarlo. Esta SRAM tiene un tiempo de "address to data valid" maximo de 25ns

100ns + 25ns = 125ns Lo que es justito el tiempo del periodo de 8mhz. Repito, creo que es seguro asumir 100ns debido a que una vez que la direccion esta en el latch que controla la direccion, hasta que no haya un nuevo clk este no cambia y le da tiempo a actualizar la salida.

Analizados los tiempos, quisiera aclararte que los sumadores son "instantaneos" es decir, no tienen señal de clock. El clock se aplica si a los latches que son los que determinan el resultado de la suma previa (a ser sumado con el paso) y que ingresa a los sumadores como operador A.

Voy a apuntar a que el culpable es el numero de paso que le impongo al prescaler. Modificare el programa para que me muestre en display que es lo que verdaderamente le estoy diciendo al hardware como primer paso de depuracion.

Tambien he notado algo: a cada integrado, en su linea de VCC le pongo un filtrito pasabajos hecho con una R de bajo valor en serie y capacitor a masa. (no tenia inductores a mano). Sin embargo, en los integrados TTL estoy notando que, previo a la resistencia tengo 5V y en el pin en si tengo 4.6V. Las resistencias son de muy bajo valor ya asi que no tiene mucho sentido bajarlas mas, cambiare por inductor. Con los CMOS esto funciona de lujo porque consumen casi nada, y en ellos si tengo los 5V. Ninguno de los integrados tiene ruido en su linea VCC segun el osciloscopio.

Del hardware, si hubiera algun culpable, tambien podria apuntar a los pack de resistencias pull-down que tengo a la salida de los latches, y que los uso para poner a cero el prescaler. Actualmente son de 18K no sea cosa que esten siendo insuficientes para bajar la linea (no creo, porque es alta impedancia: la entrada de los sumadores y la salida OE deshabilitada del latch).


----------



## seaarg (Jun 4, 2014)

Encontre la "formula" para que ande perfecto, pero me ha dejado muy desconcertado no saber la causa de esto.

En este "parche" utilizo como variable "resolucion" el doble del calculado. Es decir, si habia calculado 8mhz / 2^24 = 0.476837158203125 entonces uso 0.95367431640625

Luego, calculo el valor de paso asi:

paso = fout / resolucion;

El resultado de esa division es un valor entero.

Extraigo los 3 bytes de ese prescaler, almacenandolos en p2, p1 y p0

Y ahora, va el truco:


```
if (paso > 32767) {
	p2 = p2 + 1;
	t = intval((paso - 32768) / 65535);
	p2 = p2 + t;
	if(p1 > 127) {
		p1 = p1 - 128;
	}
}
```

Es decir: Se deja el LSB como esta (p0) y si el valor de paso es > 32k incremento en 1 el MSB... luego...
... al paso le resto 32k y eso lo divido por 64k. La parte entera del resultado de la division se la sumo al MSB

y por ultimo, si el byte del medio (p1) es > 127, le resto 128.

Envio p0, p1, p2 al sumador como valor de paso.

Con este ajuste horrible, obtengo exactitud en toda la gama de frecuencias que probe desde 100hz a 2mhz

Llegue a estos valores experimentalmente, analizando los valores que matematicamente deberia aplicar contra lo obtenido y entonces fue que note que luego de los 32k de valor de paso, el generador no me subia la frecuencia de salida hasta que el valor de paso fuera bastante mayor para llegar a afectar el MSB.

Luego observe, a medida que incrementaba fout, que cada cierto valor limite "binario" (64K = 65535) necesitaba incrementar en 1 el MSB del paso para que la salida correspondiera a lo deseado.

Lo "solucione" a mi problema digamos y anda perfecto, pero ahora estoy totalmente confundido porque este arreglo no tiene sentido y algo me dice que este arreglo solo esta "parchando" un problema electrico o logico en algun bit. No tengo idea como cazar el error real para solucionarlo sin parches.

Ejemplo:

Para generar 120khz necesito, segun la cuenta Fout / resolucion:

valor paso = 125829
p2 = 1
p1 = 235
p0 = 133

Esto genera 62.56 khz

Aplicado el parche queda:

Como el valor paso es > 32767 sumo 1 a p2
Luego:
valor paso - 32768 = 93061
93061 / 65535 = 1.41

p2 = 3     [1 + 1 + entero(1.41)]
p1 = 107  (235 - 128)
p0 = 133

Lo que genera aprox 120.09 khz lo que es bastante acertado (aceptando errores y derivas)

Esto sucede en toda la gama que probe, y se corrige en dicha gama

No entiendo mas nada!!


----------



## cosmefulanito04 (Jun 4, 2014)

Bueno si conseguiste obtener la frecuencia medianamente bien, no hay muchas vuelta que darle.

Sin embargo no entiendo porque tanto despelote, tiene que salir de una, tampoco entiendo porque 32kHz es una frecuencia frontera.



			
				seaarg dijo:
			
		

> Numeros:
> 
> 8mhz = 125ns de periodo
> 
> ...



Ok, la hoja de datos avala que al menos en 8MHz se mueve, entonces tal vez lo haga más rápido, es necesario que midas ese retardo bien y la única forma que se me ocurre es que hagas que el paso sea 0x00FFFF y que el contenido de la RAM te permita leer cambios de estado, es decir la idea podría ser que el contendio en los índices pares sea 1 y en los índices impares 0, de esa forma te colgás con el OCR en el bit de menor peso de la salida de la RAM y medís a que frecuencia se dá ese cambio, con eso obtenés la fsampling.


----------



## seaarg (Jun 4, 2014)

Bueno, atento a tu sugerencia, al programa le agregue la forma de onda cuadrada compuesta de 255,0,255,0...

A la salida, con prescaler 0x00FFFF tengo exactamente 4mhz lo que implica 8mhz de sampling. Obviamente la forma de onda es redondeada por el filtro y por la entrada de mi osciloscopio casero.

En la salida digital de la ram tengo lo mismo pero mas cuadrada y de maxima amplitud.

Mas info: El efecto "parche" lo hace tambien en proteus! Esto implica que no tengo un problema electronico en la placa sino que: O es necesario el parche, o hay un error de logica en el diseño.

Me inclino por el ultimo, porque el parche "no tiene sentido". aunque no se que podra ser.

Probe tambien (en proteus) conectar el carry out del MSB al carry in del LSB por si fuera ese el problema con identicos resultados (variacion despreciable)


----------



## cosmefulanito04 (Jun 4, 2014)

¿A la salida de la RAM (no la salida después del filtro) obtenés una rectangular con duty 50% perfecta de 4MHz? si así tu fsampling es 8MHz como decís.

O sea, que las muestras en la RAM son: 0x00, 0xFF, 0x00,.... etc


----------



## seaarg (Jun 4, 2014)

Exactamente. Es lo mismo antes o despues del filtro ya que el valor es 0xFF, 0x00.... Obviamente despues del filtro es mas redonda nomas.


----------



## cosmefulanito04 (Jun 4, 2014)

Y si cargas al paso 0x00F5C2, ¿qué frecuencia tenés a la salida de la RAM?, siempre usando 0x00, 0xFF.... como muestras.

Si no me equivoco, debería darte 30kHz/256 y tal vez un poco menos por los carry.

*Editado:*

No, estoy tirando cualquiera, debería darte 30kHz/2, o sea clavado 15kHz.


----------



## seaarg (Jun 4, 2014)

mmmm esto es raro, poniendo ese paso que me sugeris: 0,245,194 en la cuadrada tengo igual 4 mhz

No me suena a que me de una frecuencia tan baja como decis debido a que es un valor de paso bastante alto, de hecho, es cercano a 0x00FFFF

¿Cual es la ecuacion que hiciste para obtener 30khz / 256 a partir de ese valor de paso?

Mismo valor, en senoidal que usa las 256 muestras para 1 ciclo da 31.36 khz (hay redondeos implicados en el calculo que hace el soft del osci)

Meti el valor 31360 en mi soft externo que calcula los valores de paso que deberia poner (para senoidal, querya contrastar tu valor sugerido) y, sin parche, me da: 0,128,115

con parche: 1,0,115

No se si tendras posibilidad de hacer esto pero, ¿vos tenes forma de imponerle a tu generador un paso de 0x007FFE (32766) y luego uno de 0x0087FE (34814) y ver si te genera una frecuencia apropiada para esos pasos, a tu resolucion? El motivo de estos numeros es que el primero es menor a 32k (32768 seria) y el segundo mayor. Si tuvieses el mismo problema que yo, practicamente no aumentaria la frecuencia de salida entre esos dos valores de paso, mas alla de algun redondeo del instrumento de medicion.


----------



## cosmefulanito04 (Jun 4, 2014)

No, es cierto lo que decís, lo que estás haciendo ahí es darle un paso un poco más lento para que hayan más de 256 muestras y la frecuencia se mantiene en 4MHz que es la mitad de la fsampling que mencioné que siempre se mantiene.

Para ver esos 15kHz, las muestras deberían ser 128 con 0xFF y 128 con 0x00 o viceversa, pensá que así como está, estamos repitiendo las muestras 128 veces, que eso servía para sacar la fsampling.


----------



## cosmefulanito04 (Jun 4, 2014)

seaarg dijo:


> No se si tendras posibilidad de hacer esto pero, ¿vos tenes forma de imponerle a tu generador un paso de 0x007FFE (32766) y luego uno de 0x0087FE (34814) y ver si te genera una frecuencia apropiada para esos pasos, a tu resolucion? El motivo de estos numeros es que el primero es menor a 32k (32768 seria) y el segundo mayor. Si tuvieses el mismo problema que yo, practicamente no aumentaria la frecuencia de salida entre esos dos valores de paso, mas alla de algun redondeo del instrumento de medicion.



Es un quilombo, porque tengo que reprogramarlo. Pero te hago la inversa, sabiendo el paso, averiguo la frecuencia que debería darme para corroborar que no hay problemas con esos pasos.

En mí código el paso lo calculaba así:


```
step=(u32)((frecuencia_salida*9.437184));
```

Donde 9,437184 sale siempre de lo mismo => Step= 2^(24)* 9 * Frecuencia_Hz / 16MHz = 9,437184*Frecuencia_Hz

2^(24): es el acumulador.
9: la cantidad de clks que me toman.
16MHz: el cristal.

Entonces en base a tus pasos:


*0x007FFE (32766)* => Frecuencia_Hz= Paso/9,437184= 32766/9,437184= 3472Hz


*0x0087FE (34814)* => Frecuencia_Hz= Paso/9,437184= 34814/9,437184= 3689Hz



No se vé bien, pero el tester indica unidad de kHz.

En tu caso, la cuenta que deberías hacer en el PIC es la siguiente:


```
step=(u32)((frecuencia_salida*2.097152));
```

Y después obviamente el resultado lo separás en 3 bytes. No sé porque no te dá bien, ya que el hard hace exactamente lo mismo que el soft.


----------



## seaarg (Jun 4, 2014)

Gracias por las fotos!


```
step=(u32)((frecuencia_salida*2.097152));
```

Haciendo esto, y removiendo el "parche" sigue fallando. Con parche sigue andando bien ya sea esta multiplicacion o la division /0.47x2 que yo hacia.

Sin embargo, a pesar de que haciendo tu cuenta en la realidad falla igual, en proteus anda perfecto SIN parche. Antes a menos que pusiera el parche tambien fallaba en proteus... o sea que entre hacer F / 0.47 o sino F / 0.92 y hacer F x 2.09... hay diferencia real.

Esto significa que ahora tengo una punta para empezar a pensar que hay algo mal, ya no en el diseño y/o conexionado logico sino en el armado del hardware fisico, y el olfato me dice que por ahi sea algun nivel que no esta correcto en los carry o en la transferencia serial.

Me voy a volver loco para depurar 24 bits en hardware moviendose a todo trapo!!!



Deseenme suerte


----------



## cosmefulanito04 (Jun 4, 2014)

A favor de los carry es que te dieron la fsampling teórica cuando usaste 0x00FFFF como paso.

Podrías probar usando un acumulador de 16 u 8 bits para ir descartando (sacás sumadores, obviamente sin tocar el hard):

- 16 bits:


```
step=(u16)((frecuencia_salida*0.008192));
```

El paso lo mandás así: 0x Byte1 - Byte2 - 00 

- 8 bits:


```
step=(u8)((frecuencia_salida*0.000032));
```

El paso lo mandás así: 0x Byte1 - 00 - 00 

Obviamente perdés resolución, con 16 bits 122Hz y con 8 bits 31,25 kHz.


----------



## seaarg (Jun 4, 2014)

Solucionado!! funciona perfectisimo ahora sin parches.

Al final no fue tan dificil depurar los 24 bits. Primero estableci un valor de paso determinado por tu formula (hay diferencia entre tu multiplicacion y mi division. Gana tu multiplicacion)

Entonces, con el osciloscopio me puse a ver el valor de cada bit de los shifters.... OK

Entonces, verifique los carryout y carryin de cada sumador. En todos tenia una frecuencia, excepto en el sumador nro 4, que tenia un carryin, pero carryout siempre era 5v

Luego, tenia que ver los 24 bits cambiantes. Con el osci, pin por pin... todos tenian una frecuencia excepto la entrada A3 del sumador nro 4 (bit 16) del acumulador, donde tenia un valor constante de 1.5v. Esto me suena!!! bit 16?? muy relacionado a 65535  Resulta que los TTL con entrada abierta, quedan con 1.5v en dicha entrada y lo toma indeterminado (obvio) con tendencia a 1 logico.

Entonces, revise el latch LS374 en su salida Q7 que va a ese pin del sumador > tenia una frecuencia.

Agarre el tester, medir continuidad. Tenia 15 megaohms entre el pin del latch y la entrada del sumador. UNA maldita mala soldadura entre el pin del latch y su pista!!!

Repase la soldadura... presto!!! anda perfecto  Ahora pensando en retrospectiva, el parche era muy "logico" por ser el bit 16, pero no revisaba las pistas porque en proteus tambien fallaba (y ahi no hay soldaduras!!!  ). Resulta que sumado a la mala soldadura estaba que la cuenta haciendo division provocaba exactamente el mismo error en proteus! Increible, ni queriendo tendria tanta suerte jaja.

Ahora si, puedo continuar mejorando el programa, la interfaz, etc, como para contribuirlo al foro cuando este listo 100%.

Muchas gracias cosme por tu colaboracion y tus ganas de ayudarme a depurar esto.


----------



## cosmefulanito04 (Jun 4, 2014)

Bien ahí. Eso toma vuelo, ya 8 MHz de sampling es super respetable.

Después si te interesa te puedo pasar el protocolo (es bastante sonzo) del puerto serie para poder establecer comunicación con el programita que hice en Java.


----------



## seaarg (Jun 5, 2014)

Ni hablar, ahora que se que genera bien con 8mhz puedo ponerme ambicioso y volver a poner el oscilador de 16 mhz 

Sobre tu ofrecimiento, podria verlo para chusmear que cosas podria hacerle desde la PC. En este caso es usb y el programa lo voy a hacer en .NET

Desde la PC pocas cosas se podrian hacer ya que no es necesario excepto para transferir formas de onda personalizadas.

La funcion sweep va a ser bien complicada, porque implica trasferencia del paso a traves de los serial shifters, pero creo que es posible gracias al STROBE.

Todo este hardware permite que el generador sea standalone, no necesita la PC mas que para transferir ondas personalizadas 

Quisiera hacer unas consultas para ver sus sugerencias:

- Para la salida TTL, la conecte derecho al generador de PWM del pic. Me conviene usar esto o hacer una rutina para generarla poniendo alto-bajo?

- La electronica analogica es donde mas hago agua. Me gustaria si alguien tiene tiempo de mirar en el esquematico la parte de los operacionales, para ver si hay alguna mejora para hacerle al filtro de salida (el pote es para el offset, la amplitud la voy a controlar digitalmente alterando la señal en ram)

Pido esto, porque el filtro actual anda bien pero a frecuencias bajas esta un poco imperfecto y a frecuencias muy altas reduce amplitud (o es mi osciloscopio el que reduce, ya voy a pedir prestado uno profesional)

Lo ideal seria cambiar la frecuencia de corte del filtro en forma escalonada pero eso implica poner otro integrado y distorsion. No se si hay una forma mejor de hacerlo (o una llave mecanica podria ser!)


----------



## cosmefulanito04 (Jun 5, 2014)

seaarg dijo:


> Sobre tu ofrecimiento, podria verlo para chusmear que cosas podria hacerle desde la PC. En este caso es usb y el programa lo voy a hacer en .NET



Sos del lado oscuro... corporativo de .NET 

Fijate en los videos que subí que ahí se vé lo que hace el programa, pero si manejás .NET lo ideal es que te hagas algo a medida.



seaarg dijo:


> Desde la PC pocas cosas se podrian hacer ya que no es necesario excepto para transferir formas de onda personalizadas.
> 
> La funcion sweep va a ser bien complicada, porque implica trasferencia del paso a traves de los serial shifters, pero creo que es posible gracias al STROBE.
> 
> Todo este hardware permite que el generador sea standalone, no necesita la PC mas que para transferir ondas personalizadas



Claro, pero la magia que te puede dar la PC además de las formas de ondas, es el barrido tanto en frecuencia como en duty.



seaarg dijo:


> Ni hablar, ahora que se que genera bien con 8mhz puedo ponerme ambicioso y volver a poner el oscilador de 16 mhz



Duda, ¿en qué te cambia un oscilador de 16MHz si los tiempos de propagación son fijos? ¿no deberías buscar integrados más rápidos?



seaarg dijo:


> Quisiera hacer unas consultas para ver sus sugerencias:
> 
> - Para la salida TTL, la conecte derecho al generador de PWM del pic. Me conviene usar esto o hacer una rutina para generarla poniendo alto-bajo?



Yo lo hice por rutina porque el PWM del hard no superaba los 30 y pico kHz, en cambio con assembler llegaba mucho más.



seaarg dijo:


> - La electronica analogica es donde mas hago agua. Me gustaria si alguien tiene tiempo de mirar en el esquematico la parte de los operacionales, para ver si hay alguna mejora para hacerle al filtro de salida (el pote es para el offset, la amplitud la voy a controlar digitalmente alterando la señal en ram)
> 
> Pido esto, porque el filtro actual anda bien pero a frecuencias bajas esta un poco imperfecto y a frecuencias muy altas reduce amplitud (o es mi osciloscopio el que reduce, ya voy a pedir prestado uno profesional)
> 
> Lo ideal seria cambiar la frecuencia de corte del filtro en forma escalonada pero eso implica poner otro integrado y distorsion. No se si hay una forma mejor de hacerlo (o una llave mecanica podria ser!)



Después lo veo, la frecuencia inferior la podés controlar, la superior dependerá mucho de los operacionales que usastes y la fcs que impusiste.

Lo único que vi de tu hard, es que el teclado no tiene los diodos schottky que evitan que haya un corto entre los puertos en caso de pulsar dos botones a la vez.


----------



## seaarg (Jun 5, 2014)

cosmefulanito04 dijo:


> Sos del lado oscuro... corporativo de .NET



Jaja no way, en realidad trabajo en PHP, o en C++ pero para aplicaciones desktop siempre le di a .NET porque Java no me puse a aprender 



cosmefulanito04 dijo:


> Claro, pero la magia que te puede dar la PC además de las formas de ondas, es el barrido tanto en frecuencia como en duty.



Para el barrido en frecuencia, tenia pensado que el micro cambie el prescaler cada un tiempo determinado. ¿Como seria eso del barrido en duty para una onda senoidal?



cosmefulanito04 dijo:


> Duda, ¿en qué te cambia un oscilador de 16MHz si los tiempos de propagación son fijos? ¿no deberías buscar integrados más rápidos?



En que la fsampling seria en vez de 8mhz, de 16mhz. A cada ciclo de reloj tengo un sample en ram, que puede ser distinto al anterior o igual, depende el prescaler. Los tiempos de propagacion de los integrados solo limitan hasta que fsampling puedo llegar, y 16 mhz *creo* que llegan (ahora que anda bien puedo probarlo). Digo *creo* porque los tiempos que calculamos mas arriba son los tipicos, pero no significa que sean necesariamente los limites. A 12mhz estoy bastante seguro que llega bien solo que no tengo un oscilador de 12mhz (antes removi el oscilador y puse un cristal en el inversor que uso para distribuir el reloj)



cosmefulanito04 dijo:


> Yo lo hice por rutina porque el PWM del hard no superaba los 30 y pico kHz, en cambio con assembler llegaba mucho más.



http://calculator-sundar.hpage.co.in/pic-pwm_88966258.html

En esta pagina, pone como frecuencia 48mhz, deja el pwm freq en blanco y pone el duty en 50%. Te va tirar la lista de todos los pwm posibles con ese micro. A medida que te vas mas arriba tenes menos "suavidad" entre saltos.



cosmefulanito04 dijo:


> Después lo veo, la frecuencia inferior la podés controlar, la superior dependerá mucho de los operacionales que usastes y la fcs que impusiste.



Los operacionales son LM318 configurados de forma tal que den el maximo slew rate (son de 50v/us y con esta configuracion trepan a 150v/us) El ancho de banda en señal pequeña es de 15mhz. No estoy seguro que puedan ir a tanto pero creo que deberian poder tirar una señal de 5vpp a 4-5mhz sin problemas. (No hice cuentas aun)



cosmefulanito04 dijo:


> Lo único que vi de tu hard, es que el teclado no tiene los diodos schottky que evitan que haya un corto entre los puertos en caso de pulsar dos botones a la vez.



AUCH! que buena observacion no me habia dado cuenta. Puedo solucionarlo poniendo en alta impedancia los pines de las columnas que no estoy sensando (en el esquematico no esta, pero cada fila tiene una resistencia pull-down)

De todos modos, aun no se si dejar puesto este teclado o no, hace que sea muy comodo ir por los menu y particularmente ingresar la frecuencia, pero es voluminoso. Estaba pensando en reemplazarlo por 3 botones nomas (arriba, abajo, menu/enter)


----------



## cosmefulanito04 (Jun 5, 2014)

seaarg dijo:


> Para el barrido en frecuencia, tenia pensado que el micro cambie el prescaler cada un tiempo determinado.



Si, el tema es que tenes que crearle un menú al usuario que permita configurar la frecuencia de inicio y final del barrido y su deltaF, incluso con el programa de PC permito que sean pasos lineales, por octavas o por decadas. Hacer ese menú en el uC es un despelote y encima termina siendo recontra secuencial para el usuario (tedioso).



seaarg dijo:


> ¿Como seria eso del barrido en duty para una onda senoidal?



El duty nada más se setea en la salida TTL.



seaarg dijo:


> En que la fsampling seria en vez de 8mhz, de 16mhz. A cada ciclo de reloj tengo un sample en ram, que puede ser distinto al anterior o igual, depende el prescaler. Los tiempos de propagacion de los integrados solo limitan hasta que fsampling puedo llegar, y 16 mhz *creo* que llegan (ahora que anda bien puedo probarlo). Digo *creo* porque los tiempos que calculamos mas arriba son los tipicos, pero no significa que sean necesariamente los limites. A 12mhz estoy bastante seguro que llega bien solo que no tengo un oscilador de 12mhz (antes removi el oscilador y puse un cristal en el inversor que uso para distribuir el reloj)



Pero vos no tenés control sobre el tiempo de propagación y ya mediste que te daba 125nS clavados. Vos bien me corregiste diciendo que los sumadores no usan ningún clock, por lo tanto los 100nS ya están fijos, solo queda ver que pasa con la RAM.



seaarg dijo:


> http://calculator-sundar.hpage.co.in/pic-pwm_88966258.html
> 
> En esta pagina, pone como frecuencia 48mhz, deja el pwm freq en blanco y pone el duty en 50%. Te va tirar la lista de todos los pwm posibles con ese micro. A medida que te vas mas arriba tenes menos "suavidad" entre saltos.



Con el AVR no puedo llegar tan lejos, porque mi cristal máximo era de 16MHz y tenía un preescaler mínimo de 256 cuentas (ba prodría reducir la resolución del duty y hacerlo de solo 100). Con 16MHz dá 60 y pico de kHz con 8MHz era que daba 30 y pico de kHz. Si lo podés hacer por el hard del uC, dale para adelante.



seaarg dijo:


> Los operacionales son LM318 configurados de forma tal que den el maximo slew rate (son de 50v/us y con esta configuracion trepan a 150v/us) El ancho de banda en señal pequeña es de 15mhz. No estoy seguro que puedan ir a tanto pero creo que deberian poder tirar una señal de 5vpp a 4-5mhz sin problemas. (No hice cuentas aun)



Cerca de los 5MHz todavía tenés una ganancia de 20dB (10 veces), así que podés llegar, pasando los 10MHz es casi 10dB.

Lo que te recomiendo es usar el Pspice o el LTspice (si es que tienen ese amplificador) y graficar la respuesta en frecuencia del filtro, porque estás muy cerca de los polos de lazo abierto del operacional y eso te va a correr la frecuencia de corte superior.


----------



## seaarg (Jun 5, 2014)

cosmefulanito04 dijo:


> Pero vos no tenés control sobre el tiempo de propagación y ya mediste que te daba 125nS clavados. Vos bien me corregiste diciendo que los sumadores no usan ningún clock, por lo tanto los 100nS ya están fijos, solo queda ver que pasa con la RAM.



Quisiera aclararte algo que me parece que estas pasando por alto:

Si bien te dije que los sumadores no usan clock, fijate que el latch LS374 es el que determina el valor que tiene el acumulador en ESE momento (que sera sumado al valor de paso seteado por los 4094).

Los latch SI tienen clock y es en cada clock donde cambia su salida por la sumatoria. Entonces, de ahi que la velocidad de sampling es = la velocidad de reloj aplicada a los latches.

La RAM en si no tiene clock, simplemente cuando ve una direccion en la entrada, pone su dato a la salida dentro de los 25ns de aplicada la direccion.

Tenes razon en un punto y es que el conjunto sumadores + latch + ram "usa" 125ns en teoria para poner un dato a la salida, por lo tanto, estaria en teoria limitado a 8mhz. En la practica, a 16mhz funciono bien. Me queda hacer una prueba para ver si no hay "bug" o corrimiento de frecuencia como tenia con la soldadura mala, pero esta vez provocado porque algun sumador no alcance a sumar a tiempo... o un latch que no llegue a pasar bien un dato, etc. a velocidades maximas.

Por ej: Originalmente esto lo puse a 20mhz. Tenia formas de onda distorsionadas debido a que los integrados no alcanzaban a operar correctamente. Al bajar a 16mhz se corrigio pero tenia el defecto que solucionamos juntos, solamente que buscando ese defecto fue que lo baje a 8mhz. Ahora que esta eso corregido es cuestion de volver a soldar el oscilador de 16mhz y ver que pasa.


----------



## cosmefulanito04 (Jun 5, 2014)

O sea tus sumadores son más rápidos que los 25nS originales, ya que estaban limitados por los latch. Ojo con terminar con una base de tiempo variable, deberías volver a medir la fsampling.


----------



## chclau (Jun 6, 2014)

Yo tengo hecho y simulado un DDS en FPGA, esta bien si lo pongo en este hilo?


----------



## cosmefulanito04 (Jun 6, 2014)

Apareció el amigo que sabe de Fpga!. 

Si por favor, subilo, definitivamente es la mejor forma de encarar este proyecto y con que subas alguna punta tal vez se consiga que un usuario a futuro se anime y lo arme con esta tecnología.


----------



## seaarg (Jun 6, 2014)

A mi me fascinaria hacerlo con fpga pero de lo unico que dispongo aqui son GAL. No averigue si puedo conseguir alguna en Cordoba y no sabria como conseguir un programador. Basicamente, no se por donde empezar (altera, xilix, etc)

De momento, viendo un post tuyo cosme, me baje el Atmel Studio 6.2


----------



## chclau (Jun 6, 2014)

Con respecto a empezar con FPGAs, se pueden conseguir kits de evaluacion muy buenos a costos de entre 50 y 100 dolares. Tanto Xilinx como Altera son muy buenos (hay otros), yo personalmente conozco Altera asi que si puedo ayudar con mas datos, no hay mas que preguntar.

Y ahora, el código del NCO. NCO quiere decir oscilador controlado numéricamente. 

Primero las explicaciones. El codigo esta basado en una tabla seno de un cuarto de ciclo, luego usa simetria y restadores para generar los otros tres cuadrantes de la señal. Esto se hace asi porque desde el punto de vista de recursos es mucho mas "barato" una unidad de suma-resta y multiplexor, que usar una memoria interna del dispositivo cuatro veces más grande.
La lógica incluye un prescaler para generar una frecuencia intermedia.

Suponiendo que clkin =  50MHz (es lo que usa mi tarjeta de evaluación con un Cyclone III de Altera), y usando clk_divide = 780, la frecuencia generada por el prescaler es 50MHz/781 o sea aproximadamente 64kHz. Si escribo en freq_prog la cantidad 256, la frecuencia de salida sera de 64kHz. freq_prog tiene 16 bits, los ocho mas significativos son la parte entera y el resto la parte decimal.

Si con el mismo prescaler quiero obtener 1kHz, freq_prog debe ser  3.99872 (en binario, 00000011'11111111, en hexa 03FF).  

En este caso la salida es de 8 bits con signo, o sea de -127 a 127. En su versión original el código está parametrizado pero lo pasé a numeros "hard coded" para mayor claridad del código en esta primera versión que posteo. 


Por orden de aparición.
1 - Declaro las librerías a utilizar
2 - Declaro la entidad y sus puertos
3 - Constantes y señales
4 - Infiero los contenidos de la ROM de un cuarto de ciclo usando GENERATE en GENROM
5 - Proceso del prescaler
6 - Proceso del acumulador de fase. Noten que la fase para la tabla la tomo de la parte más significativa del acumulador (phase_in), asimismo de esa parte más significativa tomo los dos bits más significativos para el proceso de la tabla en cuatro cuadrantes (phase_sel).
7 - Genera cuatro cuadrantes de la señal senoidal aprovechando sus propiedades de simetría y signo.

Próximamente postearé resultados de un test bench.


```
LIBRARY ieee;
use ieee.std_logic_1164.all;
use ieee.math_real.all;
use ieee.numeric_std.ALL;

ENTITY nco IS

PORT (	
	clk 		: in std_logic;
	reset 		: in std_logic;
	
	-- NCO ports
	clk_divide	: in unsigned(11 downto 0);
	freq_prog		: in unsigned(15 downto 0);
	dout 		: out signed(7 downto 0)
);
END nco ;--
--
ARCHITECTURE rtl OF nco IS

	CONSTANT ROMSIZE : INTEGER  := 2**(8-2);
	CONSTANT ROM_W   : INTEGER  := 2**8;
	CONSTANT HALF_PI : REAL     := MATH_PI/2.0;
	CONSTANT STEP_FR : natural  := 64;
	TYPE SINTAB IS ARRAY(0 TO ROMSIZE-1) OF signed (NNCO-1 DOWNTO 0);
	
	signal SINROM: SINTAB;
	signal phase_sel   : std_logic_vector(1 downto 0);
	signal phase_accum : std_logic_vector(15 downto 0); 
	signal phase_in    : std_logic_vector(7 downto 0);
	signal freq_divide : unsigned(15 downto 0);
	signal acc_en      : std_logic;	
	
BEGIN

	-- Infer ROM sine table,
	GENROM:
	FOR idx in 0 TO ROMSIZE-1 GENERATE
		CONSTANT y: 	REAL := SIN(real(idx)*HALF_PI/real(ROMSIZE));
		CONSTANT ysc:   REAL := y*real((ROM_W-1)/2);
		CONSTANT yn: 	SIGNED (7 DOWNTO 0) := TO_SIGNED(INTEGER(ysc),8);
	BEGIN
		SINROM(idx) <= yn; 
	END GENERATE; 
	
	-- Sampling freq. generator 
	-- Divides input clk by clk_divide
    process (clk, reset) begin
        if (reset = '1') then
            acc_en		<= '0';
			freq_divide <= (others => '0');
        elsif (rising_edge(clk)) then
            acc_en 	    <= '0';
			freq_divide <= freq_divide + 1;
			if (freq_divide = clk_divide) then
				acc_en 		<= '1';
				freq_divide <= (others => '0');
			end if;	
		end if;	
	end process;
	
	-- phase accumulator
    process (clk, reset) begin
        if (reset = '1') then
            phase_accum <= (others => '0');
        elsif (rising_edge(clk)) then
			if (acc_en = '1') then
				phase_accum <= std_logic_vector(unsigned(phase_accum) + unsigned(freq_prog));
			end if;	
		end if;	
	end process;
	phase_in  <= phase_accum(15 downto 8);
	phase_sel <= phase_in(7 downto 6);

	-- phase lookup - four quadrants
    process (clk, reset) begin
        if (reset = '1') then
            dout 		<= (others => '0');
        elsif (rising_edge(clk)) then
			case (phase_sel) is
				when "00" =>
					dout <= SINROM(to_integer(unsigned(phase_in(5 downto 0))));
				when "01" =>
					dout <= SINROM(to_integer(ROMSIZE-1-unsigned(phase_in(5 downto 0))));
				when "10" =>
					dout <= -SINROM(to_integer(unsigned(phase_in(5 downto 0))));
				when "11" =>
					dout <= -SINROM(to_integer(ROMSIZE-1-unsigned(phase_in(5 downto 0))));
				when others =>	
					dout <= SINROM(to_integer(unsigned(phase_in(5 downto 0))));
			end case;		
		end if;	
	end process;
	
END;
```


----------



## cosmefulanito04 (Jun 8, 2014)

*seaarg*, estuve viendo tu salida analógica. Después del primer operacional que solo lo usas como inversor, tenes dos etapas:

1- Filtro pasa bajos de 1er orden compuesto por la resistencia de 560 Ohms (R1) y el capacitor de 220pF (C1).
2- Filtro pasa bajos de 1er orden usando el segundo operacional impuesto por la resistencia de 22pF (C2) y de 1,2kOhm (R3).

La expresión matemática de todo (incluyendo el 1er operacional, simplifique las negaciones) queda así:

[LATEX]Av_{(s)}=\frac{\frac{1}{C_{1}.R_{1}}}{s+\frac{1}{C_{1}.R_{1}}}.\frac{R_{3}}{R_{2}}.\frac{\frac{1}{C_{2}.R_{3}}}{s+\frac{1}{C_{2}.R_{3}}}[/LATEX]

- La ganancia en baja frecuencia será: R3/R2.
- Vas a tener 2 polos, uno en 1,29MHz (lo impone R1 y C1) y otro en 6MHz (lo impone R3 y C2). 

Esto último no está bien, porque te impone una frecuencia de corte superior inferior a 1,29MHz, es decir todo lo que venga a partir de esa frecuencia se empieza a atenuar (o mejor dicho a tener menor ganancia).

Otro problema que veo, es que no tenés un pasa altos (o un capacitor en serie) que elimine el offset de 2,5V.

Yo creo que el 1er operacional está totalmente desperdiciado y el segundo también, ya que podrías hacer un filtro de mayor orden. Pero antes que nada, vas a tener que definir bien:

- Ganancia en frecuencias bajas.
- Ancho de banda (esto incluye la necesidad de imponer una frecuencia de corte inferior => pasa altos).
- Rechazo a las frecuencias de imagen que vas a tener.
- Que tan lineal deberá ser el filtro, a menor linealidad, mayor distorsión en señales poliarmónicas.

Por cierto, ¿para qué necesitas el Atmel Studio si estás usando un PIC?


----------



## seaarg (Jun 9, 2014)

Ante todo, gracias por tu tiempo y tu analisis! como comente antes, en analogica hago agua 



cosmefulanito04 dijo:


> *seaarg*, estuve viendo tu salida analógica. Después del primer operacional que solo lo usas como inversor, tenes dos etapas:



Si, la idea era usarlo solamente de buffer sin amplificar ni hacer de filtro para no alterar de ningun modo la señal. Me parecio que al ser una red de resistencias, si me ponia a hacer un filtro ahi podia llegar a distorsionar o atenuar la señal original debido a la alta impedancia de la salida del R2R



cosmefulanito04 dijo:


> 1- Filtro pasa bajos de 1er orden compuesto por la resistencia de 560 Ohms (R1) y el capacitor de 220pF (C1).
> 2- Filtro pasa bajos de 1er orden usando el segundo operacional impuesto por la resistencia de 22pF (C2) y de 1,2kOhm (R3).



Exacto el #1, pero el #2 en realidad, si no me equivoco, era una mezcla de filtro pasabajos (capacitor) + regulacion de ganancia (relacion entre resistencia 430 ohms y 1.2k) para que me de exactamente 5vpp. Llegue a ese valor a fuerza bruta probando con el simulador ya que desconozco las formulas implicadas.




cosmefulanito04 dijo:


> Otro problema que veo, es que no tenés un pasa altos (o un capacitor en serie) que elimine el offset de 2,5V.



No necesite eso ya que le puse un potenciometro de offset de 10k, ese que esta entre VEE y AVDD y que va al pin 3 del operacional. Con eso ajusto el offset poniendo la señal en la parte positiva enteramente o en la parte negativa y aparentemente funciona sin problemas (otra vez, llegue a esos valores experimentando)



cosmefulanito04 dijo:


> Yo creo que el 1er operacional está totalmente desperdiciado y el segundo también, ya que podrías hacer un filtro de mayor orden. Pero antes que nada, vas a tener que definir bien:



Esto es importante me parece: ¿Vos decis que no hacia falta que el 1r operacional sea solamente buffer? Es decir, no se veria afectada la señal por la alta impedancia de la salida del DAC?

Cambiar el diseño ahora es impractico porque ya esta el PCB (es un infierno hacer este pcb de nuevo!) pero, si no es demasiada molestia, me gustaria saber si se puede mejorar esta etapa analogica cambiando valores, o haciendo modificaciones minimas que pueda hacer sobre este circuito ya ensamblado.



cosmefulanito04 dijo:


> Por cierto, ¿para qué necesitas el Atmel Studio si estás usando un PIC?



Porque voy a intentar hacer mi proximo proyecto usando Atmel  asi que hay que empezar a aprenderlo. Me gusto ver la diferencia de performance.


----------



## cosmefulanito04 (Jun 9, 2014)

seaarg dijo:


> Exacto el #1, pero el #2 en realidad, si no me equivoco, era una mezcla de filtro pasabajos (capacitor) + regulacion de ganancia (relacion entre resistencia 430 ohms y 1.2k) para que me de exactamente 5vpp. Llegue a ese valor a fuerza bruta probando con el simulador ya que desconozco las formulas implicadas.



Claro, el 2do operacional es el que finalmente impone la ganancia, lo que yo llamo R3/R2.



seaarg dijo:


> No necesite eso ya que le puse un potenciometro de offset de 10k, ese que esta entre VEE y AVDD y que va al pin 3 del operacional. Con eso ajusto el offset poniendo la señal en la parte positiva enteramente o en la parte negativa y aparentemente funciona sin problemas (otra vez, llegue a esos valores experimentando)



Si, lo pensaste de forma distinta, yo preferí eliminar la componente de continua y luego agregarla o no con un pote + llave. Es cuestión de gusto.



seaarg dijo:


> Esto es importante me parece: ¿Vos decis que no hacia falta que el 1r operacional sea solamente buffer? Es decir, no se veria afectada la señal por la alta impedancia de la salida del DAC?



Está bien usar un buffer después de la red, pero no esa configuración que usaste, ya que presenta una impedancia de solo 5,1kOhm. Lo ideal hubiera sido usar un buffer no negador, que tiene como característica principal tener una impedancia de entrada muy alta. Lo que podés hacer, para no cambiar el PCB es aumentar el valor de esas resistencias.



seaarg dijo:


> Cambiar el diseño ahora es impractico porque ya esta el PCB (es un infierno hacer este pcb de nuevo!) pero, si no es demasiada molestia, me gustaria saber si se puede mejorar esta etapa analogica cambiando valores, o haciendo modificaciones minimas que pueda hacer sobre este circuito ya ensamblado.



Lo que se puede hacer es modificar los polos de esos dos filtros cambiando los valores de los capacitores/resistencia. Pero antes es necesario que definas los puntos que te puse antes:



			
				Cosmefulanito04 dijo:
			
		

> - Ganancia en frecuencias bajas.
> - Ancho de banda.
> - Rechazo a las frecuencias de imagen que vas a tener.
> - Que tan lineal deberá ser el filtro, a menor linealidad, mayor distorsión en señales poliarmónicas.



El último punto en este caso no se aplicaría, ya que son filtros simples de 1er orden. El 1er punto ya lo definiste y queda por saber los otros dos.


----------



## seaarg (Jun 9, 2014)

Honestamente desconozco como definir lo que decis, ya que no se (supongo) que es el ancho de banda o frecuencia de imagen.

Ancho de banda: Asumo que implica que frecuencias quiero dejar pasar con el filtro. Si tengo un reloj de 8mhz y para representar aproximadamente una triangular necesitaria al menos 5 puntos, creo que deberia tener un ancho de banda de 1hz-1.6 mhz, o sino, una senoidal de 4 puntos: 1hz-2mhz Supongamos 2mhz si es correcto lo que interpreto.

Rechazo a las frecuencias de imagen: (cerebro tilt aca) Adivinando diria que son todas las frecuencias arriba de la maxima que quiero representar, que seria 2mhz. Google no me esta ayudando en esto porque no se como buscarlo.

Idealmente, el filtro deberia dejar pasar la señal sin atenuar hasta los 2mhz, por lo tanto, quiza la FC deba ser un poco mayor que eso, ya que no creo que este filtro sea de corte abrupto.


----------



## cosmefulanito04 (Jun 9, 2014)

El ancho de banda es el rango de frecuencia que marcan una caída de 3dB. Como en tu caso no eliminás la comtinua, tu ancho de banda irá de DC a una frecuencia de corte superior. En esa frecuencia de corte superior, vas a tener una caída de 3dB.

Para saber el ancho de banda necesitas saber la frecuencia de sampling y la máxima frecuencia senoidal que esperás. Pero antes es necesario que entiendas lo básico del muestreo, si hacés un análisis en frecuencia de lo que hacés cuando muestreas, pasa esto:







Olvidate por un momento de las frecuencias negativas que aparecen en el gráfico que pueden confundirte un poco.

Ahí la señal X(jw) es la salida que esperás obtener (no importa la forma), cuyo ancho de banda es wm. P(jw) es la señal de muestreo (en la realidad esta señal es distinta, pero no quiero marearte), que tendrá una frecuencia de muestreo fs (o en radianes ws).

Xp(jw) sale de una operación llamada "convolución" entre X(jw) y P(jw), para resumir, esa convolución lo que hace es desplazar la señal X(jw) en c/"flechita" (delta de dirac) que tiene P(jw). Entonces si te fijás en c/ws aparece una señal X(jw) repetida (obviamente con distinta amplitud). Esa convolución se dá cuando muestras, siempre.

La idea de ese filtro pasa bajos final, es eliminar todas esas X(jw) de "más" (frecuencias imágenes) y quedarte solo con la que se encuentra entre DC y fm. Si no atenúas bien esas frecuencias de imágenes, a la salida hablando en forma temporal, aparecen y terminan deformando la señal original. Por lo tanto lo conveniente es que el nivel de atenuación de esas frecuencias imágenes sea muy alto (rechazo a la frecuencias imágenes), ¿qué tan alto? según que tan pura querés que sea la señal a la salida, en términos de senoidal, cuanto THD estás dispuesto a tolerar (además obviamente del THD que introduce el circuito en si).

Si ves la segunda Xp(jw), la que tiene solapamiento, eso sucede cuando la frecuencia máxima de la señal es mayor a fsampling/2, la famosa fórmula de nyquist, si superás fsampling/2, obtenés a la salida una deformación por aliasing.

En mi caso yo tenía una fsampling de 1,7MHz, quería llegar a una senoidal de 500kHz, por lo tanto mi frecuencia de imagen empezaba en 1,2MHz (fsampling-fseñal), yo implementé un filtro de 4to orden para tener una atenuación cercana a 24dB sobre esa frecuencia imágen. Sin embargo me encontré con otro problema que nunca terminé de solucionar, que era ese armónico de baja frecuencia que se me metía cuando la señal estaba cerca de los 500kHz.

Una forma de mejorar ese rechazo, es aumentar la fsampling o disminuir la frecuencia máxima de la señal (agregar más muestras). Aumentando la fsampling en frecuencias altas (usé un acumulador de solo 8 bits), conseguí aumentar un poco más esa fsampling, dando como resultado una mejora en la salida de los 500kHz.

Volviendo un poco a tu caso, suponiendo que lográs una fsampling de 16MHz, tu máxima frecuencia teórica será 8MHz, en forma práctica si imponés dicha frecuencia en 5MHz, la frecuencia imagen la vas a tener en 11MHz y con dos filtros de 1er orden conseguís atenuar más o menos 12dB (va a ser un poquito más). 

Es decir, si querés una senoidal de 5MHz, en frecuencia vas obtener una armónica en 5MHz (la senoidal que buscás) y una armónica 12dB atenuada en 11MHz, esa 2da armónica mientras menor peso tenga, mejor se verá la senoidal de 5MHz.


----------



## seaarg (Jun 9, 2014)

cosmefulanito04 dijo:


> La idea de ese filtro pasa bajos final, es eliminar todas esas X(jw) de "más" (frecuencias imágenes) y quedarte solo con la que se encuentra entre DC y fm. Si no atenúas bien esas frecuencias de imágenes, a la salida hablando en forma temporal, aparecen y terminan deformando la señal original. Por lo tanto lo conveniente es que el nivel de atenuación de esas frecuencias imágenes sea muy alto (rechazo a la frecuencias imágenes), ¿qué tan alto? según que tan pura querés que sea la señal a la salida, en términos de senoidal, cuanto THD estás dispuesto a tolerar (además obviamente del THD que introduce el circuito en si).



Muchisimas gracias por tu explicacion, los graficos me superan un poco pero tu explicacion verbal la comprendo. ¿Estas frecuencias imagen son las que, si no tuviera bien el filtro, las veria como picos fuera de la frecuencia pura esperada en el grafico de FFT?



cosmefulanito04 dijo:


> En mi caso yo tenía una fsampling de 1,7MHz, quería llegar a una senoidal de 500kHz, por lo tanto mi frecuencia de imagen empezaba en 1,2MHz (fsampling-fseñal), yo implementé un filtro de 4to orden para tener una atenuación cercana a 24dB sobre esa frecuencia imágen. Sin embargo me encontré con otro problema que nunca terminé de solucionar, que era ese armónico de baja frecuencia que se me metía cuando la señal estaba cerca de los 500kHz.



mi fsampling=8mhz - 2mhz = 6mhz Ahi tendria que estar mi FC verdad? El armonico vos lo ves mediante la FFT o de otra forma?



cosmefulanito04 dijo:


> Volviendo un poco a tu caso, suponiendo que lográs una fsampling de 16MHz, tu máxima frecuencia teórica será 8MHz, en forma práctica si imponés dicha frecuencia en 5MHz, la frecuencia imagen la vas a tener en 11MHz y con dos filtros de 1er orden conseguís atenuar más o menos 12dB (va a ser un poquito más).
> 
> Es decir, si querés una senoidal de 5MHz, en frecuencia vas obtener una armónica en 5MHz (la senoidal que buscás) y una armónica 12dB atenuada en 11MHz, esa 2da armónica mientras menor peso tenga, mejor se verá la senoidal de 5MHz.



A 16mhz lo probe y no llegue, los acumuladores se saltean bits. Llegaria a 12 pero lo deje en 8mhz y se me ocurre que la fmax que este bicho pueda generar manteniendo la forma seria 2mhz y el filtro deberia hacer caer los mayores dB posibles a 6mhz

¿Entendi mas o menos bien?


----------



## cosmefulanito04 (Jun 9, 2014)

seaarg dijo:


> Muchisimas gracias por tu explicacion, los graficos me superan un poco pero tu explicacion verbal la comprendo. ¿Estas frecuencias imagen son las que, si no tuviera bien el filtro, las veria como picos fuera de la frecuencia pura esperada en el grafico de FFT?



Si. La FFT lo que te muestra son armónicos. Una senoidal perfecta está compuesta de un solo pico de esos.



seaarg dijo:


> mi fsampling=8mhz - 2mhz = 6mhz Ahi tendria que estar mi FC verdad?



No, si 2MHz es tu frecuencia de señal máxima, 6MHz será el inicio de tu frecuencia de imagen, vos tendrías que cortar a partir de 2MHz en adelante, para que en 6MHz la atenuación sea la máxima posible. 

Ahora, el problema es con las señales poliarmónicas, ya que para ver más o menos la forma de onda de una señal rectangular necesitas agarrar 5 armónicos de la frecuencia fundamental, es decir si tenés una rectangular de 1MHz, para verla medianamente bien, tu filtro debería empezar a cortar recién en 5MHz (5to armónico de la fundamental de 1MHz). Por eso, mi generador está limitado a 100kHz en las señales poliarmónicas, porque la triangular se empieza a ver "fea". Por eso *fogo* propuso hacer un filtro variable en pasos.



seaarg dijo:


> El armonico vos lo ves mediante la FFT o de otra forma?



Si, fijate que en el mensaje #3, la señal violeta es el FFT. Ahí podés ver que la senoidal tiene solo un pico.



seaarg dijo:


> se me ocurre que la fmax que este bicho pueda generar manteniendo la forma seria 2mhz y el filtro deberia hacer caer los mayores dB posibles a 6mhz
> 
> ¿Entendi mas o menos bien?



Esa sería la idea, en 6MHz tenés que conseguir la máxima atenuación posible. Con tus filtros fijados cerca de 2MHz, en 4MHz conseguís 12dB y en 6 vas a conseguir un poco más de atenuación.


----------



## seaarg (Jun 9, 2014)

Ok cosme, muchisimas gracias. Lo que voy a hacer es reproducir en proteus la parte de los operacionales y hacerle un analisis en frecuencia cambiando valores, o configuracion en lo posible, hasta dar con un corte como el que describis.


----------



## cosmefulanito04 (Jun 9, 2014)

El proteus y la parte analógica no son muy compatibles. 

Como tenés varias etapas de filtros de 1er orden primero tenés que calcular la frecuencia de corte de c/filtro (que será la misma en todas las etapas), eso lo hacés así:

[LATEX]f_{corte-una-etapa}=\frac{f_{corte-total}}{\sqrt{\sqrt[n]{2}-1}}[/LATEX]

Como son 2 filtros => n=2 y si buscamos que todo el circuito presente una frecuencia de corte en 2MHz:

[LATEX]f_{corte-una-etapa}=\frac{2MHz}{\sqrt{\sqrt[2]{2}-1}}=3,1MHz[/LATEX]

Ahora tenés que usar esta fórmula para calcular el 2do filtro:

[LATEX]f_{corte-2do-filtro}=\frac{1}{2.\pi.C_{2}.R_{3}}[/LATEX]

Supongamos que le dás el valor C2=10pF => R3=5,1kOhm. Solo vas a tener que recalcular el valor de la resistencia de 430 Ohms para obtener más o menos la misma ganancia, digamos que podría valer 1,8kOhm más cerca de 2kOhm para mantener la misma ganancia.

*Ahora que me doy cuenta, en el análisis del post anterior no tuve en cuenta que el 1er filtro está cargado con esa resistencia de 430Ohm* y  en realidad la expresión es otra. Para el primer filtro, tenés que usar esta fórmula:

[LATEX]f_{corte-1er-filtro}=\frac{R_{1}+R_{2}}{2.\pi.C_{1}.\left(R_{1}.R_{2}\right)}[/LATEX]

R2 será 1,8kOhm del 2do filtro, te queda calcular R1 y C1. Si imponemos el valor de C1=100pF => R1=680Ohm; con esos valores la fc de ese filtro es cercana a 3,2MHz.


----------



## seaarg (Jun 10, 2014)

Gracias cosme! El finde veo de hacer estos cambios y te comento. Muchas gracias


----------



## seaarg (Jun 18, 2014)

Queria contarles, me puse a probar contra un grafico de frecuencia en proteus (es lo que tengo) y cambiando los valores de los componentes segun el esquema que adjunto logre:

- Respuesta plana en frecuencia con 0db desde 1hz hasta ~2mhz
- Fcorte con -3db alrededor de los 2Mhz
- Aumentar de 5K1 a 47K las resistencias de la entrada analogica.

Con estos cambios, la señal se ve mejor. No es tan perfecta como la que se ve en el OCR de cosme (tiene apenas un poco mas de ruido porque falta filtro) pero no veo armonicos de baja frecuencia. Tengo que hacer mas pruebas

Lo que si, voy a tener que aumentar la resistencia que determina la ganancia del ultimo operacional, porque sigo teniendo una señal de 3.8vpp en vez de 5vpp

Tambien a partir de 1mhz la señal empieza a tener atenuacion. Aun me queda determinar si la causa es la frecuencia de corte, o la otra causa probable es que a partir de ciertos valores de prescaler, el generador saltea muestras de las 256 que hay en RAM. Dicho salteo casi seguro esta tomando samples de valores mas chicos, provocando efecto de atenuacion. Para solucionarlo, creo que voy a tener que hacer varios "sets" de samples en ram para elegir el mas adecuado de acuerdo a la F de salida esperada.

En este momento, estoy trabajando en la interfaz de usuario, le saque el teclado (era muy voluminoso) y estoy haciendo una interfaz a traves de un potenciometro como hizo cosme en su diseño.

Tambien le implemente en el hard y soft un frecuencimetro que mide la F de salida y realiza correcciones en el prescaler para mantenerla, pero luego quite esto y lo deje como visualizacion nomas, ya que no fue necesario que haga un "lock" automatico de la frecuencia. (es estable de por si)

Eso nomas, queria no dejar de comentar como me habia ido con los cambios


----------



## cosmefulanito04 (Jun 18, 2014)

Bajate el pspice student, el LTspice o "comprá" el multisim, haceme caso.


----------



## seaarg (Jul 7, 2014)

Cosme,

Estuve avanzando en el generador de señal mio y ya casi esta para meter en gabinete.

Me meti aca para comentarte: Al mio le implemente un encoder rotativo, sacado de la ruedita del medio de un mouse. Es muy sencillo de implementar y me permitio hacer un menu para seleccion de frecuencia y muchas otras funciones muy facilmente

Era para darte la idea nomas. Un abrazo.


----------



## cosmefulanito04 (Jul 7, 2014)

Cuando lo termines, si podés subilo para ver como quedo y ver esi de la rueda que parece muy práctico.


----------



## seaarg (Jul 14, 2014)

Cosme,






Aca puse un video del funcionamiento basico, mas que nada para mostrar como funciona el menu a traves del encoder de mouse.

A mi generador le falta un poco de desarrollo aun, mas que nada en el firmware para poder considerarse completo (por ej: aun no genero nada en la salida TTL, me faltan funciones de sweep, señal almacenada, transferencias, etc) Por suerte, todo eso es soft 

En cuando a hard, este generador es a la vez frecuencimetro de su salida. Me falta poner un transistor para que siga detectando señales de baja amplitud. Tambien me falta ajustar algunos presets para que la senoidal sea perfecta (en este momento, el pico superior es apenas mas angosto que el inferior)

Por ultimo, una de las ideas, ya que tengo el encoder, es hacer una rutina de ajuste fino instantaneo. Es decir: Si yo le digo al generador que salgan 20khz y el frecuencimetro incorporado me dice que hay 19999hz a la salida, con la ruedita del mouse puedo ajustar dinamicamente el prescaler para dejar la salida en la exactitud deseada.

En cuanto a lo que se ve en el osciloscopio en este video, la señal por ahi esta un poco movediza pero es causado por defectos en el trigger (es mi osc. caserito) Lo probe con un osci comercial y la señal esta quietisima.

PD: El gabinete da asco  jaja lo hice con un stereo viejo al cual le puse un frente de aluminio cortado a las apuradas.


----------



## cosmefulanito04 (Jul 14, 2014)

Te quedó muy bueno la ruedita esa, además el sonido termina siendo la cereza del postre  . Está buena la presentación del display, sobre la amplitud tal vez sea más piola usar Vp en vez de Vpp.

Por cierto, muy interesante el OCR, ¿si promedias las muestras no evitás ese corrimiento?


----------



## seaarg (Jul 14, 2014)

Gracias cosme. Quiero destacar que no hubiera podido hacer este DDS si no me hubieras explicado tu prescaler soft para poder hacerlo con TTLs 

Por VPP entiendo en este caso los casi 5v cuando la señal esta al 100%. Lo que hago en soft es antes de mandar las muestras de ROM a la RAM, las multiplico por 0.6 (en el caso 60%). Sabiendo que la salida ideal 100% es 5vpp, si multiplique por 0.5 tendria que decir en el display 2.5vpp.

Uso vpp porque deja de importar el offset. Usando Vp importaria? No me doy cuenta la ventaja de uno a otro salvo que uno es la mitad (leyendo aca: http://mathforum.org/library/drmath/view/56347.html)

Sobre el OCR, es el que termine desarrollando donde el usuario del foro palurdo me ayudo enormemente con las matematicas. Promediar muestras es algo que hace en pantalla solamente, si se quiere, asi que el trigger no es afectado. Como el trigger es un operacional como comparador y no esta bien diseñado, los pequeños picos de la señal hacen un pequeño jitter. No le di mas bola porque estoy en proceso de hacerme el osciloscopio nuevo, con mas memoria, mas velocidad, trigger digital, etc. usando todo lo que aprendi haciendo este.

Si el tema del osci te interesa, me gustaria mucho armar un thread juntos al respecto para que debatamos ideas. Por el momento tengo un diseño de captura digital de 1 canal que anda muy bien (en los papeles) y justamente estaba viendo de expandirlo a 2 canales sin necesidad de meter otro ADC+memoria. Me vendrian muy bien tus conocimientos sobre llaves analogicas con mosfet para esto  Vos decime y yo armo un thread posteando lo que tengo.

Por ultimo, y volviendo al tema, este generador DDS funcionado es exactamente el mismo esquematico que puse varios posts atras, solamente que le removi el teclado (reemplazo por encoder y 2 botones), cambie valores del filtro segun tus sugerencias, y por ultimo, meti la señal analogica en la entrada de timer del PIC para que este haga de frecuencimetro.

Por ahora, si otra persona quiere un DDS sugiero que haga el de cosme que es mas sencillo y efectivo. Con el mio se llega a mas frecuencia (hasta ahora ~1.5mhz los genera sin dramas) pero falta desarrollar un poco mas el soft y actualizar los esquematicos. Si alguien quiere, solo pidalo y armo un ZIP con el proyecto en proteus + firmware + PCB (desactualizada pero usable) para que hagan el suyo propio a partir de eso, con la aclaracion del caso de que no es "armar y enchufar"


----------



## solhaz (Ene 28, 2016)

muy buen aporte!, me podrias explicar de nuevo como variar la frecuencia de la señal de salida, fue algo que no entendi.

Arme un circuito sencillo para probar, use un pic 18f4550 a 20Mhz y el circuito r2r.
Use un bucle "for" para arrojar (a lo bruto) el vector por el puerto c para ver com funcionaba, se ve muy bien la señal. Genera una señal alrededor de 2kHz

Entiendo que esta parte debiera estar los mas optimizado posible para obtener la maxima velocidad. Lo que no entendi es como le haces para variar la señal de salida, de un minimo (1Hz) hasta el maximo posible.

Gracias!


----------



## cosmefulanito04 (Ene 28, 2016)

En el mensaje #10 más o menos dí una explicación detallada sobre el asunto:

https://www.forosdeelectronica.com/f23/aporte-generador-senales-dds-106851/#post879547

Si no entendés algo de esa explicación avisame y lo vemos (al principio cuesta verlo, pero no es difícil).

Saludos.


----------



## solhaz (Feb 3, 2016)

Disculpa aún sigo sin entenderlo al 100.

Ya leí varias veces el mensaje #10 y hay una línea en un párrafo que no entiendo (el párrafo más importante), parece ser que falta una palabra, en la tercera línea:



> Entonces de esos 24 bits, solo te quedás con los últimos 8 bits más significativos y los usás como índice para el vector de la señal. El truco está en que ese vector de datos que tendrá la señal almacenada sea de 256 elementos (muestras) para que de esta forma con 8 bits puedas fácilmente esos elementos y cuando haya desborde en el índice (es decir el acumulador de 24bits desborde), vuelva al primer elemento (o muestra) del vector de datos. Fijate que de esta forma armas una cola circular de datos que siempre se repite al paso de la frecuencia que vos elegistes.




Según lo que entiendo (hasta ahora) es que tienes que jugar con los delays y la frecuencia de muestreo de los datos almacenados; por favor corrigeme si estoy mal.

Gracias!


----------



## cosmefulanito04 (Feb 3, 2016)

Seria así:



			
				cosmefulanito04 dijo:
			
		

> Entonces de esos 24 bits, solo te quedás con los últimos 8 bits más significativos y los usás como índice para el vector de la señal. El truco está en que ese vector de datos que tendrá la señal almacenada sea de 256 elementos (muestras) para que de esta forma con 8 bits puedas fácilmente *indexar* esos elementos *(es decir, usar un índice para seleccionar un elemento)* y cuando haya desborde en el índice (es decir el acumulador de 24bits desborde), vuelva al primer elemento (o muestra) del vector de datos. Fijate que de esta forma armas una cola circular de datos que siempre se repite al paso de la frecuencia que vos elegistes.



Agregando más casos del ejemplo del mensaje, si quisiera 100Hz con una resolución de 0,105...Hz, debo ir aumentando el acumulador en 943 cuentas por c/vuelta que dá el loop, esto quiere decir que antes de la vuelta 70 el acumulador será demasiado bajo para que los últimos 8 bits modifiquen el índice:

Vuelta 69= 943*69= 65067 = 0x*00*FE2B (Hexa)= *0000 0000* 1111 1110 0010 1011 (binario) => El índice sigue siendo 0.

Vuelta 70= 943*70= 66010 = 0x*01*01DA (Hexa)= *0000 0001* 0000 0001 1101 1010 (binario) => El índice ahora es 1 (salen de los 8 bits más significativos).

....

Vuelta 17791= 943*17791 = 16776913 =  0x*FF*FED1 (Hexa)= *1111 1111* 1111 1110 1101 0001 => El índice ahora es 255.

Vuelta 17792= 943*17792 = 16777856 =  0x1*00*0280 (Hexa)= 1 *0000 0000* 0000 0010 1000 0000 => Como ahora hay desborde (rojo), debido a que el acumulador es de solo 24 bits, se vuelve al índice 0 y volvemos al primer elemento de la señal.

Así, se repite indefinidamente.

Más información en el _mensaje #32_.


----------



## seaarg (Feb 3, 2016)

Te escribo de la forma que yo termine entendiendolo:

Para simplificar, voy a escribir con valores decimales 0-255 y solo a 2 bytes (16 bits en vez de 24).

En memoria, hay almacenada una senoidal de 256 samples. Accedes a cada sample a traves del indice del vector, digamos.

El acumulador hace la operacion: ACUM = ACUM + PASO

Supongamos un acumulador y un paso de 16 bits

1- Valor inicial del acumulador: cero por lo tanto:

Byte Alto = 0
Byte Bajo = 0

2- Valor de paso: 128 en mi ejemplo

3- Entonces: 0 + 128 = 128

Byte Alto = 0
Byte Bajo = 128

4- Luego: 128 + 128 = 256

Byte Alto = 1
Byte Bajo = 0

5- Mas: 256 + 128 = 384

Byte Alto = 1
Byte Bajo = 128

Si te fijas, entre el paso 3 y el paso 5, el byte alto ha aumentado en 1 su valor (y se ha repetido en el paso 4 y 5)

Como usamos el byte alto como indice del vector de la señal en memoria, va a usar:

- Paso numero 3: sample numero 0
- Paso numero 4: sample numero 1
- Paso numero 5: sample numero 1

En el paso numero 6 seguiria acumulando por lo que usaria el sample numero 2 y asi sucesivamente.

De lo dicho, se concluye que el valor de PASO representa el prescaler que se utiliza para variar la frecuencia de salida de la señal.

Probalo vos con otros numeros y lo vas a ver claramente. Recordar que en este generador el acumulador no es de 16 bits sino de 24, proveyendo mucha mas resolucion.


----------



## cornel065 (Feb 28, 2016)

saludos he revisado paso a paso este exelente post y e logrado hacer las simulaciones gracias al programa que realizo seaarg tomado de cosmefulanito04 muy interesante, en las siguientes imagenes muesto la señal a una frecuencia de 200khz, ahora no se si esta correcto solo faltaria pasarlo por un filtro, pretendo realizar uno pero solo hasta esta frecuencia y con  sweep, observando el programa no veo la forma de salir de la rutina buclesenal, mi pregunta es el se queda colgado en este ciclo a se puede salir de el para generar otra señal, no entiendo eso jajaja
seaarg puedes colgar el firmware y la simulacion proteus para comparar gracias


----------



## seaarg (Feb 29, 2016)

cornel,

podrias indicarme con un link el mensaje o simulacion al que haces referencia? Te lo pregunto porque el generador mio no es basado en micro sino en compuertas logicas.

Si hice algunas pruebas en CCS C con el micro 18F2550 pero eran pruebas basicas, sin ningun tipo de control ni salidas de bucle ni nada de eso.

A proposito: no se si soy yo, pero no logro ampliar la imagen que pusiste

@cosmefulanito:

En este momento estoy abocado al desarrollo de mi OCR basado en fpga (200+200msps) pero cuando lo termine, el proximo proyecto es un DDS basado en CPLD o FPGA. Te imaginas lo que se podria lograr con el acumulador funcionando a 200mhz o mas? La fpga que estoy usando tiene PLLs para generar un clock (que probe) de hasta 400mhz (quiza mas) con un oscilador de 50mhz


----------



## cosmefulanito04 (Feb 29, 2016)

seaarg dijo:


> @cosmefulanito:
> 
> En este momento estoy abocado al desarrollo de mi OCR basado en fpga (200+200msps) pero cuando lo termine, el proximo proyecto es un DDS basado en CPLD o FPGA. Te imaginas lo que se podria lograr con el acumulador funcionando a 200mhz o mas? La fpga que estoy usando tiene PLLs para generar un clock (que probe) de hasta 400mhz (quiza mas) con un oscilador de 50mhz



Es el camino, con una CPLD tenés quepoder hacer magia. Le colocás un uC simple para la interfaz con el usuario y para cambiar el acumulador y ya. De paso hacé una comunicación I2C o algo similar entre uC y CPLD/FPGA.


----------



## cornel065 (Feb 29, 2016)

hola seaarg, haa ok bueno he estado haciendo pruebas con el pic18f2550 realmente lo ando buscando es hacer barridos de frecuencia solo hasta 200khz pero no logro salirme del bucle donde se realiza el calculo de frecuencia. gracias


----------



## chclau (Feb 29, 2016)

seaarg dijo:


> cornel,
> 
> podrias indicarme con un link el mensaje o simulacion al que haces referencia? Te lo pregunto porque el generador mio no es basado en micro sino en compuertas logicas.
> 
> ...



No es por pincharte el globo pero lograr que toda la lógica te funcione a 200MHz (ni hablar de 400) es bastante peliagudo. No alcanza con que el reloj alcance esa frecuencia, la lógica y el timing tienen que alcanzarla también 

Pero con las tecnologías actuales, trabajar a 100MHz como primer objetivo es algo fácilmente realizable. Yo comenzaría por allí. Luego de conseguido eso, iría subiendo de a poco.

Si necesitás ayuda chiflá


----------



## seaarg (Mar 1, 2016)

@chclau: estoy de acuerdo, no es nada sencillo. Para el osciloscopio que estoy haciendo con un cyclone III mande a hacer la placa doble faz a una fabrica directamente y en base a las pruebas que estoy haciendo, alcance los 200mhz (salida de clock a los ADC) y funciona. Aun no se si "bien". Eso si, recien al empezar a escribir el vhdl me di cuenta que tendria que haber usado pines de salida especificos de clock  Igual funciona.

Aclaro que la logica interna, el nios, la memoria, etc todo funciona a 133mhz internamente. Solo hay 200mhz en la salida clock a los ADC y en la entrada de datos a los mismos (pistas de 1cm de largo max) Pude hacer un sistema con memorias fifo donde 2 conversores 8 bits a 200mhz graban en 2 sdram 16 bits a 133mhz (double buffering)

@cornel

Una idea que yo estaba manejando para ese PIC es asi: Suponte que tenes tu bucle infinito trabajando, para poder salir de el sin agregar ciclos de reloj en las instrucciones, pensaba en emplear una interrupcion (externa, cambio de estado de pin) en la cual, dentro de ella, en vez de retornar normalmente de la interrupcion, haces un salto incondicional a un NOP que este FUERA del bucle, previamente limpiando stack y cualquier otra cosa que puedas necesitar.


----------



## djnanno (Ago 25, 2016)

cosmefulanito04 dijo:


> *Subo los siguientes archivos:*
> 
> 
> *Generador de Señal - Proyecto Altium:* se encuentra el esquemático y el PCB del generador.
> ...



Hola! me encantó el proyecto y estoy pensando seriamente en realizar uno igual al tuyo. La duda que me surge es con que software realizaste el código y como hago para subirlo, nunca he trabajdo con este modelo de micro. Muchas gracias!


----------



## cosmefulanito04 (Ago 25, 2016)

Para programar usé el Avr Studio 5, ahora creo que está disponible el 6. Lo podés bajar gratuitamente de la página de Atmel.

Para programar el uC (microcontrolador), usé el ponyprog con un programador muy sencillo que requiere un puerto serie. Acá podés encontrarlo:

http://electronics-diy.com/avr_programmer.php

El micro, para no meterse con SMD, podés conseguirlo en DIP, pero no sé si tenés exáctamente los mismo puertos.


----------



## djnanno (Ene 17, 2017)

Buenas cosme, te hago una consulta. Realicé nuevamente todo el proyecto de cero para construirlo con el micro que conseguí (Atmega32 PDIP) el hardware por suerte quedó bien pero me encuentro con un problema al generar la señal. Del puerto C utilizado al igual que vos para el conversor D/A solo obtengo salida de algunos bits, casualmente los dos bits menos significativos y los dos mas significativos funcionan pero los 4 del medio no. Quedando Bit 2 en 1 ;Bit 3 en 1 ;Bit 4 en cero y Bit 5 en 1. Alguna idea de lo que puede estar pasando?


----------



## cosmefulanito04 (Ene 17, 2017)

Si no me equivoco, es porque al programar los fuses, no destildaste la opción de JTAG, entonces los puertos:

PC5 (TDI)
PC4 (TDO)
PC3 (TMS)
PC2 (TCK)

El control no lo tiene el soft, sino el JTAG. Desactivalo y deberían funcionar.


----------



## djnanno (Ene 17, 2017)

Ahh ahora me fijo, si debe ser eso. Gracias!

Solucionado eso, no tenia ni idea de dicha opcion. Ahora el tema es este la generacion sale deformada. El orden de los bits del puerto es el mismo que el de tu diseño y el R2R esta re contra revisado.

Alguna idea? Muchas gracias por tu ayuda!


----------



## cosmefulanito04 (Ene 17, 2017)

¿En que sentido te sale deformado? ¿La frecuencia es relativamente baja? ¿Cristal y fuses configurados para trabajar a 16MHz?


----------



## djnanno (Ene 17, 2017)

cosmefulanito04 dijo:


> ¿En que sentido te sale deformado? ¿La frecuencia es relativamente baja? ¿Cristal y fuses configurados para trabajar a 16MHz?



Senoidal:


Diente de sierra +:


Triangular:



Fuses Leidos con AVRDUDES
          Low: 0xEF
          High: 0xC9
          E:     0xFF ==> Desconozco que es.
          LB: 0x3F

El codigo esta tal cual el tuyo solo cambiado de dispositivo a atmega32.

Fotos de Salida del conversor D/A


----------



## juanma2468 (Ene 17, 2017)

El el senoidal pareciera que les estuviera restando/sumando una continua (valor) en el recorrido de la tabla de valores. En la triangular invierte cada una por medio, como si cambiara de mendiente de positiva a negativa. mmmmm


----------



## cosmefulanito04 (Ene 17, 2017)

Pareciera como si los niveles bajos tuvieran el mismo peso que los altos.

Te tiro dos alternativas, quitá el amplificador de la salida del DAC y:

Alternativa 1- Con 7 cables conectados al zócalo del uC del puerto C, llevalos a un protoboard y juga con los niveles de los bits, probá con todos a GND, menos 1 que vaya a 5V. Con un tester deberías ir midiendo una tensión continua en la salida del DAC.

Alternativa 2- Esperame mañana a la tardecita que te hago un programa que haga esa secuencia de los bits para ver la salida del DAC.

En ambos casos deberías comprobar que:

VDAC=(2^n)*5V/256

Donde n es el número de bit (0 a 7) que está a 5V y el resto a GND. 

Ejemplo bit 7 => VDAC=(2^7)*5/256=2,5V
Ejemplo bit 6 => VDAC=(2^6)*5/256=1,25V
... etc


----------



## djnanno (Ene 17, 2017)

cosmefulanito04 dijo:


> Pareciera como si los niveles bajos tuvieran el mismo peso que los altos.
> 
> Te tiro dos alternativas, quitá el amplificador de la salida del DAC y:
> 
> ...



 Si te espero, si no es mucha molestia. Voy a revisar una vez mas los valores de la red R2R y buscar errores en el pcb pero no creo que venga por ahi. Tambien lo realice con altium y el esquemático esta bien, sería raro que este mal el impreso.

Quedan dos opciones que el orden de los bits del puerto este cambiado "magicamente" o en el impreso. Veré y Veré el impreso


----------



## cosmefulanito04 (Ene 17, 2017)

Dale, mañana te subo un programa que te haga la secuencia del puerto C.

Por otro lado, subí el esquemático en PDF, usá el smart pdf.

PD: verificá que el puerto 7 no esté en corto con 6 o que siempre esté a 5V, usá el OCR con una señal para ver eso.


----------



## djnanno (Ene 17, 2017)

Cosme, esta solucionado esto. Resulta que cuando estaba verificando lo anterior desolde parte de la R2R y al volver soldarla quedó un falso contacto. Ya genera sin problema en estos dias termino de arreglar otros detalles y lo subo por si alguien quiere hacerlo con este encapsulado!

Por otro lado debido al layout del pcb con este encapsulado me vi forzado a cambiar el puerto de los led y del Potenciómetro. Quedando de la siguiente manera:


En vez de tu propuesta original:


En que secciones debo modificar el código para que funcionen los led y el pote correctamente?

Gracias por la buena onda!


----------



## cosmefulanito04 (Ene 20, 2017)

Si no me equivoco (si, el soft lo hice yo, pero puede fallar ), tenés que hacer las siguientes modificaciones:

1- En el archivo "Conversor_ADC.c", al principio de todo reemplazar:


```
//----------------- Señales Analógicas a convertir ---------//
#define CURSOR		PA7
//----------------- Señales Analógicas a convertir ---------//
```

Por...


```
//----------------- Señales Analógicas a convertir ---------//
#define CURSOR		PA0
//----------------- Señales Analógicas a convertir ---------//
```

2- En el archivo "defines.c", reemplazar:


```
/*	PUERTO SALIDA TTL		*/

#define PORT_SALIDA_TTL		PORTA
#define DDR_PORT_SALIDA_TTL	DDRA
#define PIN_PORT_SALIDA_TTL	PINA
#define BIT_SALIDA_TTL		PA4

/*	PUERTO SALIDA LEDS		*/

#define PORT_SALIDA_LED_TTL		PORTA
#define DDR_PORT_SALIDA_LED_TTL	DDRA
#define PIN_PORT_SALIDA_LED_TTL	PINA
#define BIT_LED_ANALOG			PA0
#define BIT_LED_TTL				PA1
```

Por...


```
/*	PUERTO SALIDA TTL		*/

#define PORT_SALIDA_TTL		PORTA
#define DDR_PORT_SALIDA_TTL	DDRA
#define PIN_PORT_SALIDA_TTL	PINA
#define BIT_SALIDA_TTL		PA3

/*	PUERTO SALIDA LEDS		*/

#define PORT_SALIDA_LED_TTL		PORTD
#define DDR_PORT_SALIDA_LED_TTL	DDRD
#define PIN_PORT_SALIDA_LED_TTL	PIND
#define BIT_LED_ANALOG			PD4
#define BIT_LED_TTL				PD5
```

3- En el archivo "main.c", reemplazar:


```
configura_puertos((1<<BIT_SALIDA_TTL)|(1<<BIT_LED_TTL)|(1<<BIT_LED_ANALOG),0x00,0xff,0x00);
```

Por...


```
configura_puertos((1<<BIT_SALIDA_TTL),0x00,0xff,(1<<BIT_LED_TTL)|(1<<BIT_LED_ANALOG));
```

Creo no olvidarme de nada, cualquier cosa avisá. 

Saludos.


----------



## djnanno (Ene 31, 2017)

Genial Cosme, solucionado todo. Exceptuando todo menos el pote lo solucione un rato despues de consultarte, el pote lo tenía bien configurado pero el ruido me afectaba muchisimo. Me fue muy util tu ayuda para confirmar que esta todo ok!
Por otro lado no logro hacer correr el software de java, instale las ultimas versiones de java x64 y x86. Coloqué las librerías adjuntas en el soft en ambas carpetas jre y no se que mas se puede hacer.

Algun centro de que mas hacer? Nunca he ejecutado un .jar. Muchas gracias


----------



## cosmefulanito04 (Ene 31, 2017)

Desde Windows 64 bits los pasos son los siguientes:

1- Abrir una ventana de comandos (Inicio -> escribir 'cmd' -> enter).
2- Escribir el siguiente comando: java -version (te tira un diálogo con la versión).
3- Ir a "...Archivos de Programas\Java\jre VERSION del paso 2" y copiar los archivos del RxTx64:


> Copy RXTXcomm.jar ---> <JAVA_HOME>\jre\lib\ext
> Copy rxtxSerial.dll ---> <JAVA_HOME>\jre\bin
> Copy rxtxParallel.dll ---> <JAVA_HOME>\jre\bin


4- Copiar la carpeta "Generador de Señal" en la ubicación deseada.
5- En esa carpeta, manteniendo apretado el shift y tocando el botón derecho seleccionar "Abrir Ventana de comandos aquí".
6- Ejecutar el programa con el siguiente comando:java -jar Generador_de_Senial.jar

Se que hay una forma de hacer un .bat para que ejecute directamente desde el explorador de windows y evitar la línea de comandos.


----------

