Re: датчик положения дроссельной заслонки
Добавлено: Вт июн 02, 2026 18:07:45
Да, ночью займусь.
Здесь можно немножко помяукать :)
https://radiokot.ru:443/forum/
Да, ночью займусь.
Я правильно понял, что Вы используете Ардуино Нано? полагаю, есть и Arduino IDE?
Код: Выделить всё
const uint8_t Dta[11] PROGMEM = {
0b11111100 /*0*/, 0b01100000 /*1*/, 0b11011010 /*2*/, 0b11110010 /*3*/, 0b01100110 /*4*/, 0b10110110 /*5*/,
0b10111110 /*6*/, 0b11100000 /*7*/, 0b11111110 /*8*/, 0b11110110 /*9*/, 0b00000000 /* */
};
volatile uint8_t dig = 0;
volatile uint16_t display = 0;
volatile uint16_t filteredADC = 0;
ISR(TIMER2_OVF_vect) {
PORTB &= 0xC3; // Выключить все разряды
dig = (dig + 1) & 0x03;
uint8_t segments = pgm_read_byte(&Dta[(display >> (dig << 2)) & 0x0F]);
if (dig == 2) segments &= ~0x01;
PORTD = (PORTD & 0x03) | (segments >> 2) & 0xFC;
PORTB = (PORTB & 0x03) | (segments & 0x03) | (1 << (2 + dig));
}
ISR(ADC_vect) {
static uint32_t sum = 0;
static uint8_t count = 0;
sum += ADC;
if (++count >= 16) {
filteredADC = (filteredADC * 7 + (sum >> 4)) >> 3;
sum = 0;
count = 0;
}
}
inline void updateDisplay() {
uint16_t val = filteredADC;
uint8_t d3 = val % 10;
val /= 10;
uint8_t d2 = val % 10;
val /= 10;
uint8_t d1 = val % 10;
val /= 10;
uint8_t d0 = val;
if (d0 == 0) {
d0 = 10;
if (d1 == 0) {
d1 = 10;
if (d2 == 0) d2 = 10;
}
}
display = (uint16_t)d0 << 12 | (uint16_t)d1 << 8 | (uint16_t)d2 << 4 | d3;
}
void setup() {
/** Порты */
DDRD |= 0xFC; // PD2..PD7 = выходы (A-F)
DDRB |= 0x3F; // PB0..PB5 = выходы (G,H + Dig1..Dig4)
PORTB &= 0xC3; // Выключить все разряды
/** ADC */
ADMUX = (1 << REFS0) | 7;
ADCSRA = (1 << ADEN) | (1 << ADIE) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
// Free Running Mode
ADCSRB = 0;
ADCSRA |= (1 << ADATE) | (1 << ADSC);
/** TIM2 */
TCCR2A = 0;
TCCR2B = (1 << CS22) | (1 << CS21);
TIMSK2 |= (1 << TOIE2);
sei();
}
void loop() {
updateDisplay();
delay(50);
}Отлично, потому что тогда ни Ваш, ни мой код не подходят даже для текущей схемы
Ввёл настройку для этого, сможете сами подстроить. Но какое максимальное значение? Если 99, то можно добавить признак, что отображается именно этот счётчик: вращение верхнего квадратика на индикаторе.ЗАВ писал(а): Ср июн 03, 2026 08:56:45 при 150 импульсах значение должно быть 30,0 (эти показания уточним на месте по факту).
Код: Выделить всё
/*********** Настройки **********************************************************************************/
#define DISPLAY_COMMON_CATHODE //Дисплей с общим катодом
//#define DISPLAY_COMMON_ANODE //Дисплей с общим анодом
/* Максимальное отображаемое значение при необходимом напряжении равно L + (H - L) * 5 / V,
где H - отображаемое число при напряжении V, L - отображеемое число при напряжении 0.
* Например: для значений на дисплее 80.0 и 4.00 при напряжении 4.5 В
* HIGH_VALUE_DISLAY_TRIM = LOW_VALUE_DISLAY_TRIM + (800 - LOW_VALUE_DISLAY_TRIM) * 5 / 4.5 = 889
* HIGH_VALUE_DISLAY_PRESSURE = LOW_VALUE_DISLAY_PRESSURE + (400 - LOW_VALUE_DISLAY_PRESSURE) * 5 / 4.5 = 444
* Значения всегда должны быть из трёх цифр, если используется запятая, при этом она игнорируется при расчёте и устанавливается в DOT_TRIM и DOT_PRESSURE.
*
* LOW_VALUE_DISLAY_TRIM и LOW_VALUE_DISLAY_PRESSURE должны учитывать точку. То есть, если требуется отображение числа "2", при этом
* DOT_TRIM равен 1, то необходимо установить 20, иначе на дисплее будет "0.2" */
#define LOW_VALUE_DISLAY_TRIM 0 // Минимальное отображаемое значение "ТРИМ".
#define HIGH_VALUE_DISLAY_TRIM 889 // Максимальное отображаемое значение "ТРИМ" при сигнале 5 В.
#define DOT_TRIM 1 // Количество разрядов после запятой дисплея "ДАВЛЕНИЕ" (3 - запятая выключена).
#define FREQ_TRIM 2 // Период опроса в десятках миллисекунд датчика "ТРИМ" \
// значение 1...100, например: 1 = опрос 100 раз в секунлду, 100 = раз в секунду.
#define LOW_VALUE_DISLAY_PRESSURE 0 // Минимальное отображаемое значение "ДАВЛЕНИЕ".
#define HIGH_VALUE_DISLAY_PRESSURE 445 // Максимальное отображаемое значение "ДАВЛЕНИЕ" при сигнале 5 В.
#define DOT_PRESSURE 2 // Количество разрядов после запятой дисплея "ДАВЛЕНИЕ" (3 - запятая выключена).
#define FREQ_PRESSURE 100 // Период опроса в десятках миллисекунд датчика "ДАВЛЕНИЕ" \
// значение 1...100, например: 1 = опрос 100 раз в секунлду, 100 = раз в секунду.
#define IMPULS 1 // Количество импульсов, изменяющих счётчик на единицу
/*********** конец настроек пользователя **********************************************************************************/
#define CH_ADC_TRIM 1 // Канал АЦП датчика "ТРИМ".
#define CH_ADC_PRESSURE 2 // Канал АЦП датчика "ДАВЛЕНИЕ".
#define CH_ADC_COUNT 0 // Канал АЦП датчика "СЧЁТЧИК".
#define INDICATOR_1 0 // Смещение разрядов первого индикатора.
#define INDICATOR_2 3 // Смещение разрядов второго индикатора.
// Массивы сегментов, номер элемента массива соответсвует отображаемой цифре. Десятый элемент - пробел.
#ifdef DISPLAY_COMMON_CATHODE
const uint8_t Segments[11] = { 0xFC, /*0*/ 0x18, 0x6D, 0x3D, 0x99, 0xB5, 0xF5, 0x1C, 0xFD, 0xBD /*9*/, 0x00 };
#else
const uint8_t Segments[11] = { 0x03, /*0*/ 0xE7, 0x92, 0xC2, 0x66, 0x4A, 0x0A, 0xE3, 0x02, 0x42 /*9*/, 0xFF };
#endif
volatile uint8_t display[9] = { 0 }; // Дисплейный буфер. ЭЛЕМЕНТЫ 0...2 - первый индикатор, 3...5 - второй индикатор, 6...8 - третий индикатор.
volatile uint8_t currentDig = 0; // Текущий отображаемый разряд.
volatile uint8_t adcChannels[3] = { CH_ADC_TRIM, CH_ADC_PRESSURE, CH_ADC_COUNT}; // Каналы АЦП для датчиков 1...3 индикаторов 1...3
/******** Дисплей ************************************************************************************************************************/
// Функция преобразования числа и точки в сегменты дисплейного буфера для определённого индикатора.
void updatedisplay(uint16_t val, uint8_t dot, uint8_t indicator) {
// Выделение цифр из числа:
uint8_t d3 = (uint8_t)(val / 100); // сотни,
uint8_t d2 = (uint8_t)((val / 10) % 10); // десятки,
uint8_t d1 = (uint8_t)(val % 10); // единицы.
// Проверка на лишние нули слева:
if ((!d3) && (!d2) && (!d1)) { // если все три цифры - 0...
d3 = d2 = 11; // ... то они становятся пробелами,
dot = 3; // а точка не нужна при 0;
}
if ((!d3) && (dot != 2)) { // если крайний левый разряд не отделен точкой и равен 0...
d3 = 11; //... то он становится пробелом.
}
// Получение сегментов соответственно цифрам:
uint8_t seg3 = Segments[d3];
uint8_t seg2 = Segments[d2];
uint8_t seg1 = Segments[d1];
// Добавление точки к нужному разряду:
#ifdef DISPLAY_COMMON_CATHODE
if (dot == 2) seg3 |= 0x02;
if (dot == 1) seg2 |= 0x02;
if (dot == 0) seg1 |= 0x02;
#else
if (dot == 2) seg3 &= ~0x02;
if (dot == 1) seg2 &= ~0x02;
if (dot == 0) seg1 &= ~0x02;
#endif
// Обновление массива дисплея новыми данными:
cli();
display[indicator + 2] = seg1;
display[indicator + 1] = seg2;
display[indicator + 0] = seg3;
sei();
}
// Отображение разрядов индикаторов
ISR(TIMER2_OVF_vect) {
uint8_t seg = 0;
#ifdef DISPLAY_COMMON_CATHODE
PORTB |= 0x38; // Выключить все разряды первого индикатора
PORTC |= 0x38; // Выключить все разряды второго индикатора
#else
PORTB &= ~0x38; // Выключить все разряды первого индикатора
PORTC &= ~0x38; // Выключить все разряды второго индикатора
#endif
// бесконечный перебор шести разрядов
if (++currentDig > 5) {
currentDig = 0;
}
seg = display[currentDig];
PORTD = (PORTD & 0x03) | seg; // управление сегментами A...F (PD2-PD7)
PORTB = (PORTB & 0xFC) | (seg & 0x03); // управление сегментом G и точкой (PB0,PB1)
// включение текущего разряда
#ifdef DISPLAY_COMMON_CATHODE
if (currentDig < 3) {
PORTB &= ~(1 << (currentDig + 3));
} else {
PORTC &= ~(1 << (8 - currentDig));
}
#else
if (currentDig < 3) {
PORTB |= (1 << (currentDig + 3));
} else {
PORTC |= (1 << (8 - currentDig));
}
#endif
}
/******** АЦП ************************************************************************************************************************/
ISR(ADC_vect) {
static uint32_t sum[3] = { 0 };
static uint8_t count[3] = { 0 };
static uint8_t hz[3] = { 0 }; // Частота обновления
static uint8_t ch = 0; // Текущий канал
int8_t ready = -1; // флаг готовности данных
uint32_t val = 0;
sum[ch] += ADC; // Сохранение результата текущего канала
if (++count[ch] > 32) { // Усреднение после 32 измерений
count[ch] = 0;
val = sum[ch] >> 5;
sum[ch] = 0;
ready = ch;
if (hz[ch] < 255) hz[ch]++;
}
if (++ch > 2) { // Переключение на следующий канал
ch = 0;
}
ADMUX = (ADMUX & 0xF0) | adcChannels[ch]; // изменение канала в ADMUX (сохраняя REFS0)
// val = 921; // тест показаний дисплея при 4.5 В
switch (ready) {
case 0:
if (hz[0] >= FREQ_TRIM) {
hz[0] = 0;
val *= (HIGH_VALUE_DISLAY_TRIM - LOW_VALUE_DISLAY_TRIM);
updatedisplay((uint16_t)(LOW_VALUE_DISLAY_TRIM + val / 1023), DOT_TRIM, INDICATOR_1);
}
break;
case 1:
if (hz[1] >= FREQ_PRESSURE) {
hz[1] = 0;
val *= (HIGH_VALUE_DISLAY_PRESSURE - LOW_VALUE_DISLAY_PRESSURE);
updatedisplay((uint16_t)(LOW_VALUE_DISLAY_PRESSURE + val / 1023), DOT_PRESSURE, INDICATOR_2);
}
break;
case 2:
hz[2] = 0;
break;
default:
break;
}
ready = -1;
}
void setup() {
/** Порты */
DDRD = 0xFC; // выходы PD2...PD7 - A...F
DDRB = 0x3B; // выходы PB0, PB1 - G,H; PB3...PB5 - Dig1...Dig3 (displayTRIM)
DDRC = 0x38; // выходы PC3...PC5 - Dig4...Dig6 (displayPRESSURE)
#ifdef DISPLAY_COMMON_CATHODE
PORTB |= 0x38; // Выключить все разряды displayTRIM
PORTC |= 0x38; // Выключить все разряды displayPRESSURE
#else
PORTB &= ~0x38; // Выключить все разряды displayTRIM
PORTC &= ~0x38; // Выключить все разряды displayPRESSURE
#endif
/** ADC */
ADMUX = (1 << REFS0) | adcChannels[0]; // AVCC, канал первого датчика
ADCSRA = (1 << ADEN) | // Включить АЦП
(1 << ADIE) | // Разрешить прерывание по завершению
(1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // предделитель 128
// Free Running Mode
ADCSRB = 0;
ADCSRA |= (1 << ADATE) | (1 << ADSC);
/** TIM2 */
TCCR2A = 0;
TCCR2B = (1 << CS22) | (1 << CS20);
TIMSK2 |= (1 << TOIE2);
sei();
}
void loop() {
}
а какой удобно? он в любом случае будет настраиваемый.