Paso 4: Código microcontrolador
He puesto mi trabajo código a Github. Tiene algunas piezas:Estas declaraciones const y #defines hacen cálculos de tiempo de compilación para que el código sólo tiene que hacer comparaciones de uint8_t simple en lugar de punto flotante que no es factible en un microcontrolador. Usando const las fuerzas al tipo del resultado uint8_t y alienta el compilador para hacer el cálculo en tiempo de compilación.
#define PWM_FREQ 62500
#define PWM_RESOLUTION (F_CPU / PWM_FREQ)
#define MIN_DUTY_CYCLE 0.40
#define MAX_DUTY_CYCLE 0.80
const uint8_t MIN_PWM_LEVEL = PWM_RESOLUTION * MIN_DUTY_CYCLE;
const uint8_t MAX_PWM_LEVEL = PWM_RESOLUTION * MAX_DUTY_CYCLE;#define VREF 1.1
#define DESIRED_VOUT 20.0
#define DIVIDER_RATIO 30.0
#define DESIRED_ADC_IN_V (DESIRED_VOUT / DIVIDER_RATIO)const DESIRED_ADC_RESULT de uint8_t = 255 * DESIRED_ADC_IN_V / VREF;
Definen algunos macros de utilidad para que el código es más fácil de seguir:
#define DUTY_CYCLE_REG OCR0B
#define ADC_ENABLE() (ADCSRA | = _BV(ADEN))
#define ADC_START_CONVERSION() (ADCSRA | = _BV(ADSC))
La función principal tiene una fase de configuración inicial donde da vuelta en los varios periféricos que necesitaremos:
int Main {}
/ * Establecer A7 como una salida. (Necesario para el PWM). */
DDRA | = _BV(DD7);
PORTA = 0;/ * Deja alimentación estabilizar... * /
_delay_ms(500);/*
* Configurar el Timer0 como un PWM rápida. Será
* - activar el pin de salida al inicio de cada ciclo
* - lo apague cuando el valor llega DUTY_CYCLE_REG
* - Envuelva a 0 cuando sale OCR0A
*/
TCCR0A = _BV(COM0B1) | _BV(WGM01) | _BV(WGM00);
OCR0A = PWM_RESOLUTION;
/ * Arrancar con ciclo de trabajo 40% y la rampa evitar la avalancha. */
DUTY_CYCLE_REG = (uint8_t)(PWM_RESOLUTION * 0.40);
/ * Establecer la fuente de reloj Timer0 que oscilador principal. Esto permite que el temporizador. */
TCCR0B = _BV(CS00) | _BV(WGM02);/*
* Encienda el ADC,
* - utilizar la referencia de tensión interna.
* - configurar ADC0 como fuente
* - izquierda-ajustar el resultado, 8 bits es suficiente para nosotros
* - desactivar el búfer de entrada digital en el pin
* - permiten el ADC.
*/
ADMUX = / * REF = * / _BV(REFS1) | / * ENTRADA = * / 0;
ADCSRA | = / * PRESCALER = 16 = 2 ^ * / 4;
ADCSRB | = / * AJUSTE IZQUIERDA * / _BV(ADLAR);
DDRA & = ~ _BV(DD0);
DIDR0 | = _BV(ADC0D);
ADC_ENABLE();
_delay_ms(1);
Entonces, simplemente lazos, leer el valor analógico desde el potenciómetro y comparándolo con su objetivo:
mientras que {} (1)
/ * Esperar el Timer0 desborde... * /
loop_until_bit_is_set (TIFR0, TOV0);
/ * Final de nuestro OFF, debe ser voltaje máximo... * /
TIFR0 | = _BV(TOV0); / * Borrar la bandera. *// * Comprobar el voltaje de salida. */
ADC_START_CONVERSION();
loop_until_bit_is_clear (ADCSRA, ADSC);
adc_result de uint8_t = ADCH;Si (adc_result < DESIRED_ADC_RESULT & &
DUTY_CYCLE_REG < MAX_PWM_LEVEL) {}
DUTY_CYCLE_REG ++;
}
else if (adc_result > DESIRED_ADC_RESULT & &
DUTY_CYCLE_REG > MIN_PWM_LEVEL) {}
DUTY_CYCLE_REG--;
}
}
}