forked from borislav968/swapcluster
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathswapcluster.c
214 lines (182 loc) · 11.8 KB
/
swapcluster.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#define F_CPU 16000000UL // Тактовая частота 16МГц
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h> // для вывода строк напрямую из flash-памяти
#include <avr/eeprom.h> // для хранения настроек
#include <avr/wdt.h> // сторожевой таймер
#include <stdio.h> // для sprintf()
#define UART_BAUD_RATE 57600UL // Скорость передачи UART 57600 бит/с
#define SET_UBRR ((F_CPU/(16UL*UART_BAUD_RATE))-1UL) // Значения регистров для настройки скорости UART
// Хранятся в EEPROM:
uint8_t EEMEM imp_in_E; // число входных импульсов
uint8_t EEMEM imp_out_E; // число выходных импульсов
// Используются при работе программы
uint8_t imp_in = 1; // число входных импульсов
uint8_t imp_out = 1; // число выходных импульсов
uint16_t period = UINT16_MAX; // измеренный период сигнала в 1/62500с
uint8_t time = 0; // используется в главном цикле
uint8_t setup_mode = 0; // флаг режима настройки
char buffer [50]; // буфер для вывода строк в UART
char rxbuf = 0; // буфер приёма из UART
// Настроить UART
void init_uart () {
UBRR0H = (uint8_t)(SET_UBRR>>8); // настройка скорости
UBRR0L = (uint8_t)SET_UBRR;
UCSR0B |= (1<<TXEN0); // разрешение передачи
UCSR0B |= (1<<RXEN0); // разрешение приёма
UCSR0B |= (1<<RXCIE0); // включение прерывания по приёму
UCSR0C |= (1<<UCSZ00) | (1<<UCSZ01); // 8 бит
}
// Отправить символ в UART
void uart_putc (uint8_t data) {
while (!(UCSR0A & (1<<UDRE0))); // ожидание готовности
UDR0 = data; // отправка
}
// Отправить строку в UART
void uart_puts (char *s) {
while (*s) { // пока строка не закончилась
uart_putc(*s); // отправить очередной символ
s++; // передвинуть указатель на следующий символ
}
}
// Отправить строку из памяти программ в UART
void uart_puts_P (const char *progmem_s) {
register char c;
while ((c = pgm_read_byte(progmem_s++))) { // пока строка не закончилась
uart_putc(c); // отправить очередной символ
}
}
// принять символ из UART
char uart_getc () {
while (!(UCSR0A & (1<<RXC0))); // ожидание приёма
return UDR0; // вернуть принятый байт
}
// Настроить таймер 1 (измерение частоты)
void init_meter () {
TCCR1B |= (1<<ICES1); // захват положительного фронта сигнала
TIMSK1 |= (1<<ICIE1); // включить прерывание по захвату
TIMSK1 |= (1<<TOIE1); // включить прерывание по переполнению
TCCR1B |= (1<<CS12); // делитель 256, частота 62500Гц
}
// Настроить таймер 3 (генератор выходного сигнала)
void init_gen () {
DDRD |= (1<<PD2); // сделать PD2 выходом
PORTD |= (1<<PD2); // для использования таймером, на этом выходе должна быть единица
TCCR3A |= (1<<COM3B1); // режим работы выхода OC3B: неинвертирующий
// Режим работы таймера: Fast PWM со счётом до OCR3A, скважность в OCR3B
TCCR3A |= (1<<WGM31) | (1<<WGM30);
TCCR3B |= (1<<WGM33) | (1<<WGM32);
}
// Настроить таймер 4 (главный цикл)
void init_timer_main () {
TCCR4B |= (1<<WGM42); // режим сравнения
OCR4A = 6250; // отсчёт до 6250, 1/10с
TIMSK4 |= (1<<OCIE4A); // включить прерывание по сравнению
TCCR4B |= (1<<CS42); // делитель 256, частота 62500Гц
}
// Задать частоту генератора
void set_freq (uint16_t freq) {
if (freq == 0) TCCR3B &= ~(1<<CS32); // если заданная частота = 0, остановить генератор
else {
// Запустить генератор, если не запущен. Делитель 256, тактовая частота 62500Гц
if (!(TCCR3B & 0b00000111)) TCCR3B |= (1<<CS32);
OCR3A = (62500 / freq) - 1; // период импульсов в 62500-х долях секунды
OCR3B = OCR3A / 2; // для скважность 50% здесь половина от OCR3A
}
}
// Прочитать настройки из EEPROM
void read_settings () {
imp_in = eeprom_read_byte(&imp_in_E);
if ((imp_in < 1) || (imp_in > 8)) imp_in = 1;
imp_out = eeprom_read_byte(&imp_out_E);
if ((imp_out < 1) || (imp_out > 8)) imp_out = 1;
}
// Прерывание по захвату сигнала таймером 1
ISR (TIMER1_CAPT_vect) {
TCNT1 = 0; // обнулить счётный регистр
period = ICR1; // результат измерения берётся из регистра захвата
}
// Прерывание по переполнению таймера 1
ISR (TIMER1_OVF_vect) {
period = UINT16_MAX;
}
// Прерываение по сравнению таймера 4
ISR (TIMER4_COMPA_vect) {
time++;
}
ISR (USART0_RX_vect) {
if (UDR0 == 's') setup_mode++;
}
// Используется для настройки
void setup_pulses (uint8_t offset, uint8_t * setting) {
register uint8_t correct = 0;
sprintf(buffer, "\x1b[3A\x1b[%uC\x1b[?25h", offset);// переместить курсор в нужное место и показать его
uart_puts(buffer);
while (!correct) { // повторять ожидание ввода, пока не будет введено допустимое значение
rxbuf = uart_getc(); // ожидание ввода
if ((rxbuf >= 0x31) && (rxbuf <= 0x38)) { // если введена цифра от 1 до 8
*setting = rxbuf - 0x30; // присвоить переменной её значение
correct++; // завершить цикл
}
}
uart_puts_P(PSTR("\x1b[?25l\r")); // спрятать курсор
}
int main () {
MCUSR = 0;
wdt_disable();
uint16_t frequency = 0; // частота входного сигнала
uint16_t filtered_frequency = 0; // усредненная отфильтрованная частота входного сигнала
float avg = 0.01; // коэффициент усреднения, определяющий скорость достижения усредняемого значения (<1; чем меньше значение, тем лучше усреднение)
uint16_t freq_out = 0; // частота на выходе
uint16_t rpm = 0; // обороты двигателя
init_uart(); // настроить UART
init_meter(); // настроить таймер 1 (измерение частоты)
init_gen(); // настроить таймер 3 (генератор сигнала)
init_timer_main(); // настроить таймер 4 (главный цикл)
read_settings();
uart_puts_P(PSTR("\x1b[2J\x1b[?25l\n\n"));
uart_puts_P(PSTR("Преобразователь частоты тахометра\n\n\r"));
uart_puts_P(PSTR("вход\tвыход\tоб/мин\n\n\n\r"));
uart_puts_P(PSTR("Для настройки нажмите \"s\"\r\x1b[2A"));
wdt_enable(WDTO_250MS);
sei(); // разрешить все прерывания
while (1) {
if (time) { // если прошло 100мс с прошлого выполнения
wdt_reset(); // сбросить Watchdog
frequency = 62500 / period; // вычислить частоту входного сигнала
if (abs (frequency - filtered_frequency) < 50) // грубая фильтрация от больших скачков
filtered_frequency = avg * frequency + (1 - avg) * filtered_frequency; // усреднить и вычислить плавающую частоту входного сигнала
freq_out = (filtered_frequency / imp_in) * imp_out; // ...выходного сигнала
rpm = (filtered_frequency * 60) / imp_in; // ...обороты двигателя
set_freq(freq_out); // задать новую частоту генератору
sprintf(buffer, "%u \t%u \t%u \r", filtered_frequency, freq_out, rpm); // вывести данные в терминал
uart_puts(buffer);
time = 0; // обнуляется для правильной работы таймера
}
if (setup_mode) { // режим настройки
wdt_disable(); // Watchdog выключается
set_freq(0); // сигнал на выходе выключается
cli(); // запрет всех прерываний
uart_puts_P(PSTR("\x1b[2J\x1b[?25l\n\n"));
uart_puts_P(PSTR("Настройка\x1b\r[6BСохранить и выйти: \"q\"\x1b[4A\r"));
while (1) {
sprintf(buffer, "Вход: %u имп/об, выход %u имп/об\n\n\r", imp_in, imp_out);
uart_puts(buffer);
uart_puts_P(PSTR("Изменить число импульсов: на входе \"i\", на выходе - \"o\"\n\r"));
uart_puts_P(PSTR("Допустимые значения: от 1 до 8\r"));
rxbuf = uart_getc();
if (rxbuf == 'q') { // если нажата q
eeprom_write_byte(&imp_in_E, imp_in); // сохранить настройки в EEPROM
eeprom_write_byte(&imp_out_E, imp_out);
wdt_enable(WDTO_500MS); // запустить Watchdog для перезагрузки
while (1) asm(" "); // ...и ждать его срабатывания
} else if (rxbuf == 'i') { // если нажата i
setup_pulses(6, &imp_in); // ждать ввода значения числа входных импульсов
} else if (rxbuf == 'o') { // если нажата o
setup_pulses(22, &imp_out); // ждать ввода значения числа выходных импульсов
} else uart_puts_P(PSTR("\x1b[3A"));
}
}
asm(" "); // магия, без которой бесконечный цикл не работает
}
}