Добрый день, есть такое движение у автолюбителей - покупать некоторые отечественные автомобили как своего рода конструктор для взрослых, в том числе и для последующего передвижения на нем по бездорожью. Одним из доступных таких автомобилей, до недавнего времени был «УАЗ-Patriot». Не буду вдаваться, на что идут творческие люди и с какими компромиссами сталкиваются, опишу только конструкцию переключателя режима работы полно‑приводной трансмиссии с кнопками подогрева сидений (далее просто селектор раздатки). Конструкция вполне себе какая есть, но если к примеру вы модернизируете автомобиль для себя, бывает желание изменить схему функционирования этого узла под свои нужды - например дополнить системой изменения давления в шинах и что-либо еще. Для этого надо иметь схему селектора, и возможность изменять прошивку установленного там микроконтроллера.
Приведу нарисованную мной схему селектора, который попал мне в руки:
![схема селектора схема селектора](https://webcf.waybackmachine.org/web/20231215102237/https://habrastorage.org/getpro/habr/upload_files/4d3/409/e63/4d3409e63dd7fba85b1050a48ecb65a3.jpg)
Схема не сложная, и в зависимости от комплектации, на плате могут отсутствовать некоторые компоненты. Компоненты можно приобрести и установить, но с микроконтроллером ситуация стандартная: прочитать нельзя, но запрограммировать можно. Следующая цель - создание своей прошивки. Микроконтроллер, который установлен в блоке - 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>© 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мСек
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:
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; //сюда не попадаем
}
Это был первый и единственный пока мой опыт с данным микроконтроллером, и этот код послужил как шаблон для последующих изменений функций этого блока. В последующем была чуть изменена схема и код, чуть модернизирована лицевая панель (некоторые кнопки перекрашены под свои пиктограммы):
Автомобиль немного доработали напильником правильными руками, получился хороший вездеход на больших колесах со всеми возможными блокировками и системой контроля давления в шинах. И с тех пор где-то что-то покоряет.
Спасибо, что дочитали. Может это кому-нибудь поможет в творчестве или ремонте, хотел показать как один из вариантов модернизации простых штатных электронных блоков автомобиля, не сильно меняя интерьер.