Es dañino para la luz o para mantener la carga de la batería de un automóvil.

Continúo la serie de artículos sobre la construcción de bicicletas en el campo de la administración de circuitos de energía de bajo voltaje. Esta vez hablaré sobre un dispositivo que evita la descarga profunda de la batería de un automóvil por parte de varios consumidores secundarios.


Una de las posibles consecuencias de una descarga no controlada.

Comprar el primer automóvil o motocicleta es un hito importante en la vida de cada persona, y especialmente de un ingeniero. Después de todo, ¿quién más además de las obvias ventajas de su nuevo caballo de hierro presta atención inmediata a sus desventajas no obvias? ¿Quién inmediatamente comienza a pensar en las mejoras y adiciones al estándar? Por supuesto, si se trata de un automóvil del segmento superior, e incluso de una marca "de moda", al principio puede parecer que tiene absolutamente todo. Pero, como muestra la práctica, en este caso, el tiempo refuta las primeras impresiones. Si compra un automóvil de clase económica, ¡sus manos comienzan a picar literalmente el primer día!

El deseo de "rellenar" su automóvil con varios dispositivos electrónicos auxiliares es bastante natural. Sin embargo, poco después de la implementación de todos estos planes, la vida enfrenta al propietario del automóvil con una dura realidad. Resulta que incluso los dispositivos más modernos construidos sobre la base elemental más reciente siguen ansiosos por la electricidad. Y una batería de automóvil que parece tan grande no es un reactor nuclear en absoluto, y puede "sentarse" fácilmente bajo el peso de todos estos consumidores aparentemente inofensivos en cuestión de días.

Para no avanzar más en situaciones abstractas e hipotéticas, iré directamente a mi historia. Después de comprar un automóvil, el primero fue el deseo de poner un registrador en él. Esto se hizo en el menor tiempo posible, dictado casi por completo por la velocidad de entrega del paquete desde AliExpress. Está claro que el suministro de energía regular del encendedor de cigarrillos era extremadamente inconveniente, y la grabadora rápidamente obtuvo una conexión estacionaria a la línea más cercana de la red a través de un convertidor de pulso de 12 / 5v. Y desde fue, por decirlo suavemente, no ayer, este convertidor ni siquiera era moderno, para sus propias necesidades, como resultó más tarde, consumía hasta 21 mA de corriente. Ahora, calculemos cuánto podría alimentar este convertidor solo con una batería nueva y completamente cargada con una capacidad de 60 Ah. La aritmética es extremadamente simple y decepcionante.


Entonces, en menos de cuatro meses, un convertidor que no esté cargado con nada aterrizará la batería literalmente "a cero". Si tenemos en cuenta que una batería que no está completamente nueva puede resultar fácilmente viuda, y la carga después de que el pokatushki de la ciudad esté lejos del 100%, un día lluvioso comienza fácilmente dentro de un mes con un gancho.

Y eso es todo, repito, solo un convertidor de voltaje. Sí, hoy puede comprar un convertidor que solo necesita medio miliamperio para sus propias necesidades, pero di este ejemplo solo para mostrar cuán lenta y confiable es el agua que afila una piedra, incluso sin importancia, pero actuando constantemente, el consumidor extrae energía de lo que parece ser tan enorme. batería

Vamos más allá, la grabadora en el modo de grabación FHD @ 30fps consume casi 300 mA de la fuente + 5v, que después de la conversión teniendo en cuenta la eficiencia proporciona aproximadamente 150 mA de corriente de la red a bordo. Supongamos que el convertidor se reemplaza por uno moderno, y calculamos el tiempo de descarga solo con esta corriente.


Poco más de dos semanas, pero en la práctica, diez días. Ahora se avecina la posibilidad de encender (y posiblemente cambiar la batería) después de las próximas vacaciones o viaje de negocios.

Y así me sucedió: cuando me fui de vacaciones cortas y forzadas, no pensé que en una semana más o menos una cerradura central no podría abrirme la puerta.

Muchos dirán que es su culpa, que todo debe estar desenergizado, o al menos dejar de grabar, y tendrán razón. Pero la vida es vida, y el recuerdo no es el mismo, y no siempre es posible saber de antemano cuánto dura la baja por enfermedad. Por lo tanto, la idea de un interruptor automático surgió de inmediato.

Existe, por supuesto, una opción para alimentar la grabadora desde el interruptor de encendido para que solo funcione mientras viaja, pero esta opción tampoco es muy buena, porque Si el auto choca en el estacionamiento, me gustaría tener la oportunidad de ver al culpable. Además, después de poco tiempo después de instalar la grabadora, el automóvil no contaba con varios dispositivos más, incluido un rastreador GPS oculto, que debería funcionar, si no hasta el final, al menos hasta que "casi todo" ya esté allí.

En general, durante varias semanas de reflexión pasiva, la idea de un dispositivo que debe controlar el voltaje de la red a bordo y basarse en estos datos para controlar el suministro de energía a dos grupos de consumidores: secundario (grabador, toma USB) y básico (rastreador GPS y algunos que)

¿Cómo podría hacerse esto?


Los primeros prototipos virtuales del dispositivo se "construyeron" sobre la base de los comparadores analógicos LM393N y fueron capaces de recibir todo lo que originalmente se planeó recibir del dispositivo. El esquema abstracto era algo como esto.


Aquí se usan dos comparadores para cambiar cargas. Un generador de voltaje de referencia común, dos divisores que determinan los umbrales para la operación, comparadores de flejes, dos interruptores de potencia. La unión externa del dispositivo terminado se planifica de la siguiente manera.


La clave primaria permanece encendida durante más tiempo que la secundaria, por lo que el convertidor reductor se alimenta a través de ella. Las cargas primarias están conectadas directamente al convertidor. El interruptor secundario conmuta las cargas secundarias que ya están en el circuito de + 5v en la salida del inversor.

Lo que salió al final


Parece ser todo lo que se necesita, pero, como sucede a menudo, con el pensamiento de los detalles, aparecieron ideas de implementaciones alternativas. En primer lugar, el circuito analógico contenía una montaña decente de elementos discretos que proporcionan modos de operación de comparación, y en segundo lugar, se supone que los umbrales de disparo se establecen utilizando resistencias de compensación, lo que complica la configuración y crea la probabilidad de "alejarse" de las sacudidas y el tiempo. Por lo tanto, al final, se decidió hacer hincapié en una implementación digital, que resultó ser mucho más simple tanto esquemáticamente como en configuración, al tiempo que abrió enormes oportunidades para mejorar el algoritmo de control y, lo más importante, en este contexto, resultó ser un orden de magnitud más económico en términos de consumo actual.

El controlador ATtiny13A simplemente solicitó el corazón del dispositivo, que, además de su facilidad de uso y bajo costo, todavía está disponible en un estuche DIP de tubo cálido y cálido para oldfag. Inicialmente, las capacidades de incluso un controlador tan pequeño parecían redundantes en todos los frentes, desde la cantidad de entradas / salidas hasta la cantidad de programa y RAM, sin embargo, como saben, el apetito viene con una comida. Como resultado, mirando hacia el futuro, diré que la versión final del caso resultó ser todas las conclusiones del microcircuito, y la memoria del software libre no dejó más de dos docenas de bytes.

Para medir el voltaje de la red a bordo, el microcontrolador requirió solo una entrada, que está vinculada al ADC. Dos resultados lógicos más fueron administrar a los consumidores. En primer lugar, después de la transición mental final a lo "digital", había un deseo de adaptar dos GPIO gratuitos al negocio, y la decisión no se hizo esperar. Cuando, una vez más, en el frío, el motor de arranque giró el motor con una lágrima mal oculta, la presencia de un sensor de temperatura en el circuito y el algoritmo parecían muy útiles. Como resultado, se usó el segundo ADC para medir la temperatura. Y para que el termistor consuma corriente solo cuando sea necesario, se decidió alimentarlo desde la última salida lógica restante.

Como resultado, el diagrama del dispositivo ha adquirido esa forma final.


Aquí vemos el mínimo de detalles, y entre ellos nada está sujeto a ningún tipo de "torsión". Repasemos brevemente los puntos principales.

Para la fuente de alimentación, el controlador necesita un voltaje estable de 1.8 a 5.5 V, lo que significa que debe haber un estabilizador en el circuito que disminuya el voltaje de la red a bordo al nivel requerido. Desde el punto de vista del ahorro de energía, puede parecer que hay un lugar para un convertidor reductor de pulsos exclusivamente, pero esto es solo a primera vista. El hecho es que ATtiny13A, incluso en el modo de operación con mayor consumo de energía (frecuencia 8 MHz, ejecución de código activo) no consume más de 6 mA. En este esquema, el controlador el 99% del tiempo está en modo de reposo profundo y también funciona a una frecuencia de 1.2 MHz, lo que resulta en un consumo promedio de aproximadamente menos de 15 µA. Además, aproximadamente 80 µA a las corrientes de base de los transistores de control (si ambas cargas están encendidas). Bueno, por una pequeña fracción de segundo, se activa la potencia del termistor, que agrega aproximadamente 25 microamperios a la corriente promedio. Y aquí está la respuesta a la pregunta "¿Vale la pena cargar un convertidor de pulso en aras de una carga con un consumo de no más de 120 µA? Parece no tan sencillo. Y si consideramos que estamos tratando con mediciones analógicas, definitivamente no vale la pena. Por lo tanto, se utilizó el estabilizador lineal LP2950, ​​un análogo funcional del popular 78L05, pero mucho más económico. Este convertidor puede proporcionar hasta 100 mA de corriente en la salida, mientras no consume más de 75 µA para el ser querido.

El divisor de voltaje de la red a bordo, protegido por un diodo zener y un condensador, le permite medir voltajes de hasta 15 V.

Sé que ahora una ola de críticas me golpeará por tal decisión, pero seremos objetivos. En primer lugar, no estoy desarrollando un satélite, y en segundo lugar, no existe un factor único que pueda conducir al desastre. La resistencia del hombro es alta, el diodo zener puede desviar mucha más corriente que la que puede fluir a través del divisor, incluso en el escenario más pesimista. De los pulsos de alta frecuencia, cuando el diodo zener no tiene suficiente velocidad, el condensador C2 protegerá (con una resistencia R7 crea un filtro de paso bajo con una frecuencia de corte de solo 7 Hz). D1 y R6 aseguran en cierta medida que el esquema no se caiga entre sí. Y uno no debe olvidarse de la linealidad, cualquier método de aislamiento galvánico en ese lugar hará que el cálculo teórico de las cantidades sea completamente irreal, tendremos que calibrar al menos el prototipo, pero no lo necesitamos.

La resistencia de salida del divisor es diez veces mayor que los 10 kOhm recomendados para la fuente de señal ADC, pero gracias al condensador C2 no hay problemas de medición.

En general, la impedancia de entrada de los circuitos ADC de los controladores AVR de acuerdo con la hoja de datos se declara al menos 100 megaohmios. Sin embargo, sin embargo, la misma hoja de datos recomienda utilizar fuentes con resistencia interna de hasta 10 kOhm. Por qué El punto es el principio de funcionamiento de este ADC en sí. El convertidor funciona según el principio de aproximación secuencial , y su circuito de entrada es un filtro de paso bajo de una resistencia y un condensador. La obtención de una muestra de 10 bits es iterativa, y es necesario que el condensador se cargue al voltaje medido completo durante todo el tiempo de medición. Si la impedancia de salida de la fuente es demasiado grande, el condensador continuará cargándose directamente durante el proceso de conversión y el resultado será inexacto. En nuestro caso, la capacitancia C2 es más de siete mil veces la capacidad del filtro ADC, lo que significa que cuando la carga se redistribuye entre estos condensadores cuando se encienden en el momento de la medición, el voltaje de entrada disminuirá en no más de 1/7000, que es siete veces menos que la precisión máxima de un ADC de 10 bits. Es cierto que debe tener en cuenta que este truco solo funciona para mediciones individuales con pausas significativas entre ellas, por lo que no debe "mejorar" el programa de control agregando un ciclo para varias mediciones consecutivas con un promedio del resultado.

El divisor con un termistor debido a la presencia de una fuente de energía controlada se construye utilizando las clasificaciones recomendadas. NTCLE100E3 se usa como sensor, pero no hay restricciones, puede usar cualquier termistor de aproximadamente la misma clasificación, lo principal es hacer las correcciones correspondientes a su característica en las constantes del código fuente para que el voltaje del divisor se convierta al valor de temperatura correcto.

Como teclas de control, los MOSFET de canal P de potencia de cualquier tipo se utilizan con una resistencia de canal abierto aceptable y un voltaje de fuente de drenaje máximo de al menos 30 voltios. El circuito anterior usa diferentes transistores. Esto se hace porque tienen que cambiar diferentes voltajes y el tipo de cada uno de ellos fue seleccionado para condiciones de trabajo específicas. El transistor superior debe ser de más alto voltaje, y el inferior debe, si es posible, tener una resistencia mínima de canal abierto. Pero, repito, esta decisión está dictada por el circuito de conmutación del dispositivo (ver arriba), con otra inclusión, los requisitos para el transistor inferior pueden ser diferentes.

Para controlar los interruptores de alimentación, se usa un par de transistores bipolares idénticos. Al principio puede parecer que estos transistores son superfluos, pero aquí no es tan simple. Los transistores de efecto de campo con una puerta aislada comienzan a abrirse no desde cualquier voltaje de la polaridad requerida en la puerta, sino solo después de alcanzar un cierto nivel de umbral, que aparece en las hojas de datos bajo el nombre "voltaje de umbral de puerta a fuente" y generalmente es igual a 2..4 V. Ahora vamos a solo cuenta. El circuito de salida del controlador puede formar dos niveles lógicos: "0" lógico con un voltaje que tiende a cero; y lógico "1" con voltaje que tiende a suministrar. Cuando se alimenta con 5 voltios, estos serán voltajes alrededor de 0 y 5 V, respectivamente. Como resultado, al cambiar una fuente de 12 voltios, un "0" lógico en la puerta creará una diferencia de voltaje de la puerta de la fuente de 12 - 0 = 12 voltios, el transistor de potencia está abierto. Todo parece ser normal, pero el "1" lógico con su voltaje de 5 V creará un voltaje entre 12 - 5 = 7 voltios entre la fuente y la puerta, y el transistor de potencia seguirá abierto. Por lo tanto, la señal de control de cinco voltios no puede controlar la tecla, que conmuta el voltaje por encima de 7..9 voltios. Por lo tanto, los transistores bipolares de control en realidad no funcionan tanto con las teclas de señal como con los amplificadores que elevan el voltaje de control de 5 voltios al voltaje de la red a bordo.

La resistencia en el circuito base de cada uno de los transistores de control simplemente limita la corriente de las salidas del controlador a un nivel suficiente para controlarlo. Sus clasificaciones se pueden reducir dos o tres veces sin consecuencias para el funcionamiento del circuito.

Es fácil ver que los transistores de control no estaban en el circuito analógico basado en el LM393N. La cuestión es que la etapa de salida del comparador seleccionado se construye de acuerdo con el circuito de colector abierto, es decir, su salida es simplemente la salida del colector del transistor terminal. Este principio de construcción requiere que se cuelguen piezas adicionales en el chip para crear la carga de la etapa de salida, pero, por otro lado, hace que el chip sea muy flexible. Un colector abierto permite que el comparador controle cualquier fuente de corriente aceptable, y no solo compatible con la que proporciona energía al comparador mismo.

Debo decir que limitar el voltaje umbral de un MOSFET de potencia funciona no solo hacia voltajes altos, como se mencionó anteriormente, sino también hacia los bajos. Después de todo, si el voltaje de apertura mínimo del transistor es, digamos, 4 voltios, cuando se cambia la fuente de 3.3 V, incluso conectar la puerta a tierra no creará la diferencia de voltaje deseada entre la fuente y la puerta, y el transistor permanecerá cerrado. Entonces, 5 voltios es, quizás, el voltaje mínimo que los transistores seleccionados pueden conmutar de manera confiable.

Personalización


Configurar un dispositivo es una conversación separada. Por un lado, no hay un solo elemento de sintonización en el circuito, pero por el otro, estamos lidiando con la medición de voltajes con una precisión no inferior a 0.1 V. ¿Cómo vincular todo esto? Hay dos formas El primero es usar resistencias R6, R7 y R8 con una tolerancia de al menos 1% (o mejor 0.1%). El segundo implica el uso de resistencias convencionales con medición de sus resistencias reales y corrección de coeficientes en el código fuente del programa.

El primer método es bueno para la producción en masa, pero es mucho más atractivo para nosotros no molestarnos en la búsqueda de los valores de alta precisión necesarios, así que vamos a la segunda. La resistencia se puede medir con un multímetro ordinario, su precisión aquí es suficiente. Otro objeto de medición será el voltaje del estabilizador que alimenta el circuito. El ADC del controlador puede funcionar en diferentes modos, pero por varias razones es más conveniente para nosotros usar uno en el que el resultado de la conversión digital se cuente en relación con el voltaje de suministro. Por eso es importante saberlo con la mayor precisión posible.

El cálculo es extremadamente simple y consiste en calcular el coeficiente de división del divisor resistivo y la proporción de la traducción del resultado en LSB durante la conversión de analógico a digital.


Ux es el voltaje de entrada del divisor;
Ru es la resistencia del brazo superior del divisor (al que se suministra Ux);
Rd es la resistencia del brazo inferior del divisor (que está conectado al suelo);
Uref - voltaje de referencia del ADC (es decir, voltaje de alimentación del controlador);
1024 - el número de valores discretos en la salida de un ADC de 10 bits;
LSB es el valor numérico obtenido por el programa del ADC.

Comencemos con el divisor de voltaje R6-R7. . 5.0 . 13.5 :


, , , .

, , , Ru, Ux Uref. :


R8 , R9 NTCLE100E3 0⁰C:


, R8 R9 , , , . . , R9 , 0.5 m, . , , 0.01 .

, , , . , . - , .

, , , .

Firmware


AtmelStudio ( gcc-avr 5.4.0) , hex . , .

//#define F_CPU 1200000UL //    

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

//#define DBG

#define TEMPERATURE_OVERHEAT 753 // LSB-  +50⁰C
#define TEMPERATURE_GIST     8   //    ( LSB)     
#define VOLTAGE_GIST         3   //    ( LSB)     

#define INTERVAL             WDTO_1S //     (1 )
#ifndef DBG
#define CELL_CHANGE_TIMEOUT  90  //      (  INTERVAL,   254)
#define OVERHEAT_TIMEOUT     300 //      "" (  INTERVAL)
#else
#define CELL_CHANGE_TIMEOUT  2
#define OVERHEAT_TIMEOUT     3
#endif

typedef unsigned char bool; //    
#define true  0 == 0        //     
#define false 0 != 0        //      

typedef enum {st_none = 0b00, st_primary = 0b01, st_secondary = 0b10, st_both = 0b11} t_states; //    
                                                                                                //       ,      
typedef enum {adc_temperature, adc_voltage} t_measure;                                          //   
typedef enum {move_null, move_up, move_down} t_movement;                                        //      

//    
struct t_coordidates {
  signed char row, col;
};

//       
struct t_correction {
  t_movement voltage, temperature;
};

#define CELLS_ROWS 3 //      ( )
#define CELLS_COLS 5 //      ( )

//  
const t_states CELLS[CELLS_ROWS][CELLS_COLS] = {
  {st_both, st_both,    st_both,    st_primary, st_none},
  {st_both, st_both,    st_primary, st_none,    st_none},
  {st_both, st_primary, st_none,    st_none,    st_none}
};

// LSB- ,      
const unsigned int ROWS_EDGES[CELLS_ROWS - 1] = {
  241, // 0⁰C
  157  // -10⁰C
};

// LSB- ,      
const unsigned int COLS_EDGES[CELLS_COLS - 1] = {
  864, // 13.5V
  800, // 12.5V
  787, // 12.3V
  768  // 12.0V
};

unsigned int overheat_rest_time = 0; //       ""
unsigned char cell_change_time  = 0; //      
unsigned char no_cur_cell_time  = 0; //  ,            

#define NULL_CELL (struct t_coordidates){.col = -1, .row = -1} // ,   
#define NULL_CORRECTION (struct t_correction){.voltage = move_null, .temperature = move_null} // ,   

struct t_correction moved_from = NULL_CORRECTION; //       
struct t_coordidates cur_cell  = NULL_CELL,       //      
                     next_cell = NULL_CELL;       //  -   

//  
static void init_pins() {
  DDRB |= (1 << PB0) | (1 << PB1) | (1 << PB3);     //   2 (PB3), 5 (PB0)  6 (PB1)  
  PORTB &= ~(1 << PB0) & ~(1 << PB1) & ~(1 << PB3); //      2 (PB3), 5 (PB0)  6 (PB1)
}

// /    
static void toggle_thermal_sensor(bool state) {
  if(state) {
    PORTB |= (1 << PB1);  //  state ,      6 (PB1)

    _delay_ms(5); //    
  } else {
    PORTB &= ~(1 << PB1); //  state  ,      6 (PB1)
  }
}

//   
static unsigned int measure_adc(t_measure measure) {
  if(measure == adc_temperature) {
    toggle_thermal_sensor(true); //    ,    

    ADMUX = 0b10; //      -   3 (PB4)
  } else {
    ADMUX = 0b01; //      -   7 (PB2)
  }

  ADCSRA = (1 << ADPS2) | //       = 16 (75 )
           (1 << ADIE) |  //    
           (1 << ADEN);   //  

  set_sleep_mode(SLEEP_MODE_ADC); //   "" 
  do {
    sleep_cpu(); //      ,      ,   
  } while(ADCSRA & (1 << ADSC)); //        ,  

  ADCSRA = 0; //  

  toggle_thermal_sensor(false); //     

  return ADC; //  10-  
}

//    watchdog
static void init_interrupts(void) {
  sleep_enable(); //   

  WDTCR = (1 << WDCE) | (1 << WDE); //  watchdog
  WDTCR = (1 << WDTIE) | INTERVAL; // watchdog      ,  1 

  sei(); //  
}

//          
static void toggle_loads(t_states states) {
  unsigned char port = PORTB & ~((1 << PB3) | (1 << PB0)),     //           ,   
                bits = (((states & st_primary) >> 0) << PB3) | //        
                       (((states & st_secondary) >> 1) << PB0);

  PORTB = port | bits; //    
}

//     t_coordidates
static bool cells_equal(struct t_coordidates cell1, struct t_coordidates cell2) {
  return cell1.row == cell2.row && cell1.col == cell2.col;
}

//          LSB- 
static signed char get_cell_row(unsigned int temperature) {
  signed char row = 0;

  while(row < CELLS_ROWS - 1) {          //          
    if(temperature >= ROWS_EDGES[row]) { //  temperature     ,    
      return row;
    } else {
      ++row;
    }
  }

  return CELLS_ROWS - 1; //  temperature         ,       
}

//          LSB- 
static signed char get_cell_col(unsigned int voltage) {
  signed char col = 0;

  while(col < CELLS_COLS - 1) {      //          
    if(voltage >= COLS_EDGES[col]) { //  voltage     ,    
      return col;
    } else {
      ++col;
    }
  }

  return CELLS_COLS - 1; //  voltage         ,       
}

//    ,       
static void get_row_edges(signed char row, unsigned int *upper, unsigned int *lower) {
  *upper = row > 0 ? ROWS_EDGES[row - 1] : 0xffff - TEMPERATURE_GIST; //       ,    
  *lower = row < CELLS_ROWS - 1 ? ROWS_EDGES[row] : TEMPERATURE_GIST; //       ,    
}

//    ,       
static void get_col_edges(signed char col, unsigned int *upper, unsigned int *lower) {
  *upper = col > 0 ? COLS_EDGES[col - 1] : 0xffff - VOLTAGE_GIST; //      (  )  ,    
  *lower = col < CELLS_COLS - 1 ? COLS_EDGES[col] : VOLTAGE_GIST; //      (  )  ,    
}

//    -              
static void gisteresis_correction(struct t_coordidates* new_cell, unsigned int temperature, unsigned int voltage) {
  unsigned int upper_edge, lower_edge;

  get_row_edges(cur_cell.row, &upper_edge, &lower_edge); //    
  if(new_cell->row > cur_cell.row && moved_from.temperature == move_up && temperature >= lower_edge - TEMPERATURE_GIST) {
    --new_cell->row; //   -   ,    ,        ,    
  }

  if(new_cell->row < cur_cell.row && moved_from.temperature == move_down && temperature <= upper_edge + TEMPERATURE_GIST) {
    ++new_cell->row; //   -   ,    ,        ,    
  }

  get_col_edges(cur_cell.col, &upper_edge, &lower_edge); //    
  if(new_cell->col > cur_cell.col && moved_from.voltage == move_up && voltage >= lower_edge - VOLTAGE_GIST) {
    --new_cell->col; //   -   ,     (  ),        ,    
  }

  if(new_cell->col < cur_cell.col && moved_from.voltage == move_down && voltage <= upper_edge + VOLTAGE_GIST) {
    ++new_cell->col; //   -   ,     (  ),        ,    
  }
}

//       stdlib::abs()
 static unsigned char absolute(signed char value) {
  return value >= 0 ? value : -value;
}

//      -
static void calc_movement(struct t_coordidates new_cell) {
  moved_from = NULL_CORRECTION;                                                   // -   
  if(!cells_equal(new_cell, NULL_CELL) && !cells_equal(cur_cell, NULL_CELL)) {    //         ,  -
    if(absolute(new_cell.row - cur_cell.row) == 1) {                              //      
      moved_from.temperature = new_cell.row < cur_cell.row ? move_up : move_down; //   
    }

    if(absolute(new_cell.col - cur_cell.col) == 1) {                              //      
      moved_from.voltage = new_cell.col < cur_cell.col ? move_up : move_down;     //   
    }
  }
}

//   -
static void set_next_cell(struct t_coordidates cell) {
  next_cell = cell;
  cell_change_time = 0; //    
}

//    
static void set_cur_cell(struct t_coordidates cell) {
  cur_cell = cell;
  no_cur_cell_time = 0; //        
  set_next_cell(NULL_CELL); //  -
}

// ,      
static void change_cell(struct t_coordidates new_cell) {
  if(cells_equal(new_cell, NULL_CELL)) { //         
    toggle_loads(st_none);
  } else {
    toggle_loads(CELLS[new_cell.row][new_cell.col]); //         
  }

  calc_movement(new_cell); //     
  set_cur_cell(new_cell);  //   
}

//  
static void main_proc(void) {
  unsigned int temperature, voltage; // 10- LSB-    
  struct t_coordidates cell;         //      -

  if(overheat_rest_time) { //      ""  ,          
    --overheat_rest_time;
  } else {
    temperature = measure_adc(adc_temperature); //  
    if(temperature >= TEMPERATURE_OVERHEAT) {   //      +50C,  :
      change_cell(NULL_CELL);                   //      (   )
      overheat_rest_time = OVERHEAT_TIMEOUT;    //        
    } else {
      voltage = measure_adc(adc_voltage);   //  

      cell.col = get_cell_col(voltage);     //    -  
      cell.row = get_cell_row(temperature); //    -  

      if(cells_equal(cur_cell, NULL_CELL)) { //        ,         
        change_cell(cell);
      } else {
        gisteresis_correction(&cell, temperature, voltage); //              

        if(cells_equal(cell, cur_cell)) { //   -   ,      
          set_next_cell(NULL_CELL);
          no_cur_cell_time = 0; //    ,  
        } else {
          if(no_cur_cell_time++ > CELL_CHANGE_TIMEOUT) { //    CELL_CHANGE_TIMEOUT+1        cur_cell,      
            change_cell(cell); //    ,     
          } else {
            if(cells_equal(next_cell, NULL_CELL) || !cells_equal(next_cell, cell)) { //  -       ,   
              set_next_cell(cell);
            } else {
              if(++cell_change_time >= CELL_CHANGE_TIMEOUT) { //   ,       , ,    
                change_cell(cell);
              }
            }
          }
        }
      }
    }
  }
}

//    watchdog
ISR(WDT_vect) {
  WDTCR |= (1 << WDTIE); //    watchdog   ""    
}

//    ,        ADSC  measure_adc()
EMPTY_INTERRUPT(ADC_vect);

//  
int main(void) {
  init_pins();       //  
  init_interrupts(); //    watchdog
	
  while(true) {                          //  ,       
    set_sleep_mode(SLEEP_MODE_PWR_DOWN); //        
    sleep_cpu();                         //        watchdog 

    main_proc();                         //          
  }
}


: L:0x6A, H:0xFF.

. , – , – . , . :


, .

, . , . , , .

, - , . , , . , - , , , , . .. - , , . . . , , .

, . , , , 12.5 , , 12.4 . . , .



, , , . , . «» 8-9 .

, . «» , . , , «» , - (, , , , , - ).

, +50⁰C , . , , , . .

«», , (watchdog). .

, – . . Watchdog , , , . , , , watchdog. , , .

. 1006 , - .

, , . , O2, , Os , 1024 . -, .

.


Eagle .

Source: https://habr.com/ru/post/446956/


All Articles