¿Como hacer un convertidor A/D utilizando un ATmega328p enviado a un 74hc595?

Hola e buscado por la red de como realizar un conversor A/D y es facil pero lo que se me dificulta es utilizar el registro de desplazamiento, lo que deseo hacer es poder mostrar el valor de un potenciometro en dos display de 7 segmentos y convertir el valor del potenciometro a millas por hora o cualquier otro dato de forma que se puedan utilizar dos display\'s
Por ello les pido si me pueden enseñar a utilizarlo aquí coloco el código ejemplo que e utilizado, si se puede utilizar el 74hc595 si no también tengo el HCF4094B o mas conocido como el 4094.
Lo ideal es implementarlo en un micro mas pequeño un attiny84 y no en un atmega328p pero es el mas utilizado mas que nada x el arduino, se que con ese micro no seria necesario utilizar el registro de desplazamiento.
Lo que e podido ver es que se utiliza el TWI o I2C en pic\'s pero solo e visto ejemplos con eeprom y no con display\'s u envio de datos por shift register.
El software que utilizo para programar es atmel studio
De ante mano gracias x su tiempo
Código:
/*
* Conversor A/D  
* Versión: 1.0.0  
* Creado: 31/05/2016    01:59:51 
* Autor: David Alejandro
*
* Microcontrolador: ATmega328P
* Comentarios:    
*/ 

#define F_CPU 14745600
#include <avr/io.h>



void conf_puertos(void);         //Prototipos de las Funciones
void conf_adc(void);
uint16_t ReadADC(uint8_t ch);

void conf_puertos(void)
{
	DDRB = (0<<DDB5) | (0<<DDB4) | (0<<DDB3) | (0<<DDB2) | (0<<DDB1) | (0<<DDB0);
	DDRC = (0<<DDC5) | (0<<DDC4) | (0<<DDC3) | (0<<DDC2) | (0<<DDC1) | (0<<DDC0);
	DDRD = (0<<DDD7) | (0<<DDD6) | (0<<DDD5) | (0<<DDD4) | (0<<DDD3) | (0<<DDD2) | (0<<DDD1) | (0<<DDD0);
	
	PORTB = (0<<PORTB5) | (0<<PORTB4) | (0<<PORTB3) | (0<<PORTB2) | (0<<PORTB1) | (0<<PORTB0);
	PORTC = (0<<PORTC5) | (0<<PORTC4) | (0<<PORTC3) | (0<<PORTC2) | (0<<PORTC1) | (0<<PORTC0);
	PORTD = (0<<PORTD7) | (0<<PORTD6) | (0<<PORTD5) | (0<<PORTD4) | (0<<PORTD3) | (0<<PORTD2) | (0<<PORTD1) | (0<<PORTD0);
}

void conf_adc(void)
{
	// Inicialización del Convertidor Analógico-Digital
	// Referencia del ADC AREF
	// Conversión del ADC a 10 bits
	// Frecuencia de Muestreo 1843200 Hz Hz
	// ADC Detenido
	// Entradas Digitales en  ADC0D On, ADC1D On, ADC2D On, ADC3D On, ADC4D On, ADC5D On.
	
	ADMUX = (0<<REFS1) | (0<<REFS0) | (0<<ADLAR) | (0<<MUX3) | (0<<MUX2) | (0<<MUX1) | (0<<MUX0);
	ADCSRA = (1<<ADEN) | (0<<ADSC) | (0<<ADATE) | (0<<ADIF) | (0<<ADIE) | (0<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
	ADCSRB = (0<<ACME) | (0<<ADTS2) | (0<<ADTS1) | (0<<ADTS0);
	DIDR0 = (0<<ADC5D) | (0<<ADC4D) | (0<<ADC3D) | (0<<ADC2D) | (0<<ADC1D) | (0<<ADC0D);
}


uint16_t ReadADC(uint8_t ch)
{
	ch&=0b00000101;                      // Limita la entrada a 6 canales ADC (ADC0-ADC5)
	ADMUX = (ADMUX & 0xF0)|ch;           // Asigna el valor del canal ADC denotador por ch
	ADCSRA|=(1<<ADSC);                   // Inicia la conversión
	While((ADCSRA)&(1<<ADSC));           // Espera hasta que a conversión esté completa
	Return(ADC);                         // Rgresa el valor del ADC a 10 bits
}




int main(void)
{
	conf_puertos();
	conf_adc();


    while(1)
    {
        //TODO:: Please write your application code 
    }
} 

y para el TWI tengo el siguiente codigo pero no entiendo como funciona y como poder enviar los datos del potenciometro se utiliza un attiny85
/*
 * Counter.cpp
 *
 * Created: 12.04.2013 14:12:59
 *  Author: Jopi
 */ 


#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h> 

#define LATCH_PIN PB4
#define SHIFT_PIN PB1
#define DATA_PIN PB0

#define BUTTON_PIN PB2 //PB2 is INT0

uint8_t digits[10] = {
  0b00111111,
	0b00001100,
	0b01101011,
	0b01101101,
	0b01011100,
	0b01110101,
	0b01110111,
	0b00101100,
	0b01111111,
	0b01111101
	
};

uint8_t rightDigit = 0;
uint8_t leftDigit = 0;

void latch() {
		//Set the Latch clock pin to low
		PORTB &= ~(1<<LATCH_PIN);	
		//Set latch pin back to high -> shift register will copy buffer to latch register
		PORTB |= 1<<LATCH_PIN;
}

void shiftOut(uint8_t data) {

	
	for(uint8_t i = 0; i < 8; i++) {
		bool state = !((data & (1 << i )) != 0);
		//bool state = bit_is_clear(PINB, 1);
		
		if(state) {
			//set high signal
			PORTB |= 1 << DATA_PIN;
			
		} else { //low or different than 1
		//set low signal
			PORTB &= ~(1<< DATA_PIN);
		}
	        
		//pulse shift clock pin -> shift register will shift the current bit in
		PORTB |= 1 << SHIFT_PIN;
		PORTB &= ~(1<< SHIFT_PIN);
	}

	
	return;
}

void incrementCounter(){
	if(rightDigit == 9) { //check if left digit has to be incremented
		if(leftDigit == 9) { //99; reset
			leftDigit = 0;
			rightDigit = -1; //will be set to 0 later by incrementing
		} else { //left digit < 9
			leftDigit += 1;
			rightDigit = -1; //will be set to 0 later by incrementing
		}
	}	
	
	rightDigit += 1;
}

void shiftCounter() {
	shiftOut(digits[rightDigit]);
	shiftOut(digits[leftDigit]);
	latch();
}

int main(void)
{
	//Set PB0 to Output
	DDRB = 0b11; //PB0-1 Output
	DDRB |= (1 << LATCH_PIN); //Set Latch Pin to Output
	
	//Set Button Pin to Input & HIGH
	DDRB &= ~(1 << BUTTON_PIN);
	PORTB |= 1 << BUTTON_PIN;
	
	//Set Interrupt for PB2 (INT0)
	//Falling Edge:
	MCUCR |= 1 << ISC01; //ISC01 to 1
	MCUCR &= ~(1 << ISC00); //ISC00 to 0
	
	//Activate Interrupt INT0
	GIMSK |= 1 << INT0;
	
	//Activate Interrupts
	sei();
	
	
	//int out[8] = {0, 0, 0, 0, 0, 0, 0, 1}; //Last bit equals Q0, which won\'t be used for this project
	
    while(1)
    {
		//for(uint8_t i = 0; i < 100; i++) { // 0 - 99
			//incrementCounter();
			//
			//shiftCounter();
//
			//_delay_ms(500);	
		//}	
		
			
    }
}

ISR(INT0_vect) {
	incrementCounter();
	shiftCounter();
} ...
 
Última edición por un moderador:
el registro opera mas como SPI que como I2C, el latch vendría siendo lo que CS en el SPI, a diferencia de que se manda el dato primero y luego solo se pulsa sobre el latch para volcar los datos al registro.
 
Última edición:
Calcular las millas por hora por medio de valor que se obtenga de un potenciómetro, no se me hace la mejor forma.
Si lo que quieres medir son las revoluciones por minuto, es más conveniente que midas los pulsos.
De otra forma, será muy relativa la conversión y lectura del potenciómetro, con las R,P.M reales.

Ahora bien, si lo que te interesa es el envío de datos a un registro de desplazamiento, hace un tiempo hice un contador con 74HC595, que cuenta hasta 9999 y obviamente se puede expandir.
Es para PIC, pero como está escrito en C, la base te puede servir.
Es algo similar al programa que subiste, pero sin ADC y el conteo lo hice con pulsadores.

Aquí adjunto el programa y la simulación.
 

Adjuntos

  • 16F628A Contador 74HC595.rar
    27.4 KB · Visitas: 12
Última edición:
es que el registro de corrimiento es eso un registro de 8 bits

es decir se comporta como un puerto

la idea es hacer una rutina de escritura ya sea SPI o cualquier metodo

algo asi

void escribe(char letra)
{
//mi codigo usando SPI
}



y en el main

se escribe como si se tratara de un puerto no se como un puerto B algo asi:

char numero =74;

escribe(numero);


ahora puedes meter tu codigo para poder hacer funcionar un display usando el seudo puerto.

claro el codigo que hagas ya no tiene nada que ver con el registro de corrimiento, eso es muy aparte
 
Muchas gracias por la ayuda creí que ya estaba pero no me funciona en avr en pic ya me funciono muy bien.
El código que hice es el siguiente
 

Adjuntos

  • SPI.rar
    474 bytes · Visitas: 9
  • 4094.rar
    14.3 KB · Visitas: 9
Ahora bien, si lo que te interesa es el envío de datos a un registro de desplazamiento, hace un tiempo hice un contador con 74HC595, que cuenta hasta 9999 y obviamente se puede expandir.
Es para PIC, pero como está escrito en C, la base te puede servir.
Es algo similar al programa que subiste, pero sin ADC y el conteo lo hice con pulsadores.

Aquí adjunto el programa y la simulación.
Hola muchas gracias por la ayuda, ya pude realizar el programa en avr pero tengo un inconveniente la velocidad de los datos es demaciado rapido y no se ve pero queria poner lo siguiente
si el valor de x y z se mantiene
entonces vuelve al inicio hasta que cambie
si cambia muestra el nuevo dato.

Gracias por tu tiempo
 

Adjuntos

  • Rpm Control.rar
    1.8 KB · Visitas: 7
  • 4094.rar
    16.3 KB · Visitas: 5
También puedes hacer un convertidor A/D de 1bit Sigma-Delta y hacer la decimación y el submuestreo digitalmente para incrementar la resolución. Si no necesitas mucha velocidad de muestreo cualquier MCU con unos cuantos componentes pasivos te puede ser útil.
 
También puedes hacer un convertidor A/D de 1bit Sigma-Delta y hacer la decimación y el submuestreo digitalmente para incrementar la resolución. Si no necesitas mucha velocidad de muestreo cualquier MCU con unos cuantos componentes pasivos te puede ser útil.
Hoal gracias por el aporte aunque no se trata de la velocidad del conversor si no de la velocidad del ciclo lo que realmente quiero es que el valor quede estático y no se este modificando.
Aun sin el adc se tiene ese parpadeo aunque si es un poco mas visible ya en fisico
 
Hola. Lo que creo sucede es que como tu registro de desplazamiento se va actualizando cada que entra un bit, el efecto acumulado del recorrido de los 8 bits causa fantasmas en el display. Para evitarlo te sugiero dos alternativas:

1-Utilizar en lugar del 4094 al 74HC595 como comentaste en un inicio, este registro serie trae una señal extra de reloj que sirve para actualizar de "jalón" los 8 bits que tiene el registro de desplazamiento. Me explico, internamente tiene dos registros, uno de desplazamiento y un latch, una señal de reloj ingresa los datos serie como sucede con el 4094; pero estos datos no se pasan a las salidas sino hasta que se da un pulso en el reloj del latch
74HC595_arduino.jpg


Mediante esta forma utilizas una línea más del micro para el otro reloj pero te evitas el problema de los fantasmas en los displays.

2- La otra alternativa es controlar los comúnes de cada display y solo activarlas cuando se haya enviado por completo el dato serie desde el micro.

De cualquier manera te dejo un par de funciónes que precisamente ando utilizando en un proyecto para manejar un 74HC595 externo:

Código:
void SPI_Init(void)								
{
	SPCR = (1<<SPE)+(1<<MSTR)+(1<<SPR0)+(1<<SPIE);		//Reloj Fosc/64 =125KHz, modo maestro, habilita interrupción 
}

Esta función inicializa el puerto SPI de un ATMega8

Para enviar datos uso esta función
Código:
PORTB &= ~(1<<CKL_STO);		//Línea de captura en bajo
	SPDR = REGDESP;				//Envía datos al registro externo

y al momento de que se genera la interrupción por el envío completo de los 8 bits cierro el registro serie con esta ISR (Interrupt Service Rutine)

Código:
ISR(SPI_STC_vect)
{
	PORTB |= (1<<CKL_STO);		//Captura estado en registro externo
	PORTB &= ~(1<<CKL_STO);	
}

CKL_STO lo puedes definir como un pin extra donde desees, este servirá para guardar los datos en el latch del 595.

Saludos
 
Es verdad, descuido mío el no revisarlo, mucho mejor.
Sólo me guié por la función de envío del programa que subió

Código:
void serial_paral(unsigned char c)
{
	unsigned char i=8;
	
	do
	{i--;
		if (tst_bit(c,i))
		set_bit(PORTB,D);
		else
		clr_bit(PORTB,D);
		pulso_CLK();
	} while (i!=0);
}

:oops:
 
Es verdad, descuido mío el no revisarlo, mucho mejor.
Sólo me guié por la función de envío del programa que subió
:oops:

Así es el strobe se coloca casi al final del código al momento de enviar los dígitos, aun así cres que es mejor cambiar 74hc595 o continuo usando en 4094.
Ya que en el programa que subió D@rkbytes pudes aprecias que es con el 74hc595 y aun así se tiene ese problema de fantasmas en los display.
Código:
        unsigned char j;
	unsigned char Display[3]={block};
	Ports_Setup();
	for(j=0; j<3;j++)
	serial_paral(Display[j]);
	pulso_STB();<---------------pulso strobe o latch
 
No, ese que usas está bien, es prácticamente igual que el 595.
Ahora que reviso con más detenimiento tu código encuentro esto raro:

Código:
for(j=0; j<3;j++)
		serial_paral(Display[j]);
		pulso_STB();

Comentas que quieres mostrar los datos dos displays pero ese bucle los va a mostrar en 3 ya que el cero también cuenta.

Hola e buscado por la red de como realizar un conversor A/D y es facil pero lo que se me dificulta es utilizar el registro de desplazamiento, lo que deseo hacer es poder mostrar el valor de un potenciometro en dos display de 7 segmentos y convertir el valor del potenciometro a millas por hora o cualquier otro dato de forma que se puedan utilizar dos display\'s

:unsure:
 
SPI y usar el 74hc595 es basicamente lo mismo.

la idea es que:

limpias latch
por cada dato que envias envias junto con un clock
cuando terminas los 8 bits activas latch.

esto es un algoritmo sin usar SPI de hardware.

char _74HC595(char dato)
{
char i,dat=0;
LATCH=0;


for(i=0; i!=8 ; i++)
{


dat=dato<<i;
dat=dat&0b10000000;

if(dat==0)
{
DATA=0;
}
else
{
DATA=1;
}

CLOCK=0;
CLOCK=1;
}

LATCH=1;

return dato;
}


y para usar la funcion solo haces algo asi:

char numero = 17;


_74HC595(numero); // se escribe la variable numero directo al registro de corrimiento
 
Comentas que quieres mostrar los datos dos displays pero ese bucle los va a mostrar en 3 ya que el cero también cuenta.
:unsure:

A perdona lo que pasa es que al final me dijeron que era a 3 display en todo caso esta bien. Ahora si es con el 4094 que puedo hacer para corregir los fantasmas debo cambiar algo del programa o asi como esta es correcto.

Porque estuve buscando en un libro de avr lo del spi y no se parece mucho o no encuentro el parecido.Y a que tu configuras los registros del spi.
 
Ya veo, ya veo, bueno, pero ahora ¿cómo es tu diagrama?, algo no me queda claro aún. ¿Tienes 3 registros serie externos?, o ¿cómo decides a cada cuál de ellos activarle su respectiva señal de latch?
 
Ya veo, ya veo, bueno, pero ahora ¿cómo es tu diagrama?, algo no me queda claro aún. ¿Tienes 3 registros serie externos?, o ¿cómo decides a cada cuál de ellos activarle su respectiva señal de latch?

Ok déjame explicarte como funciona el código al leer el valor adc lo guardo en una variable en todo caso si el valor y hago la conversión V = V * (35)/60; para que de esta forma me de en números enteros después si es 345 lo separo en unidad/decena/centena y los guardo en la variable y,z,x.
Código:
int main(void)
{
	Ports_setup();<------mando llamar la configuración salidas y entradas
	ADC_setup();<-------cargo el valor del adc en y,z,x
	sei();<-----habilito interrupciones
    while(1)
    {	
		unsigned char j;
		unsigned char Display[3]={y,z,x};<----aqui colocas el valor de cada display
		Ports_Setup();<-----puertos de salida entrada el registro de desplazamiento
		for(j=0; j<3;j++)<-----con este for realizas el corrimiento de los display
		serial_paral(Display[j]);<-----corrimiento de 8 bits
		pulso_STB();<----pulso de latch o strobe
	}
}
En el for es el que hace el corrimiento de cada display y hace posible mandar su correspondiente dato (y) para el primer display, (z) para el segundo y (x) para el tercero.
 
Ok, eso lo comprendo pero por eso pregunto ¿cómo es el conexionado de los registros, diplays y AVR?. Una cosa es que los registros serie estén en cascada y otra que cada uno tenga sus líneas de control por separado.

Así como lo veo en tu código envías el dato correspondiente a cada display a un sólo registro serie externo al igual que sólo usas una línea de "Strobe"
 
Como mencioné anteriormente, no programo ATmega, pero el programa que subí para el 74HC595 está en C y se puede migrar.

Ahora lo adjunto para el 4094 y compilado con BoostC Compiler, que está más apegado al C estándar.
De ésta forma será más comprensible el programa.
 

Adjuntos

  • 16F628A Contador 0 - 9999 con CD4094.rar
    47.7 KB · Visitas: 6
Atrás
Arriba