SDCC: Driver para ADC PIC16f87x.

Publicar nuevo tema   Responder al tema

Ver el tema anterior Ver el tema siguiente Ir abajo

SDCC: Driver para ADC PIC16f87x.

Mensaje  arcachofo el Miér 3 Dic 2008 - 11:07

Todavía un primer "boceto" hecho a partir del adc para pic18f* pero simplificado... aunque en realidad las funciones son bastante diferentes, no lo he probado, pero ahí vá:


EDITO:
Arreglados algunos fallos y probado que funciona en un pic16f876a, tanto el archivo para descargar como el código mostrado han sido actualizados.
Faltaría confirmar que funciona en otros pics y sinó ver como solucionarlo.


Archivo para descargar: adc_sdcc_sgr.h

Y aquí se puede echar un ojo al código:

Código:

/*****************************************************************************
******************************************************************************
****                                                                      ****
****                    --- DRIVER ADC para PIC16F ---                  ****
****                                                                      ****
******************************************************************************
******************************************************************************

                            FUNCIONES DISPONIBLES:
------------------------------------------------------------------------------

adc_init( FOSC_32, A5_R0, INT_OFF); iniciar módulo ADC, donde:

FOSC_32:
Frecuencia de adc, en este caso 1/32 de la del oscilador del pic

A5_R0:
Definir entradas analógicas y Vref, en este caso todas las 5 del pic16f876a como analógicas y ninguna como Vref, Vref serán Vdd y GND (ver las posibles combinaciones en el driver y datasheet del pic a usar.

INT_OFF:
Interrupciones ADC deshabilitadas, la otra posiblidad es INT_ON.


adc_read(1); Leer canal ADC.

adc_close(); cerrar módulo ADC.

******************************************************************************
*****************************************************************************/

/* flag de interrupciones on/off */
#define INT_OFF   0x00
#define INT_ON   0x01

/* frequencia de oscillador */
#define FOSC_2   0x00
#define FOSC_4   0x04
#define FOSC_8   0x01
#define FOSC_16   0x05
#define FOSC_32   0x02
#define FOSC_64   0x06
#define FOSC_RC   0x07

/* configuracion de entradas y Vref(PCFG en ADCON1) */
#define A8_R0   0x00
#define A7_R1   0x01
#define A5_R0   0x02
#define A4_R1   0x03
#define A3_R0   0x04
#define A2_R1   0x05
#define A0_R0   0x06
#define A6_R2   0x08
#define A6_R0   0x09
#define A5_R1   0x0a
#define A4_R2   0x0b
#define A3_R2   0x0c
#define A2_R2   0x0d
#define A1_R0   0x0e
#define A1_R2   0x0f


void adc_init(unsigned char fosc, unsigned char pcfg, unsigned char config)
{
    ADCON0 = 0;
   ADCON0 = (fosc & 0x03) << 6;         // establecer frecuencia
   ADCON1 = (pcfg & 0x0F) | 0x080;         // establecer entradas

   if (fosc & 0x04)
      ADCS2 = 1;

   if (config & INT_ON)             // establecer interrupciones
   {
      ADIF = 0;
      ADIE = 1;
      PEIE = 1;
   }
   ADON = 1;               // habilitar módulo ADC
}


void adc_close(void)               // deshabilitar módulo ADC
{
   ADON = 0;
   ADIE = 0;
}


unsigned int adc_read(unsigned char canal)
{
   unsigned int valor;
   ADCON0 &= 0xC7;            // borrar anteriores selecciones
   ADCON0 |= (canal & 0x07) << 3;      // establecer canal seleccionado
   GO = 1;               // iniciar conversión
   while (GO);            // esperar que termine
   valor = ADRESH << 8 | ADRESL;      // leer valor
   return valor;            
}



Última edición por arcachofo el Vie 26 Dic 2008 - 1:35, editado 1 vez

arcachofo
Participante Activo
Participante Activo

Cantidad de envíos: 90
Fecha de inscripción: 26/11/2008

Volver arriba Ir abajo

SDCC: Driver para ADC PIC16f87x. Versión en ASM

Mensaje  arcachofo el Vie 26 Dic 2008 - 1:33

Pues ahí vá una nueva versión del driver ADC, esta vez está escrita en ASM y dividiendo la función adc_read(canal) en dos: adc_open(canal) y adc_read() , así se disminuye el tamaño del ejecutable generado en caso de leer varias veces el mismo canal, también he sacado las funciones de interrupciones por adc: adc_int_on() y adc_int_off(). Dividiendo las funciones también se ahorran lineas ya que solo se insertan las que se usan.

Al estar escrito directamente en asm y todo lo optimizado que he podido, el tamaño del código generado es mucho menor, casi la mitad en comparación con el driver escrito en C.

EDITO:
Las funciones han cambiado un poco:

adc_init( fosc, channel ); //las posibles opcines las puedes ver en los #defines

ejemplo:

adc_init( FOSC_4, A2_R2 ); // fosc/4 y 2 entradas adc + 2 Vref (RA2 como - y RA3 como +)
adc_init( FOSC_64, A1_R0 ); //fosc/64 y 1 entrada adc + ninguna Vref

Para ver las distintas combinaciones de entradas adc y Vref ver el datasheet.

He separado la apertura y la lectura del canal, para ahorrar código y aumentar velocidad en caso de leer varias veces el mismo canal; tambien uso algunos #defines para disminuir instrucciones, en vez de poner el número del canal hay que ponerlo como: CH_1, CH_2, CH_3... etc.:

adc_open( CH_1 ); // abrir canal 1

adc_read(); // lee canal previamente abierto

adc_close(); //deshabilita módulo adc



Código:

/*
  This code is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2, or (at your option)
    any later version.
 
    This code is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 */

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
 
// frequencia de oscillador: bit5=_ADCON1.ADCS2, bit6=_ADCON0.ADCS0, bit7=_ADCON0.ADCS1

#define FOSC_2    0x00
#define FOSC_4    0x20
#define FOSC_8    0x40
#define FOSC_16    0x60
#define FOSC_32    0x80
#define FOSC_64    0xA0
#define FOSC_RC    0xE0

// configuracion de entradas y Vref(PCFG0-3 en _ADCON1)
#define A8_R0    0x00
#define A7_R1    0x01
#define A5_R0    0x02
#define A4_R1    0x03
#define A3_R0    0x04
#define A2_R1    0x05
#define A0_R0    0x06
#define A6_R2    0x08
#define A6_R0    0x09
#define A5_R1    0x0a
#define A4_R2    0x0b
#define A3_R2    0x0c
#define A2_R2    0x0d
#define A1_R0    0x0e
#define A1_R2    0x0f
 
// selección de canal: usados bits 3-5 = ADCON0.CHS0-CHS2
 
#define CH_0  0x00
#define CH_1  0x08
#define CH_2  0x10
#define CH_3  0x18
#define CH_4  0x20
#define CH_5  0x28
#define CH_6  0x30
#define CH_7  0x38
 
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
 
void adc_init(unsigned char fosc, unsigned char channel)
{
fosc;
channel;
 
_asm

    BANKSEL STK01
    MOVWF    STK01            ;// fosc
 
// Ajustar _ADCON1
    RLF        STK01,W
    ANDLW    0x40            ;// selecciona bit 6 de fosc (5º rotado)
    IORWF    STK00,W            ;// Une channel y bit 6 de fosc (5º rotado)
    BANKSEL ADCON1   
    MOVWF    ADCON1            ;// Establece configuración de entradas y frecuencia (ADCS2)
    BSF        ADCON1,7        ;// Establece justificado a la derecha
 
// Ajustar _ADCON0
    MOVF    STK01,W            ;// fosc
    ANDLW    0xC0            ;// selecciona bits 6-7 de fosc (W)
    BANKSEL ADCON0;   
    MOVWF    ADCON0            ;// Establece frecuencia en _ADCON0 (ADCS0, ADCS1)
    BSF    ADCON0,0        ;// Habilita módulo ADC

_endasm;
}

 
void adc_open (unsigned char channel)
{
channel;

_asm

    BANKSEL ADCON0
    BCF        ADCON0,3        ;// Borra anteriores selecciones
    BCF        ADCON0,4
    BCF        ADCON0,5
    IORWF    ADCON0,F        ;// Establece canales abiertos (chanel en W)

_endasm;
}

 
unsigned int adc_read()
{
_asm

    BANKSEL ADCON0
    BSF        ADCON0,2        ;// Inicia conversión
    BTFSC    ADCON0,2        ;// Espera que termine
    GOTO    $-1
    BANKSEL    ADRESL
    MOVF    ADRESL,W
    BANKSEL    STK00
    MOVWF    STK00
    BANKSEL    ADRESH
    MOVF    ADRESH,W

_endasm;
}   
 
 
void adc_int_on()
{
_asm

    BANKSEL    PIE1
    BSF        PIE1,6            ;// Activa interrupciones ADC
    BSF        INTCON,6        ;// Activa inerrupciones periféricos
    BANKSEL PIR1;
    BCF        PIR1,6            ;// Borra flag interrupciones ADC

_endasm;
}

 
 
void adc_int_off()
{
_asm

    BANKSEL    PIE1
    BCF        PIE1,6            ;// Desactiva interrupciones ADC
    BANKSEL    PIR1
    BCF        PIR1,6            ;// Borra flag interrupciones ADC

_endasm;
}

 
void adc_close()                   
{   
    adc_int_off();

_asm

    BCF    ADCON0,0        ;// Deshabilita módulo ADC

_endasm;
}

.

arcachofo
Participante Activo
Participante Activo

Cantidad de envíos: 90
Fecha de inscripción: 26/11/2008

Volver arriba Ir abajo

Ver el tema anterior Ver el tema siguiente Volver arriba


Permiso de este foro:
No puedes responder a temas en este foro.