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

—

от автора

селектор в разобранном виде

селектор в разобранном виде

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

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

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

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

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

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

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

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

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

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

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

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

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

с другим ракурсом

вид

вид под углом

вид под углом

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

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

файл stm8s_conf.h:

Hidden text
/**   ******************************************************************************   * @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:

Hidden text
//распиновка платы 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:

Hidden text
#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мСек voidLedStatus(uint8_tLedMode) {   switch(LedMode)   {     case0:     LedMask = 0x0555;//мигание быстрое каждые 0.2 секунды (6 раз)     break;     case1:     LedMask = 0x0001;//1 раз мигнем     break;     case2:     LedMask = 0x0005;//2 раза мигнем     break;     case3:     LedMask = 0x0015;//3 раза мигнем     break;     case4:     LedMask = 0x0055;//4 раза мигнем     break;     case5:     LedMask = 0x0155;//5 раз мигнем     break;     case6:     LedMask = 0x0013;//1 раз мигнем длительно 1 коротко     break;     case7:     LedMask = 0x0053;//1 раз мигнем длительно 2 коротко     break;     case8:     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_tByteAddr;   uint8_tByteData = 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_tByteAddr;   uint8_tByteData = 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:

Hidden text
#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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *