Блок переключателей РК Патриот

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.
селектор в разобранном виде
селектор в разобранном виде

Добрый день, есть такое движение у автолюбителей - покупать некоторые отечественные автомобили как своего рода конструктор для взрослых, в том числе и для последующего передвижения на нем по бездорожью. Одним из доступных таких автомобилей, до недавнего времени был «УАЗ-Patriot”. Не буду вдаваться, на что идут творческие люди и с какими компромиссами сталкиваются, опишу только конструкцию переключателя режима работы полно-приводной трансмиссии с кнопками подогрева сидений (далее просто селектор раздатки). Конструкция вполне себе какая есть, но если к примеру вы модернизируете автомобиль для себя, бывает желание изменить схему функционирования этого узла под свои нужды - например дополнить системой изменения давления в шинах и что-либо еще. Для этого надо иметь схему селектора, и возможность изменять прошивку установленного там микроконтроллера.

Приведу нарисованную мной схему селектора, который попал мне в руки:

схема селектора
схема селектора

Схема не сложная, и в зависимости от комплектации, на плате могут отсутствовать некоторые компоненты. Компоненты можно приобрести и установить, но с микроконтроллером ситуация стандартная: прочитать нельзя, но запрограммировать можно. Следующая цель - создание своей прошивки. Микроконтроллер, который установлен в блоке - STM8S003F3, простой 8-битный, с очень малым потреблением тока и ценой. Поэтому купил отладочную плату с аналогичным микроконтроллером со встроенным программатором по шине SWIM, и еще плату для экспериментов с таким же микроконтроллером. Заодно скачал и установил необходимую среду разработки для этого класса микроконтроллеров (на Хабре есть статьи на это тему).

отладочная плата и плата для тестов
отладочная плата и плата для тестов

На плате блока есть отверстия для подключения программатора к микроконтроллеру:

подключение для программирования и отладки
подключение для программирования и отладки

заодно подключимся к свободным пинам микроконтроллера для отладки:

Светодиод для мигания при отладке
Светодиод для мигания при отладке
с другим ракурсом
с другим ракурсом

Так как функции селектора довольно простые, то и код получился компактным:

файл stm8s_conf.h:

/**
  ******************************************************************************
  * @file     stm8s_conf.h
  * @author   MCD Application Team
  * @version  V2.0.0
  * @date     25-February-2011
  * @brief    This file is used to configure the Library.
  ******************************************************************************
  * @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
  ******************************************************************************
  */ 

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM8S_CONF_H
#define __STM8S_CONF_H

/* Includes ------------------------------------------------------------------*/
#include "stm8s.h"

/* Uncomment the line below to enable peripheral header file inclusion */
#if defined(STM8S105) || defined(STM8S103) || defined(STM8S903) || defined (STM8AF626x)
#include "stm8s_adc1.h" 
#endif /* (STM8S105) ||(STM8S103) || (STM8S903) || STM8AF626x*/
#if defined(STM8S208) || defined(STM8S207) || defined (STM8AF52Ax) || defined (STM8AF62Ax)
 #include "stm8s_adc2.h"
#endif /* (STM8S208) || (STM8S207) || (STM8AF62Ax) || (STM8AF52Ax) */
#include "stm8s_awu.h"
#include "stm8s_beep.h"
#if defined (STM8S208) || defined (STM8AF52Ax)
 #include "stm8s_can.h"
#endif /* STM8S208 || STM8AF52Ax */
#include "stm8s_clk.h"
#include "stm8s_exti.h"
#include "stm8s_flash.h"
#include "stm8s_gpio.h"
#include "stm8s_i2c.h"
#include "stm8s_itc.h"
#include "stm8s_iwdg.h"
#include "stm8s_rst.h"
#include "stm8s_spi.h"
#include "stm8s_tim1.h"
#ifndef STM8S903
 #include "stm8s_tim2.h"
#endif /* STM8S903 */
#if defined(STM8S208) || defined(STM8S207) ||defined(STM8S105) || defined (STM8AF52Ax) ||\
    defined (STM8AF62Ax) || defined (STM8AF626x)
 #include "stm8s_tim3.h"
#endif /* (STM8S208) ||defined(STM8S207) ||defined(STM8S105) */ 
#ifndef STM8S903
 #include "stm8s_tim4.h"
#endif /* STM8S903 */
#ifdef STM8S903
 #include "stm8s_tim5.h"
 #include "stm8s_tim6.h"
#endif /* STM8S903 */
#if defined(STM8S208) ||defined(STM8S207) ||defined(STM8S103) ||defined(STM8S903) ||\
    defined (STM8AF52Ax) || defined (STM8AF62Ax)
 #include "stm8s_uart1.h"
#endif /* STM8S208 || STM8S207 || STM8S103 ||STM8S903 || STM8AF52Ax || STM8AF62Ax */
#if defined (STM8S105) || defined (STM8AF626x)
 #include "stm8s_uart2.h"
#endif /* STM8S105 || STM8AF626x */
#if defined(STM8S208) ||defined(STM8S207) || defined (STM8AF52Ax) || defined (STM8AF62Ax)
 #include "stm8s_uart3.h"
#endif /* STM8S208 || STM8S207 || STM8AF52Ax || STM8AF62Ax */ 
#include "stm8s_wwdg.h"

/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Uncomment the line below to expanse the "assert_param" macro in the
   Standard Peripheral Library drivers code */
//#define USE_FULL_ASSERT    (1) 

/* Exported macro ------------------------------------------------------------*/
#ifdef  USE_FULL_ASSERT

/**
  * @brief  The assert_param macro is used for function's parameters check.
  * @param expr: If expr is false, it calls assert_failed function
  *   which reports the name of the source file and the source
  *   line number of the call that failed.
  *   If expr is true, it returns no value.
  * @retval : None
  */
#define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
void assert_failed(uint8_t* file, uint32_t line);
#else
#define assert_param(expr) ((void)0)
#endif /* USE_FULL_ASSERT */

#endif /* __STM8S_CONF_H */

/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

файл board.h:

//распиновка платы STM8S003F3P6 1kB RAM 128 EEPROM 8k Flash
//
//             +---|___|---+
//      PD4  o-|           |-o PD3
//      PD5  o-|           |-o PD2
//      PD6  o-|           |-o PD1
//     NRST  o-|           |-o PC7
//      PA1  o-|           |-o PC6
//      PA2  o-|           |-o PC5
//      GND  o-|           |-o PC4
//       5V  o-|           |-o PC3
//     3.3V  o-|           |-o PB4
//      PA4  o-|           |-o PB5 
//             +-----------+
//
//**************************************************************************
//Подключения платы селектора:
//PA1  - выход светодиод подключенного бака 2
//PA2  - никуда не подключен
//PA3  - кнопка вход переключателя бака 1/2
//PB4  - кнопка вход PHEAT
//PB5  - кнопка вход DHEAT
//PC3  - выход реле/светодиод DHEAT
//PC4  - вход ADC_IN2 напряжение питания блока (VCC = 5V)
//PC5  - селектор вход 2H
//PC6  - выход реле/светодиод PHEAT
//PC7  - выход реле/светодиод подключенного бака 1
//PD1  - SWIM
//PD2  - выход управления X1_3
//PD3  - выход управления X1_1
//PD4  - селектор вход 4S
//PD5  - селектор вход 4L
//PD6  - селектор вход 4H
//
//TIM4 8 - битный таймер для генерации прерывания 1 мСек
//
// делитель напряжения питания, входное напряжение -> вход ADC
// 12V -> 2V
// 18V -> 3V
// ...
//
//тестовый светодиод RGB и кнопка
//PC7(17) - B (реле бака)
//PA2(6)  - G  test pin
//PA1(5)  - R (светодиод бака)
//PA4 - кнопка SW7
//
//состояние кнопок храним в EEPROM и восстанавливаем при включении питания
//используемые адреса:
// 0x00 - состояние кнопки подогрева водительского сиденья
// 0x01 - состояние кнопки подогрева пассажирского сиденья
// 0x03 - состояние кнопки переключателя бензобака
//

#ifndef __BOARD_H
#define __BOARD_H
#include "stm8s.h"

//селектор раздатки *****
//входы
#define  S2H_PIN   GPIO_PIN_5
#define  S2H_PORT  GPIOC
#define  S4H_PIN   GPIO_PIN_6
#define  S4H_PORT  GPIOD
#define  S4L_PIN   GPIO_PIN_5
#define  S4L_PORT  GPIOD
#define  S4S_PIN   GPIO_PIN_4
#define  S4S_PORT  GPIOD
//выходы
#define  OUT_2H_PIN     GPIO_PIN_2
#define  OUT_2H_PORT    GPIOD
#define  OUT_4L_PIN     GPIO_PIN_3
#define  OUT_4L_PORT    GPIOD

//подогрев сидений *****
//входы
#define  D_KEY_PIN   GPIO_PIN_5
#define  D_KEY_PORT  GPIOB
#define  P_KEY_PIN   GPIO_PIN_4
#define  P_KEY_PORT  GPIOB
//выходы:
#define  D_HEAT_PIN   GPIO_PIN_3
#define  D_HEAT_PORT  GPIOC
#define  P_HEAT_PIN   GPIO_PIN_6
#define  P_HEAT_PORT  GPIOC

//напряжение питания *****
#define  V_IN_PIN   GPIO_PIN_4
#define  V_IN_PORT  GPIOC

//топливный бак *****
//входы
#define  TANK_KEY_PIN   GPIO_PIN_3
#define  TANK_KEY_PORT  GPIOA
//выходы:
#define  TANK_R_PIN     GPIO_PIN_7
#define  TANK_R_PORT    GPIOC
#define  TANK_LED_PIN   GPIO_PIN_1
#define  TANK_LED_PORT  GPIOA

//тестовый выход (не разведен на плате)
#define  TEST_PIN     GPIO_PIN_2
#define  TEST_PORT    GPIOA

#define  START_EEPROM     0x4000
#define  SIZE_EEPROM      0x80

#define  D_HEAT_EEPROM     0x4000
#define  P_HEAT_EEPROM     0x4001
#define  T_EEPROM          0x4002

#define  SEL_NO         0
#define  SEL_2H         1
#define  SEL_4H         2
#define  SEL_4L         4
#define  SEL_4S         8
#define  SEL_TICK_ON    150     //сколько тиков (mS) накапливаем значение селектора
#define  SEL_TICK_LOCK  1000    //сколько тиков (mS) накапливаем значение для включения блокировки селектора

#define  KEY_NO        0        //состояние кнопки
#define  KEY_SET       1


#define  VP_MIN        12000    //минимально напряжение (mV)активности подогрева сидений
#define  VP_GIST       500      //допустимый гистерезис (mV)
void CLK_Configuration(void);
void GPIO_Configuration(void);
void Delay(uint16_t Cycles);
void TIM4_Configuration(void);
void ADC_Configuration(void);
void BoardInit(void);
void Loop(void);
void SystemMS(void);
void TestPin(uint8_t action);
void ScanSelector(void);
void SetTransferBox(uint8_t st,uint8_t lk);
void LedStatus(uint8_t LedMode);
void LedT(uint8_t action);
void ScanKeyHeat(void);
void StatusKeyHeat(void);
void SetHeat(void);
void StatusKeyTank(void);
void ScanKeyTank(void);
void SetTank(void);
uint8_t ReadStatusEEPROM(char s);
uint8_t WriteStatusEEPROM(char s,uint8_t st);

//main
extern  uint16_t        res_adc;


#endif

Файл board.c:

#include "board.h"
#include "stm8s.h"

//--------------------------------------------------------
//переменные
uint16_t  s2h_cnt;      //счетчик циклов для достоверного определения положения
uint16_t  s4h_cnt;      //счетчик циклов для достоверного определения положения
uint16_t  s4l_cnt;      //счетчик циклов для достоверного определения положения
uint16_t  s4s_cnt;      //счетчик циклов для достоверного определения положения
uint8_t   tr_Lock;      //признак режима блокировки
uint8_t   select_status;//положение селектора

uint8_t   p2h;
uint8_t   p4h;
uint8_t   p4l;
uint8_t   p4s;

uint8_t   key_d_cnt;    //счетчик циклов для достоверного распознования нажатия
uint8_t   key_p_cnt;    //счетчик циклов для достоверного распознования нажатия
uint8_t   key_t_cnt;    //счетчик циклов для достоверного распознования нажатия

uint8_t   key_d_press;  //признак нажатия кнопки
uint8_t   key_p_press;  //признак нажатия кнопки
uint8_t   key_t_press;  //признак нажатия кнопки

uint8_t   key_d_status; //статус рабочего состояния подогрева сиденья вод
uint8_t   key_p_status; //статус рабочего состояния подогрева сиденья пасс
uint8_t   key_t_status; //статус рабочего состояния переключателя бака

uint8_t   key_d_change; //блокировка изменения состояния статуса
uint8_t   key_p_change; //блокировка изменения состояния статуса
uint8_t   key_t_change; //блокировка изменения состояния статуса

uint16_t  LedDiv;       //управление миганием светодиодом режима работы
uint16_t  LedMask;      //--
uint16_t  rolmask;      //--
uint16_t  rolcnt;       //--
uint16_t  rolbit;       //--     
uint8_t   LedMode;      //--
uint8_t   workMode;     //текущий рабочий режим
uint32_t  MyTimeOn;     //текущее рабочее время
uint32_t  convAdc;      //вспомогательная переменная для пересчета АЦП
uint16_t  vdc;          //напряжение питания в милливольтах
uint8_t   heat_block;   //блокировка подогрева из за низкого напряжения
//--------------------------------------------------------
//настройка тактирования
void CLK_Configuration(void)
{
  /* Fmaster = 16MHz */
  CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);
  // Предделитель частоты тактирования процессора и периферии
  CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1);
}
//--------------------------------------------------------
//настройка портов ввода вывода
void GPIO_Configuration(void)
{
  /* GPIOD reset */
  GPIO_DeInit(GPIOA);
  GPIO_DeInit(GPIOB);
  GPIO_DeInit(GPIOC);
  GPIO_DeInit(GPIOD);

  //Селектор *****
  //выходы
  GPIO_Init(OUT_2H_PORT, OUT_2H_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
  GPIO_Init(OUT_4L_PORT, OUT_4L_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
  //входы
  GPIO_Init(S2H_PORT, S2H_PIN, GPIO_MODE_IN_FL_NO_IT);
  GPIO_Init(S4H_PORT, S4H_PIN, GPIO_MODE_IN_FL_NO_IT);
  GPIO_Init(S4L_PORT, S4L_PIN, GPIO_MODE_IN_FL_NO_IT);
  GPIO_Init(S4S_PORT, S4S_PIN, GPIO_MODE_IN_FL_NO_IT);

  //подогрев сидений *****
  //выходы
  GPIO_Init(D_HEAT_PORT,D_HEAT_PIN,GPIO_MODE_OUT_PP_LOW_FAST);
  GPIO_Init(P_HEAT_PORT,P_HEAT_PIN,GPIO_MODE_OUT_PP_LOW_FAST);
  //входы
  GPIO_Init(D_KEY_PORT, D_KEY_PIN, GPIO_MODE_IN_FL_NO_IT );
  GPIO_Init(P_KEY_PORT, P_KEY_PIN, GPIO_MODE_IN_FL_NO_IT );
  
  //топливный бак *****
  //выходы
  GPIO_Init(TANK_R_PORT,   TANK_R_PIN,   GPIO_MODE_OUT_PP_LOW_FAST);
  GPIO_Init(TANK_LED_PORT, TANK_LED_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
  //входы
  GPIO_Init(TANK_KEY_PORT, TANK_KEY_PIN, GPIO_MODE_IN_FL_NO_IT );
  
  //тестовый выход (не разведен на плате)
  //выходы PA2
  GPIO_Init(TEST_PORT, TEST_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
  
}
//--------------------------------------------------------
//задержка по тактам
//65535 ->  50mSec 
void Delay(uint16_t Cycles)
{
  uint16_t NumberOfStart = Cycles;
  while(NumberOfStart != 0)
  {
    NumberOfStart--;
  }
}
//--------------------------------------------------------
//настройка таймера 4 на генерацию прерывания с частотой 1мСек
void TIM4_Configuration(void)
{
  //Включаем тактирование таймера
  CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER4, ENABLE);
  // На всякий случай сбрасываем все настройки таймера
  TIM4_DeInit();
  // настраиваем пределитель таймера на 256 и ограничиваем счет 124
  TIM4_TimeBaseInit(TIM4_PRESCALER_128, 124);
  //Разрешаем Прерывание таймера по переполнению
  TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);
  // Включаем счет
  TIM4_Cmd(ENABLE);
}
//--------------------------------------------------------
//инициализация ADC PC4  - ADC_IN2
//преобразовывать будем по запросу, ибо в цикле слишком грузит процессор
void ADC_Configuration(void)
{
  GPIO_Init(V_IN_PORT,V_IN_PIN,GPIO_MODE_IN_FL_NO_IT);    //вход для ADC
  ADC1_DeInit();

  ADC1_Init(ADC1_CONVERSIONMODE_SINGLE, ADC1_CHANNEL_2,
            ADC1_PRESSEL_FCPU_D18,
            ADC1_EXTTRIG_TIM,DISABLE,
            ADC1_ALIGN_RIGHT,
            ADC1_SCHMITTTRIG_CHANNEL2, DISABLE);
  ADC1_ITConfig(ADC1_IT_EOCIE ,ENABLE);
}
//--------------------------------------------------------
void BoardInit(void)
{
  CLK_Configuration();  //настраиваем тактирование
  GPIO_Configuration(); //настраиваем GPIO
  TIM4_Configuration(); //настраиваем таймер4
  ADC_Configuration();  //настраиваем ADC1
  
  select_status = SEL_NO;
  s2h_cnt = 0;
  s4h_cnt = 0;
  s4l_cnt = 0;
  s4s_cnt = 0;
  tr_Lock = 0;
  
  workMode = 0;
  MyTimeOn = 0;
  LedDiv   = 0;
  rolcnt   = 0;
  rolmask  = 1;
  LedMask  = 0;
  LedMode  = 0;
  
  key_d_cnt   = 0;
  key_p_cnt   = 0;
  key_t_cnt   = 0;
  key_d_press = 0;
  key_p_press = 0;
  key_t_press = 0;

  key_d_change = 0;
  key_p_change = 0;
  key_t_change = 0;
  
  // надо сначала прочитать состояние из EEPROM  и сделать соответствующие статусы  
  key_d_status = ReadStatusEEPROM('d');
  key_p_status = ReadStatusEEPROM('p');
  key_t_status = ReadStatusEEPROM('t');;
  
  //
  vdc = 0;
  heat_block = 0;
}  
//--------------------------------------------------------
//системное прерывание каждую миллисекунду
//длительность 15uSec
void SystemMS(void)
{
//  TestPin(1);
  MyTimeOn ++;
  ScanSelector();        //сканируем селектор
  ScanKeyHeat();         //сканируем кнопки подогрева
  StatusKeyHeat();       //меняем статус кнопок подогрева       
  ScanKeyTank();         //сканируем кнопку бака
  StatusKeyTank();       //меняем статус кнопки бака
  LedStatus(workMode);   //управляем тестовым сигналом
//  TestPin(0);
}  
//--------------------------------------------------------
//основной цикл программы, не в прерывании
//цикл примерно 8mS, длительность 2uS
void Loop(void)
{
    //TestPin(1);
    Delay(11796);           //примерно 9 mSec       
    ADC1_StartConversion(); //примерно через 30 uSec готово преобразование
    Delay(1310);            //примерно 1 mSec
    //ADC 10 разрядов !
    //превращаем 0x3ff (1023) -> 30000mV -> 3ff * 29
    //питание процессора 4,8 Вольта !
    vdc = res_adc * 29;
    SetHeat();
    SetTank();
    //TestPin(0);
}  
//--------------------------------------------------------
//управление тестовым выходом, PA2
void TestPin(uint8_t action)
{
  if(action == 0)
  {
    GPIO_WriteLow(TEST_PORT, TEST_PIN);
  }
  else
  {
    GPIO_WriteHigh(TEST_PORT, TEST_PIN);
  }
}
//--------------------------------------------------------
//вызываем каждую миллисекунду
//читаем состояние селектора, действимтельно только 1 положение
void ScanSelector(void)
{
  p2h = GPIO_ReadInputPin(S2H_PORT,S2H_PIN);//возвращает 0 или номер бита !
  p4h = GPIO_ReadInputPin(S4H_PORT,S4H_PIN);//возвращает 0 или номер бита !
  p4l = GPIO_ReadInputPin(S4L_PORT,S4L_PIN);//возвращает 0 или номер бита !
  p4s = GPIO_ReadInputPin(S4S_PORT,S4S_PIN);//возвращает 0 или номер бита !
  
    if((p2h == 0) && (p4h != 0) && (p4l != 0) && (p4s != 0))
    {
      if(s2h_cnt > SEL_TICK_ON)
      {
        s2h_cnt = SEL_TICK_ON;
        select_status = SEL_2H;
        tr_Lock = 0;            //выключаем блокировку
        workMode = 1;
        SetTransferBox(select_status,tr_Lock);
      }
      else
      {
        s2h_cnt ++;
      }
      s4h_cnt = 0; s4l_cnt = 0; s4s_cnt = 0;
    }else
    if((p2h != 0) && (p4h == 0) && (p4l != 0) && (p4s != 0))
    {
      if(s4h_cnt > SEL_TICK_ON)
      {
        s4h_cnt = SEL_TICK_ON;
        select_status = SEL_4H;
        tr_Lock = 0;            //выключаем блокировку
        workMode = 2;
        SetTransferBox(select_status,tr_Lock);
      }
      else
      {
        s4h_cnt ++;
      }
      s2h_cnt = 0; s4l_cnt = 0; s4s_cnt = 0;
    }else
    if((p2h != 0) && (p4h != 0) && (p4l == 0) && (p4s != 0))
    {
      if(s4l_cnt > SEL_TICK_ON)
      {
        s4l_cnt = SEL_TICK_ON;
        select_status = SEL_4L;
        tr_Lock = tr_Lock;      //не меняем пока  блокировку
        workMode = 3;
        SetTransferBox(select_status,tr_Lock);
      }
      else
      {
        s4l_cnt ++;
      }
      s2h_cnt = 0; s4h_cnt = 0; s4s_cnt = 0;
    }else
    if((p2h != 0) && (p4h != 0) && (p4l != 0) && (p4s == 0))
    {
      if(s4s_cnt > SEL_TICK_LOCK)
      {
        s4s_cnt = SEL_TICK_LOCK;
        select_status = SEL_4S;
        tr_Lock = 1;            //включаем блокировку
        workMode = 4;
        SetTransferBox(select_status,tr_Lock);
      }
      else
      {
        s4s_cnt ++;
      }
      s2h_cnt = 0; s4h_cnt = 0; s4l_cnt = 0;
    }else
    {
      //в разрыве или сразу в двух положениях ничего не трогаем
      workMode = 0;
      s2h_cnt = 0; s4h_cnt = 0; s4l_cnt = 0; s4s_cnt = 0;
    }

    //покажем состояние блокировки  
    if(tr_Lock == 1)
    {
      TestPin(1); //G
    }
    else
    {
      TestPin(0); //G
    }
}
//--------------------------------------------------------
//сканируем кнопки подогрева сидений
void ScanKeyHeat(void)
{
    //кнопка подогрева водительского сиденья
    if(GPIO_ReadInputPin(D_KEY_PORT,D_KEY_PIN) == 0) //возвращает 0 или номер бита !
    {
      if(key_d_cnt > SEL_TICK_ON)
      {
        key_d_cnt = SEL_TICK_ON;
        key_d_press = 1;
      }
      else
      {
        key_d_cnt ++;
      }
    }
    else
    {
      key_d_press = 0;
      key_d_cnt   = 0;
    }
    
    //кнопка подогрева пассажирского сиденья
    if(GPIO_ReadInputPin(P_KEY_PORT,P_KEY_PIN) == 0) //возвращает 0 или номер бита !
    {
      if(key_p_cnt > SEL_TICK_ON)
      {
        key_p_cnt = SEL_TICK_ON;
        key_p_press = 1;
      }
      else
      {
        key_p_cnt ++;
      }
    }
    else
    {
      key_p_press = 0;
      key_p_cnt   = 0;
    }
}
//--------------------------------------------------------
//сканируем кнопку бака
void ScanKeyTank(void)
{
    //кнопка переключения топливного бака
    if(GPIO_ReadInputPin(TANK_KEY_PORT,TANK_KEY_PIN) == 0) //возвращает 0 или номер бита !
    {
      if(key_t_cnt > SEL_TICK_ON)
      {
        key_t_cnt = SEL_TICK_ON;
        key_t_press = 1;
      }
      else
      {
        key_t_cnt ++;
      }
    }
    else
    {
      key_t_press = 0;
      key_t_cnt   = 0;
    }
}
//--------------------------------------------------------
//переключаем раздаточную коробку в зависимости от статуса
void SetTransferBox(uint8_t st,uint8_t lk)
{
  if((st == SEL_2H)||(st == SEL_NO ))
  {
    GPIO_WriteHigh(OUT_2H_PORT, OUT_2H_PIN);
    GPIO_WriteLow (OUT_4L_PORT, OUT_4L_PIN);
  }else
  if(st == SEL_4H)  
  {
    GPIO_WriteLow (OUT_2H_PORT, OUT_2H_PIN);
    GPIO_WriteLow (OUT_4L_PORT, OUT_4L_PIN);
  }else
  if((st == SEL_4L)||(st == SEL_4S ))
  {
    if(lk == 1)
    {
      GPIO_WriteLow (OUT_2H_PORT, OUT_2H_PIN);
      GPIO_WriteHigh(OUT_4L_PORT, OUT_4L_PIN);
    }else
    {
      GPIO_WriteLow (OUT_2H_PORT, OUT_2H_PIN);
      GPIO_WriteLow(OUT_4L_PORT, OUT_4L_PIN);
    }
  }else
  { //2h
    GPIO_WriteLow (OUT_2H_PORT, OUT_2H_PIN);
    GPIO_WriteLow (OUT_4L_PORT, OUT_4L_PIN);
  }
}
//--------------------------------------------------------
//меняем статус кнопок подогрева сидений
void StatusKeyHeat(void)
{
  if(vdc <= (VP_MIN - VP_GIST))    //если напряжение низкое, не греем и статусы не меняем
  {
    heat_block = 1;
  } 
  
  if(vdc >= (VP_MIN + VP_GIST))    //если напряжение низкое, не греем и статусы не меняем
  {
    heat_block = 0;
  } 
//--d
    if(key_d_press == 1)          //если нажата кнопка подогрева вод сиденья
    {
      if (key_d_change == 0)      //и состояние еще не меняли 
      {
        if(key_d_status == KEY_NO)
        {
          key_d_status = KEY_SET;
        }
        else
        {
          key_d_status = KEY_NO;
        }
        WriteStatusEEPROM('d',key_d_status); //запишем новое состояние EEPROM
        key_d_change = 1;         //поменяли кнопку, выставим признак что до отпускания кнопки ничего не делаем
      }
    }
    else
    {
      key_d_change = 0;           //отпустили кнопку, можно менять состояние снова
    }
//--p
    if(key_p_press == 1)          //если нажата кнопка подогрева пасс сиденья
    {
      if(key_p_change == 0)       //и состояние еще не меняли 
      {
        if(key_p_status == KEY_NO)
        {
          key_p_status = KEY_SET;
        }
        else
        {
          key_p_status = KEY_NO;
        }
        WriteStatusEEPROM('p',key_p_status); //запишем новое состояние EEPROM
        key_p_change = 1;         //поменяли кнопку, выставим признак что до отпускания кнопки ничего не делаем
      }
    }
    else
    {
      key_p_change = 0;           //отпустили кнопку, можно менять состояние снова
    }
}
//--------------------------------------------------------
//меняем состояние выходов подогрева сидений
void SetHeat(void)
{
  if(heat_block == 1)              //если подогрев заблокирован
  {
      GPIO_WriteLow (D_HEAT_PORT, D_HEAT_PIN);
      GPIO_WriteLow (P_HEAT_PORT, P_HEAT_PIN);
  }
  else
  {
    if(key_d_status == KEY_SET)
    {
      GPIO_WriteHigh(D_HEAT_PORT, D_HEAT_PIN);
    }
    else
    {
      GPIO_WriteLow (D_HEAT_PORT, D_HEAT_PIN);
    }
  
    if(key_p_status == KEY_SET)
    {
      GPIO_WriteHigh(P_HEAT_PORT, P_HEAT_PIN);
    }
    else
    {
      GPIO_WriteLow (P_HEAT_PORT, P_HEAT_PIN);
    }
  }
}
//--------------------------------------------------------
//меняем статус выходов переключателя бака
void StatusKeyTank(void)
{
  if(key_t_press == 1)  //если нажата кнопка смены бака
  {
    if(key_t_change == 0)  //и состояние еще не меняли
    {
      if(key_t_status == KEY_NO)
      {
        key_t_status = KEY_SET;
      }
      else
      {
        key_t_status = KEY_NO;
      }
      WriteStatusEEPROM('t',key_t_status); //запишем новое состояние EEPROM
      key_t_change = 1;   //поменяли кнопку, выставим признак что до отпускания кнопки ничего не делаем
    }
  }
  else
  {
    key_t_change = 0;   //отпустили кнопку, можно менять состояние снова
  }
}
//--------------------------------------------------------
//меняем состояние выходов переключателя бака
void SetTank(void)
{
  //надо добавить работу с eeprom 
  
  if(key_t_status == KEY_SET)
  {
    GPIO_WriteHigh(TANK_R_PORT, TANK_R_PIN); //B
    //GPIO_WriteLow(TANK_LED_PORT, TANK_LED_PIN); //R        //используем для тестового мигания
  }
  else
  {
    GPIO_WriteLow(TANK_R_PORT, TANK_R_PIN); //B
    //GPIO_WriteHigh(TANK_LED_PORT, TANK_LED_PIN); //R       //используем для тестового мигания
  }
}
//--------------------------------------------------------
//мигание светодиода/тестового выхода  в зависимости от режима работы
//используем циклический сдвиг раз в 0,2 секунды 12 битного числа
//вызывать будем из прерывания системного таймера (раз в миллисекунду)
//период мигания равен 12*200мСек=2.4 секунды
//длительность импульса кратна 200мСек
void	LedStatus(uint8_t	LedMode)
{
  switch(LedMode)
  {
    case	0:
    LedMask = 0x0555;	//мигание быстрое каждые 0.2 секунды (6 раз)
    break;
    case	1:
    LedMask = 0x0001;	//1 раз мигнем
    break;
    case	2:
    LedMask = 0x0005;	//2 раза мигнем
    break;
    case	3:
    LedMask = 0x0015;	//3 раза мигнем
    break;
    case	4:
    LedMask = 0x0055;	//4 раза мигнем
    break;
    case	5:
    LedMask = 0x0155;	//5 раз мигнем
    break;
    case	6:
    LedMask = 0x0013;	//1 раз мигнем длительно 1 коротко
    break;
    case	7:
    LedMask = 0x0053;	//1 раз мигнем длительно 2 коротко
    break;
    case	8:
    LedMask = 0x0153;	//1 раз мигнем длительно 3 коротко
    break;
    default:
    LedMask = 0x0555;	//6 раз мигнем
    break;
  }
  LedDiv++;
  //досчитали до 0,2 секунды
  //полный цикл из 16 бит * 0,2 секунды = 4 секунд период мигания
  if(LedDiv >= 200)
  {
    //сдвинем маску в нужную позицию
    //и выделим бит
    rolbit = (LedMask & (rolmask << rolcnt)) >> rolcnt;
    //отправам в функцию светодиода
    LedT(rolbit); //R
    //отправам в функцию внешнего светодиода
    //к следующему биту
    if( rolcnt < 11 )
    { rolcnt ++ ; }
    else
    { rolcnt = 0; }	//или сбросим если прошли все биты
    LedDiv = 0;		
  }
}
//--------------------------------------------------------
//управление светодиодом бака, PA1
void LedT(uint8_t action) //R
{
  if(action == 0)
  {
    GPIO_WriteLow(TANK_LED_PORT, TANK_LED_PIN);
  }
  else
  {
    GPIO_WriteHigh(TANK_LED_PORT, TANK_LED_PIN);
  }
}
//--------------------------------------------------------
//прочитать сохраненный в EEPROM статус подогрева сидений  или бака
//s = 'D' или 'd' - водитель, 'P' или 'p' - пассажир,
//'T' или 't' - топливный бак
//возвращаем 0 - если выключено, 1 если включено
uint8_t ReadStatusEEPROM(char s)
{
  uint8_t       stat;
  uint32_t	ByteAddr;
  uint8_t	ByteData = 0;  
  
  if((s == 'D')||(s == 'd'))
  {
    ByteAddr    = D_HEAT_EEPROM;
  }
  else
  if((s == 'P')||(s == 'p'))
  {
    ByteAddr    = P_HEAT_EEPROM;
  }
  else
  if((s == 'T')||(s == 't'))
  {
    ByteAddr    = T_EEPROM;
  }
  else
  {
    return 0; //выходим с 0 без чтения  
  }
  
  ByteData	= FLASH_ReadByte(ByteAddr);
  if(ByteData == 0xff)
  {
    stat = 1;
  }else
  {
    stat = 0;
  }
  return stat;      
}
//--------------------------------------------------------
//записать в EEPROM статус подогрева сидений или бака
//s = 'D' или 'd' - водитель, 'P' или 'p' - пассажир,
//'T' или 't' - топливный бак
//st - статус 1 или 0
//возвращаем 1 - если записалось, 0 если ошибка
uint8_t WriteStatusEEPROM(char s,uint8_t st)
{
  uint8_t       stat = 0;
  uint32_t	ByteAddr;
  uint8_t	ByteData = 0;  
  
  if((s == 'D')||(s == 'd'))
  {
    ByteAddr    = D_HEAT_EEPROM;
  }
  else
  if((s == 'P')||(s == 'p'))
  {
    ByteAddr    = P_HEAT_EEPROM;
  }
  else
  if((s == 'T')||(s == 't'))
  {
    ByteAddr    = T_EEPROM;
  }
  else
  {
    return stat;
  }
  
  if(st == 1)
  {
    ByteData = 0xff; 
  }
  else
  {
    ByteData = 0x00; 
  }
  
  FLASH_Unlock(FLASH_MEMTYPE_DATA);             //разблокировать EEPROM
  FLASH_ProgramByte(ByteAddr, ByteData);        //записать байт в  EEPROM
  FLASH_Lock(FLASH_MEMTYPE_DATA);               //заблокировать EEPROM
  stat = 1;
  return stat;      
}
//--------------------------------------------------------
/*    
    //проверим работу АЦП, все совпадает !
    if(vdc < 10000)
    {
      workMode = 1;
    }else
    if((vdc >= 10000)&&(vdc < 11000))
    {
      workMode = 2;
    }else
    if((vdc >= 11000)&&(vdc < 12000))
    {
      workMode = 3;
    }else
    if((vdc >= 12000)&&(vdc < 13000))
    {
      workMode = 4;
    }else
    if((vdc >= 13000)&&(vdc < 14000))
    {
      workMode = 5;
    }else
    if(vdc >= 14000)
    {
      workMode = 6;
    }else
    {
      workMode = 0;
    }
*/

файл main.c:

#include "stm8s.h"
#include "board.h"
//--------------------------------------------------------
//переменные
uint16_t        res_adc;
uint8_t         flag_out_adc = 0;
uint16_t        ee_addr = 0;        //адрес для eeprom
uint8_t         ee_data = 0;        //данные для eeprom  

//--------------------------------------------------------
//обработчик прерывания завершения преобразования ADC
//22 - это вектор прерывания adc (указано в datasheet)
//время  ~5uSec
INTERRUPT_HANDLER(ADC1_IRQHandler, 22)
{
  res_adc = ADC1_GetConversionValue();
  ADC1_ClearITPendingBit(ADC1_IT_EOC);
}
//--------------------------------------------------------
//обработчик прерывания таймера 4
//23 - это вектор прерывания таймера 4 (указано в datasheet)
INTERRUPT_HANDLER(IRQ_Handler_TIM4, 23)
{
  //делаем действия по прерыванию таймера 1mSec
  SystemMS();   
  // Сбрасываем флаг прерывания переполнения
  TIM4_ClearITPendingBit(TIM4_IT_UPDATE);
}
//--------------------------------------------------------

int main( void )
{
  //инициализация
  BoardInit();
  //Включаем глобальное разрешение прерывания
  enableInterrupts();
  //основной цикл
  while(1)
  {
    Loop();
  }
//  return 0;   //сюда не попадаем
}

Это был первый и единственный пока мой опыт с данным микроконтроллером, и этот код послужил как шаблон для последующих изменений функций этого блока. В последующем была чуть изменена схема и код, чуть модернизирована лицевая панель (некоторые кнопки перекрашены под свои пиктограммы):

новые функции блока
новые функции блока

Автомобиль немного доработали напильником правильными руками, получился хороший вездеход на больших колесах со всеми возможными блокировками и системой контроля давления в шинах. И с тех пор где-то что то покоряет.

Спасибо что дочитали, может это кому-нибудь поможет в творчестве или ремонте, хотел показать как один из вариантов модернизации простых штатных электронных блоков автомобиля, не сильно меняя интерьер.

Источник: https://habr.com/ru/articles/779488/


Интересные статьи

Интересные статьи

КРЕНка на 220, если коротко. Если погуглить этот вопрос напрямую, то найдется довольно дорогая микросхема TL783. В свою очередь, драйверы из 50 рублевых ламп стоят либо 100 рублей за пакет, либо выпаи...
Однажды ко мне обратился человек с просьбой помочь опознать экран от системы водяного охлаждения NZXT Z63. Экран не работал должным образом и могла потребоваться его замена. Опознать дисплей, найт...
Web3 это концепция развития Интернета с применением блокчейн-технологий и токенизацией всего, что может существовать онлайн. Поскольку Интернет тесно переплетён с активами из реальной экономики, токен...
Данная статья поможет всем интересующимся лучше узнать про технические особенности платформ: Cosmos, Polkadot, Avalanche.Эти платформы нацелены на горизонтальное масштабирование с асинхронной гетероге...
Привет, читатель. Представляю пост который идёт строго (!) в закладки и передаётся коллегам. Он с подборкой примечательных файлов формата Jupyter Notebook по Machine Learning, Data Science и д...