Update CCS12 peripheral firmware and documentation

This commit is contained in:
2026-09-04 19:34:10 +03:00
parent 55bee25b4f
commit 8cd81736cf
56 changed files with 3808 additions and 504 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
/Debug/
/Release/
/UKSSTMS320F28335.CS_/
/Doc/build/
/Doc/.tools/

167
CAN_ADDRESS_MAP.md Normal file
View File

@@ -0,0 +1,167 @@
# Карта CAN-адресов `Balsam_167_periph` и `BALZAM_167`
## Формирование адреса peripheral
В `Source/Internal/main.c` выполняется:
```c
get_Mode();
InitCan(0, Mode);
```
В `Source/Internal/ecan.c` настроены два extended CAN ID:
```c
long id = 0x80BA0000;
MBOX0.MSGID.all = id + 0x10 + DevNum; /* передача */
MBOX1.MSGID.all = id + DevNum; /* приём */
```
`0x80000000` задаёт extended-формат и не является частью отображаемого
29-битного идентификатора. Фактические адреса на шине:
```text
TX peripheral = 0x00BA0010 + Mode
RX peripheral = 0x00BA0000 + Mode
```
## Адреса всех режимов peripheral
| `Mode` | Назначение | TX peripheral -> BALZAM | RX BALZAM -> peripheral |
|---:|---|---:|---:|
| 1 | `adr_TRN1` | `0x00BA0011` | `0x00BA0001` |
| 2 | `adr_TRN2` | `0x00BA0012` | `0x00BA0002` |
| 3 | `adr_POW1` | `0x00BA0013` | `0x00BA0003` |
| 4 | `adr_POW2` | `0x00BA0014` | `0x00BA0004` |
| 5 | `adr_LOA1`, УМП1 | `0x00BA0015` | `0x00BA0005` |
| 6 | `adr_LOA2`, УМП2 | `0x00BA0016` | `0x00BA0006` |
| 7 | `adr_ENG1` | `0x00BA0017` | `0x00BA0007` |
| 8 | `adr_PULT` | `0x00BA0018` | `0x00BA0008` |
| 9 | `adr_SHKF` | `0x00BA0019` | `0x00BA0009` |
## Приёмные адреса основного контроллера
В `BALZAM_167/Src/balzam_7/CanSetupBalzam7.c` настроено:
| RX mailbox | CAN ID | Запись обработчиком |
|---:|---:|---|
| 0 | `0x00BA0010` | `Unites[1]` |
| 1 | `0x00BA0011` | `Unites[2]` |
| 2 | `0x00BA0012` | `Unites[3]` |
| 3 | `0x00BA0013` | `Unites[4]` |
| 4 | `0x00BA0014` | `Unites[5]` |
| 5 | `0x00BA0015` | `Unites[6]` |
| 6 | `0x00BA0016` | `Unites[7]` |
| 7 | `0x00BA0017` | `Unites[8]` |
| 8 | `0x00BA0018` | `Unites[9]` |
| 9 | `0x00745019` | специальная обработка MPU |
| 10 | `0x00BA001A` | `Unites[11]` |
| 11 | `0x00BA001B` | `Unites[12]` |
Причина такого соответствия — запись в обработчике:
```c
Unites[box + 1][register_address] = received_data;
```
## Имена индексов `Unites`
Текущие определения в `CanSetupBalzam7.h`:
| Индекс | Имя устройства |
|---:|---|
| 1 | `UKSS1_CAN_DEVICE` |
| 2 | `UKSS4_CAN_DEVICE` |
| 3 | `UKSS2_CAN_DEVICE` |
| 4 | `UKSS3_CAN_DEVICE` |
| 5 | `UMP1_CAN_DEVICE` |
| 6 | `UMP2_CAN_DEVICE` |
| 7 | `UKSS8_CAN_DEVICE` |
| 8 | `VPU1_CAN_DEVICE` |
| 9 | `UKSS5_CAN_DEVICE` |
| 10 | `MPU_CAN_DEVICE` |
| 11 | `UKSS6_CAN_DEVICE` |
| 12 | `UKSS7_CAN_DEVICE` |
## Фактическое попадание пакетов
При текущем коде пакет peripheral с `Mode=N` принимается mailbox `N`, после
чего записывается в `Unites[N+1]`.
| Peripheral | TX CAN ID | Фактический `Unites` | Имя по текущим константам |
|---|---:|---:|---|
| `adr_TRN1`, Mode 1 | `0x00BA0011` | `Unites[2]` | `UKSS4_CAN_DEVICE` |
| `adr_TRN2`, Mode 2 | `0x00BA0012` | `Unites[3]` | `UKSS2_CAN_DEVICE` |
| `adr_POW1`, Mode 3 | `0x00BA0013` | `Unites[4]` | `UKSS3_CAN_DEVICE` |
| `adr_POW2`, Mode 4 | `0x00BA0014` | `Unites[5]` | `UMP1_CAN_DEVICE` |
| `adr_LOA1`, Mode 5 | `0x00BA0015` | `Unites[6]` | `UMP2_CAN_DEVICE` |
| `adr_LOA2`, Mode 6 | `0x00BA0016` | `Unites[7]` | `UKSS8_CAN_DEVICE` |
| `adr_ENG1`, Mode 7 | `0x00BA0017` | `Unites[8]` | `VPU1_CAN_DEVICE` |
| `adr_PULT`, Mode 8 | `0x00BA0018` | `Unites[9]` | `UKSS5_CAN_DEVICE` |
| `adr_SHKF`, Mode 9 | `0x00BA0019` | нет приёма | — |
## Связь с ошибкой обрыва фаз
Peripheral передаёт регистры ошибок `modbus[0]`, `modbus[1]` и `modbus[2]`
в CAN-пакете с начальным адресом данных `0`. Признак обрыва/перекоса `Wry`
занимает бит 2:
```text
Wry = 0x0004
```
Основной контроллер выставляет:
```c
mpu_out[125].bit6 =
(Unites[UMP1_CAN_DEVICE][0] & 0x0004) ||
(Unites[UMP1_CAN_DEVICE][1] & 0x0004);
mpu_out[125].bit10 =
(Unites[UMP2_CAN_DEVICE][0] & 0x0004) ||
(Unites[UMP2_CAN_DEVICE][1] & 0x0004);
```
С учётом `UMP1_CAN_DEVICE=5` и `UMP2_CAN_DEVICE=6` получаем:
| Выход MPU | Читаемые данные | CAN ID, заполняющий эти данные |
|---|---|---:|
| `mpu_out[125].bit6` | `Unites[5][0/1]` | `0x00BA0014` |
| `mpu_out[125].bit10` | `Unites[6][0/1]` | `0x00BA0015` |
Но УМП отправляют:
| Плата | Фактический CAN ID | Куда пакет попадает сейчас |
|---|---:|---|
| УМП1, `Mode=5` | `0x00BA0015` | `Unites[6]`, область УМП2 |
| УМП2, `Mode=6` | `0x00BA0016` | `Unites[7]`, область `UKSS8` |
Таким образом, сейчас присутствует смещение адреса на единицу. УМП1 может
влиять на `mpu_out[125].bit10` вместо `bit6`, а УМП2 не попадает ни в один из
двух ожидаемых массивов УМП.
## Обратное направление
Основной контроллер отправляет устройству с индексом `N` через mailbox
`15 + N`, которому назначен CAN ID с номером `N - 1`.
| Получатель | TX CAN ID BALZAM | RX CAN ID peripheral |
|---|---:|---:|
| УМП1 | `0x00BA0004` | `0x00BA0005` |
| УМП2 | `0x00BA0005` | `0x00BA0006` |
В обратном направлении также имеется смещение на единицу.
## Вывод
Перед изменением кода необходимо сверить CAN-трассу и реальные положения
адресных перемычек. После подтверждения адресной схемы возможны два варианта:
1. использовать при настройке CAN peripheral значение `Mode - 1`;
2. перенастроить RX/TX mailbox основного контроллера под текущие значения
`Mode` peripheral.
Комментарии возле mailbox основного проекта частично не совпадают с текущими
константами устройств, поэтому менять адреса только по комментариям нельзя.

67
Doc/Doxyfile Normal file
View File

@@ -0,0 +1,67 @@
# Doxygen configuration for the Balsam 167 peripheral controller firmware.
# Run from this directory with build.bat or: doxygen Doxyfile
PROJECT_NAME = "Balsam 167 Peripheral"
PROJECT_NUMBER = "1.0"
PROJECT_BRIEF = "TMS320F28335 peripheral controller firmware"
OUTPUT_DIRECTORY = build/api
OUTPUT_LANGUAGE = Russian
INPUT_ENCODING = UTF-8
INPUT_FILE_ENCODING = *.c=UTF-8 \
*.h=UTF-8
TAB_SIZE = 4
INPUT = mainpage.md \
api.dox \
files.dox \
README_CCS12.md \
PHASE_BREAK_ERROR_FLOW.md \
PHASE_COMPARISON_BALSAM_165_167.md \
PHASE_VOLTAGE_CALCULATION.md \
PHASE_VOLTAGE_WEAK_POINTS.md \
../Source/Internal
FILE_PATTERNS = *.c *.h *.md *.dox
RECURSIVE = YES
USE_MDFILE_AS_MAINPAGE = mainpage.md
OPTIMIZE_OUTPUT_FOR_C = YES
JAVADOC_AUTOBRIEF = YES
MARKDOWN_SUPPORT = YES
AUTOLINK_SUPPORT = YES
EXTRACT_ALL = YES
EXTRACT_STATIC = YES
EXTRACT_PRIVATE = YES
EXTRACT_LOCAL_CLASSES = YES
TYPEDEF_HIDES_STRUCT = YES
SORT_MEMBER_DOCS = NO
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = NO
SKIP_FUNCTION_MACROS = YES
PREDEFINED = interrupt= \
__interrupt= \
EALLOW= \
EDIS=
ALIASES = precondition="\pre"
QUIET = YES
WARNINGS = YES
WARN_IF_UNDOCUMENTED = NO
WARN_IF_DOC_ERROR = NO
WARN_NO_PARAMDOC = NO
WARN_LOGFILE = build/doxygen-warnings.log
GENERATE_HTML = YES
HTML_OUTPUT = html
GENERATE_TREEVIEW = YES
FULL_SIDEBAR = NO
SOURCE_BROWSER = YES
INLINE_SOURCES = NO
REFERENCED_BY_RELATION = YES
REFERENCES_RELATION = YES
ALPHABETICAL_INDEX = YES
SEARCHENGINE = YES
GENERATE_LATEX = NO
HAVE_DOT = NO

View File

@@ -0,0 +1,337 @@
@page phase_break_error_flow Детектирование и передача ошибки обрыва фаз
# Детектирование и передача ошибки обрыва фаз
## 1. Назначение
Документ описывает полную связь от входов АЦП платы УМП до выставления
признаков обрыва фаз в `modbus_table_mpu_out[125]` основного контроллера
`BALZAM_167`.
В текущей реализации обрыв определяется как сильный перекос действующих
значений фаз. Отдельных независимых флагов для физических фаз A, B и C нет.
Передаётся общий флаг `Wry` с маской `0x0004`.
## 2. Общая цепочка сигнала
```text
Фазные напряжения
|
v
АЦП peripheral-платы
CONV02 -> channel 0 -> измеряемая фаза A
CONV03 -> channel 1 -> измеряемая фаза C
|
v
Current_count(0), Current_count(1)
|
+--> RMS A = lev_count[0]
+--> RMS C = lev_count[1]
+--> RMS B = lev_count[4], расчёт B = -(A + C)
|
v
Выбор Max = max(RMS A, RMS B, RMS C)
|
v
(Max - RMS проверяемой фазы) / Max > 0.30
AND Max > Curr_Edge
|
v
Счётчик выдержки er_anal()
|
v
error.bit.Wry = 1 (бит 2, маска 0x0004)
|
v
sens_error[0] / sens_error[1]
|
v
modbus[0] / modbus[1]
|
v
Быстрый CAN-цикл peripheral-платы
|
v
Unites[UMP1_CAN_DEVICE][0/1]
или Unites[UMP2_CAN_DEVICE][0/1]
|
v
BALZAM_167, update_abnormal.c
|
+--> mpu_out[125].bit6 — УМП1
+--> mpu_out[125].bit10 — УМП2
```
## 3. Связь входов АЦП с фазами
Для режима УМП (`Desk == dsk_LOAD`) преобразования настроены в
`Source/Internal/ADC.c`.
| АЦП | Физический вход | Внутренний канал | Назначение |
|---|---:|---:|---|
| `CONV02` | `ADCINA5` | `0` | измеряемое напряжение фазы A |
| `CONV03` | `ADCINA4` | `1` | измеряемое напряжение фазы C |
| `CONV04` | `ADCINA7` | `2` | ток первого канала |
| `CONV05` | `ADCINA2` | `3` | ток второго канала |
В обработчике АЦП результаты `ADCRESULT2...ADCRESULT5` записываются в
`adc_table_lem[0...3]`, после чего для каждого канала вызывается
`Current_count(i)`.
## 4. Расчёт действующих значений
Из отсчёта АЦП вычитается сохранённый нулевой уровень и применяется
калибровочный коэффициент:
```c
Current = (Numb - Zero_lev[chan]) * powK[chan];
```
Действующее значение измеряемой фазы рассчитывается через экспоненциальное
усреднение квадрата:
```c
lev_quadr[chan] +=
((Current * Current) - lev_quadr[chan]) / (1.0 * ADC_FREQ);
lev_count[chan] = sqrt(lev_quadr[chan]);
```
Третья фаза непосредственно не измеряется. Она восстанавливается из условия
нулевой суммы фаз:
```text
A + B + C = 0
B = -(A + C)
```
В коде:
```c
Numb = -Current - aCurrent;
lev_quadr[thrd] +=
(Numb * Numb - lev_quadr[thrd]) / (1.0 * ADC_FREQ);
lev_count[thrd] = sqrt(lev_quadr[thrd]);
```
Диагностические значения публикуются в следующих регистрах:
| Регистр peripheral | Значение |
|---:|---|
| `0x68` | RMS фазы A |
| `0x69` | RMS фазы C |
| `0x6A` | расчётный RMS фазы B |
| `0x6B` | RMS первого токового канала |
| `0x6C` | RMS второго токового канала |
| `0x6D` | расчётный RMS третьего тока |
## 5. Условие выставления `Wry`
Для каждой пары выбирается максимальный уровень:
```c
Max = max(lev_count[chan], lev_count[pair], lev_count[thrd]);
```
Ошибка набирается при выполнении обоих условий:
```text
(Max - PhaseRms) / Max > 0.30
Max > Curr_Edge
```
Для УМП устанавливается:
```c
Curr_Edge = 300;
```
Следовательно, флаг формируется, когда:
1. максимальная из трёх фаз больше `300`;
2. проверяемая фаза ниже максимальной более чем на 30%;
3. счётчик `er_anal()` достиг выдержки.
Пример:
```text
RMS A = 400
RMS B = 390
RMS C = 100
Max = 400
(400 - 100) / 400 = 0.75
0.75 > 0.30 -> условие обрыва/перекоса фазы C активно
```
Если все три уровня одновременно ниже `Curr_Edge`, `Wry` не выставляется.
Полное пропадание напряжения должно определяться диагностикой низкого уровня
`Out`, а не фазовым перекосом `Wry`.
## 6. Выдержка времени
Частота обработки:
```c
#define ADC_FREQ 3750
```
Порог счётчика:
```c
time_3sec = 3 * ADC_FREQ; /* 11250 */
```
Функция `er_anal()` увеличивает счётчик на каждом ошибочном отсчёте и уменьшает
его на каждом нормальном отсчёте:
```c
if (term) {
if (*count >= edge)
return 1;
(*count)++;
return 0;
}
if (*count == 0)
return 0;
(*count)--;
return 0;
```
Для измеряемых фаз A и C номинальная выдержка составляет около 3 секунд.
Расчётная фаза B проверяется при обработке обоих измеряемых каналов, поэтому
её общий счётчик увеличивается дважды за цикл АЦП. Фактическая выдержка для B
может составлять около 1,5 секунды.
После запуска УМП диагностика блокируется на время `WAKE`:
```c
WAKE_TIME = 10L * ADC_FREQ;
```
То есть первые 10 секунд после запуска `Wry` не формируется.
## 7. Формирование регистра ошибки peripheral-платы
Структура регистра ошибки определена в `Source/Internal/Include/measure.h`:
| Бит | Маска | Поле | Назначение |
|---:|---:|---|---|
| 0 | `0x0001` | `Tear` | крайнее значение АЦП/обрыв датчика |
| 2 | `0x0004` | `Wry` | перекос или предполагаемый обрыв фазы |
| 3 | `0x0008` | `Out` | низкий общий уровень |
| 4 | `0x0010` | `Over` | превышение тока |
| 5 | `0x0020` | `Hyper` | превышение напряжения |
| 8 | `0x0100` | `Stop` | требование аварийного останова |
| 9 | `0x0200` | `Ready` | готовность канала |
| 14 | `0x4000` | `Ignor` | запрет локального останова |
| 15 | `0x8000` | `Bypas` | полное исключение канала |
После достижения выдержки выполняется:
```c
error.bit.Wry = 1;
if (!ignor)
error.bit.Stop = 1;
```
Поведение управляющих признаков:
| Состояние | `Wry` | `Stop` | Передача по CAN |
|---|---:|---:|---:|
| обычный канал | 1 | 1 | да |
| `Ignor = 1` | 1 | 0 | да |
| `Bypas = 1` | 0 | 0 | передаётся регистр без ошибки |
| `WAKE != 0` | 0 | 0 | передаётся регистр без ошибки |
Для УМП признаки напряжений находятся в регистрах ошибок:
| Регистр | Содержит `Wry` при проблеме |
|---:|---|
| `modbus[0]` | измеряемая фаза A или расчётная фаза B |
| `modbus[1]` | измеряемая фаза C или расчётная фаза B |
В основном контроллере регистры `0` и `1` объединяются логическим ИЛИ, поэтому
для индикации достаточно наличия маски `0x0004` в любом из них.
## 8. Передача по CAN
`Init_packMask()` включает регистры ошибок УМП `0...3` в быстрый CAN-цикл.
Главный цикл peripheral-платы вызывает:
```c
CAN_send(0, modbus, address);
```
Один CAN-пакет содержит начальный адрес и три 16-битных регистра. Основной
контроллер принимает пакет и записывает данные в массив:
```text
Unites[номер устройства][адрес регистра]
```
Соответствие устройств:
| Устройство | CAN-индекс основного контроллера | Регистры флага |
|---|---:|---:|
| УМП1 | `UMP1_CAN_DEVICE = 5` | `Unites[5][0]`, `Unites[5][1]` |
| УМП2 | `UMP2_CAN_DEVICE = 6` | `Unites[6][0]`, `Unites[6][1]` |
## 9. Выставление `mpu_out[125]`
Основной контроллер выполняет проверку в
`BALZAM_167/Src/balzam_7/update_abnormal.c`.
### УМП1
```c
modbus_table_mpu_out[125].bit.bit6 =
(Unites[UMP1_CAN_DEVICE][0] & 0x4) ||
(Unites[UMP1_CAN_DEVICE][1] & 0x4)
? 1 : 0;
```
### УМП2
```c
modbus_table_mpu_out[125].bit.bit10 =
(Unites[UMP2_CAN_DEVICE][0] & 0x4) ||
(Unites[UMP2_CAN_DEVICE][1] & 0x4)
? 1 : 0;
```
Оба признака разрешены только при следующем условии:
```c
f.FittingScheme == 1
```
Если выбрана другая схема, основной контроллер принудительно записывает ноль
в `mpu_out[125].bit6` и `mpu_out[125].bit10` независимо от данных УМП.
## 10. Итоговая таблица связей
| Физическое событие | Peripheral-регистр | Передаваемая маска | Приёмник | Выход MPU |
|---|---:|---:|---|---|
| перекос/обрыв A на УМП1 | `modbus[0]` | `0x0004` | `Unites[5][0]` | `mpu_out[125].bit6` |
| расчётный перекос B на УМП1 | `modbus[0]` или `[1]` | `0x0004` | `Unites[5][0/1]` | `mpu_out[125].bit6` |
| перекос/обрыв C на УМП1 | `modbus[1]` | `0x0004` | `Unites[5][1]` | `mpu_out[125].bit6` |
| перекос/обрыв A на УМП2 | `modbus[0]` | `0x0004` | `Unites[6][0]` | `mpu_out[125].bit10` |
| расчётный перекос B на УМП2 | `modbus[0]` или `[1]` | `0x0004` | `Unites[6][0/1]` | `mpu_out[125].bit10` |
| перекос/обрыв C на УМП2 | `modbus[1]` | `0x0004` | `Unites[6][1]` | `mpu_out[125].bit10` |
## 11. Ограничения алгоритма
1. `Wry` означает как обрыв, так и сильный перекос более 30%; эти события не
различаются.
2. Фаза B не измеряется непосредственно. Если при физическом обрыве B фазы A
и C остаются нормальными, расчётная B также может выглядеть нормальной и
ошибка не будет обнаружена.
3. Одновременное исчезновение всех фаз не создаёт `Wry`, потому что не
выполняется условие `Max > 300`.
4. Из-за двойной проверки расчётной фазы B её выдержка короче выдержки A и C.
5. Выходные биты `mpu_out[125].bit6` и `bit10` не показывают конкретную фазу,
а только общий факт фазового перекоса соответствующего УМП.

View File

@@ -1,3 +1,5 @@
@page phase_comparison_balsam Сравнение фазовой части Balsam 165 и Balsam 167
# Сравнение фазовой части Balsam 165 и Balsam 167
## Область сравнения

View File

@@ -1,3 +1,5 @@
@page phase_voltage_calculation Расчёт входных фаз, диагностика обрыва и уровни напряжения
# Расчёт входных фаз, диагностика обрыва и уровни напряжения
Документ составлен по исходникам прошивки Balsam 167 и карте регистров

View File

@@ -1,3 +1,5 @@
@page phase_voltage_weak_points Слабые места расчёта фаз и контроля напряжения
# Слабые места расчёта фаз и контроля напряжения
Документ относится к алгоритмам из `Source/Internal/measure.c`, структуре

16
Doc/README.md Normal file
View File

@@ -0,0 +1,16 @@
# Документация Balsam 167
Запустите `build.bat` из этого каталога. Скрипт всегда собирает обзорную
страницу в `build/index.html`. Скрипт использует переносимый Doxygen из
`.tools/doxygen-1.18.0` либо `doxygen.exe` из `PATH` и создаёт API-справочник
в `build/api/html/index.html`. Без Doxygen по
этому адресу создаётся поясняющая страница, поэтому ссылка из обзора не
остаётся битой.
Исходные файлы старого проекта сохранены в CP1251. `Doxyfile` явно задаёт эту
кодировку для `*.c` и `*.h`, а новые файлы документации хранятся в UTF-8.
```bat
cd Doc
build.bat
```

View File

@@ -25,5 +25,29 @@
- CCS 12 Release создаёт `UKSSTMS320F28335_Release.out` и
`UKSSTMS320F28335_Release.map` в `Bin/CCS12`.
## Публикация прошивки
`publish_firmware.bat` создаёт Gitea Release, загружает образ, вычисляет
SHA-256 и обновляет `firmware.releases` общего `update.json`. Перед запуском
сохраните вход Gitea в SETGUI. Альтернативно задайте `GITEA_TOKEN` либо пару
`GITEA_USER`/`GITEA_PASSWORD` в окружении CCS.
Пример отдельного шага после сборки CCS:
```bat
"${SRC_ROOT}\publish_firmware.bat" "${SRC_ROOT}\Bin\CCS12\UKSSTMS320F28335.bin" "1.0.0" "Описание изменений" "tms"
```
Путь к образу зависит от конфигурации CCS; первым параметром должен быть
фактический полный путь созданного `.bin` или `.hex`. Версия задаётся в формате
`MAJOR.MINOR.PATCH`. Повторный запуск той же версии заменяет asset и запись
каталога, не создавая дубликат.
Для проверки файла и доступа к Gitea без публикации добавьте пятый параметр:
```bat
"${SRC_ROOT}\publish_firmware.bat" "${SRC_ROOT}\Bin\CCS12\UKSSTMS320F28335.bin" "1.0.0" "Проверка" "tms" "check"
```
Все пути к исходникам заданы относительно каталога проекта через `SRC_ROOT`;
перенос репозитория в другой каталог не требует изменения настроек.

10
Doc/api-unavailable.html Normal file

File diff suppressed because one or more lines are too long

723
Doc/api.dox Normal file
View File

@@ -0,0 +1,723 @@
/**
@file api.dox
@brief Русскоязычные Doxygen-описания интерфейсов Balsam 167.
@defgroup measurement Измерения и диагностика
@brief АЦП, фильтрация, пересчёт каналов и формирование аварий.
@{
*/
/**
@fn void setup_adc(void)
@brief Настраивает ADC и быстрый обработчик преобразований.
@details Конфигурирует последовательность каналов и параметры накопления,
используемые измерительным контуром. Вызывается один раз при старте для всех
ролей, кроме пульта EPLT.
*/
/**
@fn void adc_isr(void)
@brief Обрабатывает очередную последовательность преобразований ADC.
@details Обновляет сырые выборки и фильтры; выполняется в контексте прерывания.
*/
/**
@fn void Init_sensors(void)
@brief Выполняет первичную инициализацию измерительных каналов.
*/
/**
@fn void Init_sensors_more(void)
@brief Применяет параметры изделия после загрузки сохранённых настроек.
*/
/**
@fn void Init_packMask(void)
@brief Формирует маски публикуемых CAN-регистров для каждой шины.
*/
/**
@fn void Temper_count(int chan, int own)
@brief Пересчитывает температурный канал и обновляет его диагностику.
@param chan Индекс базового канала в общей модели измерений.
@param own Признак локального либо внешнего источника температуры.
*/
/**
@fn void Current_count(int chan)
@brief Вычисляет ток по паре измерительных каналов и формирует ошибки.
@param chan Индекс канала тока.
*/
/**
@fn void Power_count(int chan)
@brief Вычисляет напряжение/мощность для заданного канала.
@param chan Индекс обрабатываемого канала.
*/
/**
@fn void calc_sensor_koef(void)
@brief Пересчитывает коэффициенты датчиков по калибровочным точкам.
*/
/**
@fn void calc_volta_edge(void)
@brief Пересчитывает пороги контроля напряжения из текущих настроек.
*/
/**
@fn void Is_Voltage_Hi(void)
@brief Проверяет превышение напряжения и обновляет общий признак аварии.
*/
/**
@fn void cpu_timer1_isr_SENS(void)
@brief Периодический обработчик измерительной платы.
@details Планирует выбор каналов, пересчёт величин, диагностику, мигание и
циклическую передачу. Выполняется в контексте прерывания CPU Timer1.
*/
/**
@fn int er_anal(int term, unsigned int *count, long edge, int pre)
@brief Реализует выдержку времени для появления и снятия диагностического условия.
@param term Текущее логическое состояние диагностического условия.
@param count Счётчик длительности условия; изменяется функцией.
@param edge Порог счётчика, после которого условие подтверждается.
@param pre Состояние ошибки на предыдущем шаге.
@return Ненулевое значение, когда ошибка считается активной.
*/
/**
@struct FILTERBAT
@brief Состояние рекурсивного фильтра второго порядка.
@details Содержит три коэффициента, три входные и три выходные выборки.
*/
/**
@fn float filterbat(FILTERBAT *b, float InpVarCurr)
@brief Пропускает одну выборку через фильтр Баттерворта.
@param b Экземпляр фильтра с коэффициентами и историей.
@param InpVarCurr Новая входная выборка.
@return Отфильтрованное значение.
*/
/**
@struct ERROR
@brief Шестнадцатибитное слово состояния измерительного канала.
@details Объединяет признаки обрыва, неправильного сигнала, выхода за пределы,
перегрева, блокировки, готовности, дискретных входов, игнорирования и bypass.
*/
/**
@struct FLAG
@brief Общие признаки ошибки, аварии, нагрева и теста ламп.
*/
/**
@}
*/
/**
@defgroup communication Последовательный обмен и сообщения
@brief Два канала SCI, физический режим RS-485 и прикладные команды.
@{
*/
/**
@struct RS_DATA
@brief Контекст одного SCI/RS-485 канала.
@details Хранит регистры SCI, указатели RX/TX, буферы, длины сообщений,
тайм-ауты, адреса и параметры линии. Глобальные экземпляры: `rs_a` и `rs_b`.
*/
/**
@fn void create_uart_vars(char size_cmd15_set)
@brief Инициализирует контексты двух UART и таблицу длин команд.
@param size_cmd15_set Размер payload команды 15 для текущего приложения.
*/
/**
@fn void setup_uart(char commnumber, unsigned long speed_baud)
@brief Настраивает SCI-A или SCI-B и соответствующие обработчики PIE.
@param commnumber Номер порта: `COM_1` либо `COM_2`.
@param speed_baud Скорость линии, бит/с.
*/
/**
@fn void RS_SetLineMode(RS_DATA *rs_arr, int bit, char parity, int stop)
@brief Задаёт длину слова, чётность и число стоп-битов.
@param rs_arr Контекст канала.
@param bit Число информационных битов.
@param parity Режим контроля чётности.
@param stop Режим стоп-битов.
*/
/**
@fn void RS_SetLineSpeed(RS_DATA *rs_arr, unsigned long speed)
@brief Изменяет скорость выбранного последовательного канала.
@param rs_arr Контекст канала.
@param speed Новая скорость, бит/с.
*/
/**
@fn void RS_SetBitMode(RS_DATA *rs_arr, int n)
@brief Выбирает байтовый либо упакованный режим данных SCI.
@param rs_arr Контекст канала.
@param n Требуемый режим представления элементов.
*/
/**
@fn int RS_Send(RS_DATA *rs_arr, unsigned int *pBuf, unsigned long len)
@brief Запускает неблокирующую передачу массива слов.
@param rs_arr Контекст канала.
@param pBuf Буфер передаваемых слов.
@param len Число слов.
@return Признак успешного запуска передачи.
*/
/**
@fn int RS_BSend(RS_DATA *rs_arr, unsigned int *pBuf, unsigned long len)
@brief Запускает передачу байтов, хранящихся в 16-битных ячейках C28x.
@param rs_arr Контекст канала.
@param pBuf Буфер данных.
@param len Число восьмибитных элементов.
@return Признак успешного запуска передачи.
*/
/**
@fn int get_command(RS_DATA *rs_arr)
@brief Проверяет принятый кадр и извлекает код команды.
@param rs_arr Контекст канала с принятым пакетом.
@return Код команды либо `-1`, если полного корректного кадра нет.
*/
/**
@fn void RSA_RX_Handler(void)
@brief Обработчик приёма SCI-A, делегирующий работу общему RX-автомату.
*/
/**
@fn void RSA_TX_Handler(void)
@brief Обработчик передачи SCI-A.
*/
/**
@fn void RSB_RX_Handler(void)
@brief Обработчик приёма SCI-B, делегирующий работу общему RX-автомату.
*/
/**
@fn void RSB_TX_Handler(void)
@brief Обработчик передачи SCI-B.
*/
/**
@fn void clear_timer_rs_live(RS_DATA *rs_arr)
@brief Сбрасывает счётчик контроля активности последовательного канала.
@param rs_arr Контекст канала.
*/
/**
@fn void test_rs_live(RS_DATA *rs_arr)
@brief Проверяет тайм-аут активности последовательного канала.
@param rs_arr Контекст канала.
*/
/**
@fn void ReceiveCommandModbus3(RS_DATA *rs_arr)
@brief Обрабатывает Modbus function 3 — чтение регистров.
@param rs_arr Канал, на котором принята команда.
*/
/**
@fn void ReceiveCommandModbus6(RS_DATA *rs_arr)
@brief Обрабатывает Modbus function 6 — запись одного регистра.
@param rs_arr Канал, на котором принята команда.
*/
/**
@fn void SendCommandModbus4(RS_DATA *rs_arr)
@brief Формирует запрос function 4 к внешнему устройству OWEN.
@param rs_arr Канал связи с устройством.
*/
/**
@fn void ReceiveAnswerModbus4(RS_DATA *rs_arr)
@brief Разбирает ответ OWEN на запрос входных регистров.
@param rs_arr Канал, на котором принят ответ.
*/
/**
@struct CMD_TO_TMS
@brief Формат короткой команды контроллеру.
@details Содержит адрес, номер команды, восемь байтов данных, CRC и добавочный байт.
*/
/**
@}
*/
/**
@defgroup can_bus CAN
@brief Инициализация eCAN-A, приём команд и публикация регистров.
@{
*/
/**
@fn void InitCan(int Port, int DevNum)
@brief Настраивает CAN-контроллер и почтовые ящики.
@param Port Зарезервированный номер CAN-порта; текущая плата использует порт 0.
@param DevNum Адрес/режим устройства, участвующий в конфигурации идентификаторов.
*/
/**
@fn void CAN_send(int Port, int data[], int Addr)
@brief Передаёт группу из трёх регистров общей модели.
@param Port Номер CAN-порта.
@param data Начало массива регистров.
@param Addr Адрес первого публикуемого регистра.
*/
/**
@fn void CANa_handler(void)
@brief Обработчик приёма eCAN-A; переносит данные mailbox в прикладную модель.
*/
/**
@fn void CANa_reset_err(void)
@brief Обрабатывает состояние ошибки eCAN-A и восстанавливает обмен.
*/
/**
@}
*/
/**
@defgroup storage Параметры, EEPROM и журнал
@brief Долговременное хранение конфигурации и диагностических выборок.
@{
*/
/**
@struct SE2P_DATA
@brief Описание операции с последовательной EEPROM.
@details Содержит указатель данных, размер операции и адрес EEPROM.
*/
/**
@struct SPISE2P_DRV
@brief Состояние и виртуальные методы драйвера SPI EEPROM.
*/
/**
@fn void InitSeeprom(void)
@brief Настраивает SPI-A, GPIO chip-select и Timer2 для EEPROM.
*/
/**
@fn void Seeprom_write(unsigned int adres, unsigned int buf[], unsigned int size)
@brief Синхронно записывает блок 16-битных слов во внешнюю EEPROM.
@param adres Адрес слова в EEPROM.
@param buf Буфер исходных слов.
@param size Количество записываемых слов.
*/
/**
@fn void Seeprom_read(unsigned int adres, unsigned int buf[], unsigned int size)
@brief Синхронно читает блок 16-битных слов из внешней EEPROM.
@param adres Адрес слова в EEPROM.
@param buf Буфер результата.
@param size Количество читаемых слов.
*/
/**
@fn void Default_params(void)
@brief Загружает заводские уставки и калибровочные значения в RAM.
*/
/**
@fn void Load_params(void)
@brief Читает настройки из EEPROM и проверяет их целостность.
*/
/**
@fn void Save_params(void)
@brief Записывает текущие настройки и контрольную сумму в EEPROM.
*/
/**
@struct LOG
@brief Границы и текущий указатель циклического журнала во внешней памяти.
*/
/**
@fn void clear_mem(void)
@brief Инициализирует область журнала и очищает её рабочее состояние.
*/
/**
@}
*/
/**
@defgroup hardware Аппаратные интерфейсы
@brief GPIO, режим платы, ЦАП и последовательная индикация.
@{
*/
/**
@fn void get_Mode(void)
@brief Читает конфигурационные входы и определяет `Mode` и `Desk`.
*/
/**
@fn void get_Buttons(void)
@brief Опросивает дискретные входы и обновляет биты кнопок/команд.
*/
/**
@fn void setup_leds_line(void)
@brief Временно настраивает линии двух диагностических светодиодов.
*/
/**
@fn void unsetup_leds_line(void)
@brief Возвращает мультиплексируемые линии из режима стартовой индикации.
*/
/**
@fn void select_tpl_canal(int n_tpl)
@brief Выбирает один канал мультиплексора термопар.
@param n_tpl Номер канала термопары.
*/
/**
@fn void select_tpl_255(void)
@brief Переводит адресные линии мультиплексора в неактивное состояние.
*/
/**
@fn void Setup_DAC_time(void)
@brief Вычисляет временные параметры программного обслуживания ЦАП.
*/
/**
@fn void Init_DAC(void)
@brief Инициализирует GPIO последовательного интерфейса ЦАП.
*/
/**
@fn void Anal_output(long vrot, long maxx)
@brief Выдаёт нормированное значение на аналоговый выход.
@param vrot Требуемое значение.
@param maxx Верхняя граница шкалы входного значения.
*/
/**
@fn void Load_runner(void)
@brief Обновляет выход нагрузки и формирует старт/стоп импульсы.
*/
/**
@fn void kanal_Send(int adr, long dat, int dot)
@brief Передаёт число на внешний семисегментный индикатор.
@param adr Адрес индикаторного канала.
@param dat Отображаемое целое значение.
@param dot Позиция десятичной точки.
*/
/**
@fn void cpu_timer1_isr_PULT(void)
@brief Периодический обработчик пульта и внешней индикации.
*/
/**
@}
*/
/**
@defgroup boot_protocol BIOS-протокол
@brief Команды удалённого чтения, записи, запуска и прошивки контроллера.
@{
*/
/**
@fn void Answer(RS_DATA *rs_arr, int n)
@brief Формирует и отправляет ответ BIOS-протокола.
@param rs_arr Канал ответа.
@param n Число элементов данных ответа.
*/
/**
@fn void init(RS_DATA *rs_arr)
@brief Возвращает идентификацию работающего приложения.
@param rs_arr Канал запроса.
*/
/**
@fn void initload(RS_DATA *rs_arr)
@brief Инициализирует сеанс загрузки программы.
@param rs_arr Канал запроса.
*/
/**
@fn void load(RS_DATA *rs_arr)
@brief Принимает очередной блок загружаемой программы.
@param rs_arr Канал запроса.
*/
/**
@fn void run(RS_DATA *rs_arr)
@brief Завершает загрузку и передаёт управление программе.
@param rs_arr Канал запроса.
*/
/**
@fn void peek(RS_DATA *rs_arr)
@brief Читает участок адресного пространства контроллера.
@param rs_arr Канал запроса и ответа.
*/
/**
@fn void poke(RS_DATA *rs_arr)
@brief Записывает данные в адресное пространство контроллера.
@param rs_arr Канал запроса.
*/
/**
@fn void upload(RS_DATA *rs_arr)
@brief Передаёт блок памяти контроллера ведущему устройству.
@param rs_arr Канал запроса и ответа.
*/
/**
@fn void tflash(RS_DATA *rs_arr)
@brief Выполняет команду обслуживания Flash TMS320F28335.
@param rs_arr Канал запроса и ответа.
*/
/**
@fn void xflash(RS_DATA *rs_arr)
@brief Обрабатывает совместимую команду доступа к внешней Flash/памяти.
@param rs_arr Канал запроса и ответа.
*/
/**
@fn void extendbios(RS_DATA *rs_arr)
@brief Обрабатывает расширенную команду BIOS-протокола.
@param rs_arr Канал запроса и ответа.
*/
/**
@fn unsigned int read_memory(unsigned long addr)
@brief Читает 16-битное слово по физическому адресу.
@param addr Адрес в пространстве данных C28x.
@return Прочитанное слово.
*/
/**
@fn void write_memory(unsigned long addr, unsigned int data)
@brief Записывает 16-битное слово по физическому адресу.
@param addr Адрес в пространстве данных C28x.
@param data Записываемое слово.
*/
/**
@}
*/
/**
@defgroup utilities Служебные функции
@brief CRC, внешняя зона памяти и точные задержки.
@{
*/
/**
@fn unsigned int get_crc_ccitt(unsigned int crc, unsigned int *buf, unsigned long size)
@brief Вычисляет CRC-CCITT для массива восьмибитных значений в словах C28x.
@param crc Начальное значение CRC.
@param buf Буфер входных значений.
@param size Число элементов.
@return Итоговое значение CRC.
*/
/**
@fn unsigned int get_crc_16(unsigned int crc, unsigned int *buf, unsigned long size)
@brief Вычисляет основной вариант CRC-16 проекта.
@param crc Начальное значение CRC.
@param buf Буфер входных значений.
@param size Число элементов.
@return Итоговое значение CRC.
*/
/**
@fn unsigned int get_crc_16b(unsigned int crc, unsigned int *buf, unsigned long size)
@brief Вычисляет CRC-16 с альтернативным порядком байтов.
@param crc Начальное значение CRC.
@param buf Буфер входных значений.
@param size Число элементов.
@return Итоговое значение CRC.
*/
/**
@fn int get_crc16(unsigned int *buf, int size)
@brief Вычисляет контрольное слово пакета прикладного протокола.
@param buf Буфер пакета.
@param size Число обрабатываемых элементов.
@return Шестнадцатибитное контрольное значение.
*/
/**
@fn void init_zone7(void)
@brief Настраивает зону XINTF7 для внешней памяти и периферии.
*/
/**
@fn void pause_us(unsigned long t)
@brief Выполняет программную задержку.
@param t Длительность в микросекундах согласно частоте проекта.
*/
/**
@fn void set_cntrl_addr(int cntrl_addr, int cntrl_addr_for_all)
@brief Устанавливает индивидуальный и групповой адреса контроллера.
@param cntrl_addr Индивидуальный адрес устройства.
@param cntrl_addr_for_all Групповой широковещательный адрес.
*/
/**
@fn void SPISE2P_DRV_init(SPISE2P_DRV *eeprom)
@brief Инициализирует низкоуровневый автомат SPI EEPROM.
@param eeprom Экземпляр драйвера.
*/
/**
@fn void SPISE2P_DRV_tick(SPISE2P_DRV *eeprom)
@brief Выполняет один шаг неблокирующего автомата EEPROM.
@param eeprom Экземпляр драйвера.
*/
/**
@fn void SPISE2P_DRV_csset(void)
@brief Деактивирует линию chip-select EEPROM.
*/
/**
@fn void SPISE2P_DRV_csclr(void)
@brief Активирует линию chip-select EEPROM.
*/
/**
@fn unsigned int spiSe2pFree(SPISE2P_DRV *se2p)
@brief Проверяет готовность автомата EEPROM к новой операции.
@param se2p Экземпляр драйвера.
@return Ненулевое значение, когда драйвер свободен.
*/
/**
@fn void spiSe2pWrite(SPISE2P_DRV *se2p, SE2P_DATA *data)
@brief Передаёт автомату описание операции записи.
@param se2p Экземпляр драйвера.
@param data Описание адреса, буфера и длины.
*/
/**
@fn void spiSe2pRead(SPISE2P_DRV *se2p, SE2P_DATA *data)
@brief Передаёт автомату описание операции чтения.
@param se2p Экземпляр драйвера.
@param data Описание адреса, буфера и длины.
*/
/**
@}
*/

64
Doc/build.bat Normal file
View File

@@ -0,0 +1,64 @@
@echo off
rem Builds the local HTML overview and Doxygen API reference in Doc\build.
rem Prefers the pinned Doc\.tools Doxygen, then falls back to a system install.
rem If Doxygen is unavailable, installs the explanatory API placeholder instead.
setlocal EnableExtensions
set "DOC_DIR=%~dp0"
set "SOURCE=%DOC_DIR%index.html"
set "BUILD_DIR=%DOC_DIR%build"
set "OVERVIEW=%BUILD_DIR%\index.html"
set "DOXYGEN_EXE=%DOC_DIR%.tools\doxygen-1.18.0\doxygen.exe"
echo [BALSAM 167] Building HTML documentation...
if not exist "%SOURCE%" (
echo [ERROR] Source HTML not found:
echo %SOURCE%
exit /b 1
)
if not exist "%BUILD_DIR%" mkdir "%BUILD_DIR%"
if errorlevel 1 (
echo [ERROR] Cannot create build directory:
echo %BUILD_DIR%
exit /b 2
)
copy /Y "%SOURCE%" "%OVERVIEW%" >nul
if errorlevel 1 (
echo [ERROR] Cannot build overview HTML.
exit /b 3
)
if not exist "%DOXYGEN_EXE%" (
where doxygen >nul 2>nul
if not errorlevel 1 set "DOXYGEN_EXE=doxygen"
)
if not exist "%DOXYGEN_EXE%" if /I not "%DOXYGEN_EXE%"=="doxygen" (
if not exist "%BUILD_DIR%\api\html" mkdir "%BUILD_DIR%\api\html"
copy /Y "%DOC_DIR%api-unavailable.html" "%BUILD_DIR%\api\html\index.html" >nul
echo [WARN] Doxygen is not installed; API reference was not regenerated.
echo [OK] Overview: %OVERVIEW%
exit /b 0
)
pushd "%DOC_DIR%"
"%DOXYGEN_EXE%" Doxyfile
set "DOXYGEN_RESULT=%ERRORLEVEL%"
popd
if not "%DOXYGEN_RESULT%"=="0" (
echo [ERROR] Doxygen failed with code %DOXYGEN_RESULT%.
exit /b %DOXYGEN_RESULT%
)
if not exist "%BUILD_DIR%\api\html\index.html" (
echo [ERROR] Doxygen did not create the expected API index.
exit /b 4
)
echo [OK] Overview: %OVERVIEW%
echo [OK] API: %BUILD_DIR%\api\html\index.html
exit /b 0

69
Doc/files.dox Normal file
View File

@@ -0,0 +1,69 @@
/**
@file main.c
@brief Точка входа, запуск периферии и главный цикл приложения.
@details Связывает все внутренние модули, выбирает ISR Timer1 по роли платы и
обслуживает CAN, команды конфигурации, кнопки и оба последовательных канала.
*/
/** @file ADC.c
@brief Настройка ADC TMS320F28335 и обработка потока сырых измерений. */
/** @file measure.c
@brief Пересчёт измерений Balsam 167, калибровка и диагностика каналов.
@details Содержит периодический измерительный ISR и общие массивы фильтров,
счётчиков, внешних температур, масок CAN и слов ошибок. */
/** @file filter_bat2.c
@brief Реализация рекурсивного фильтра Баттерворта второго порядка. */
/** @file RS485.c
@brief Драйвер SCI-A/SCI-B и автоматы приёма/передачи RS-485. */
/** @file bios.c
@brief Сервисные и загрузочные команды BIOS-протокола TMS. */
/** @file message.c
@brief Modbus-команды, обмен с OWEN и сохранение параметров изделия. */
/** @file ecan.c
@brief Настройка eCAN-A, почтовые ящики, приём и отправка телеметрии. */
/** @file spise2p.c
@brief Доступ к последовательной EEPROM через SPI-A. */
/** @file peripher.c
@brief Определение аппаратной роли, таблицы GPIO и дискретные входы/выходы. */
/** @file DAC.c
@brief Программный последовательный интерфейс аналогового выхода нагрузки. */
/** @file pulto.c
@brief Периодическая логика панели EPLT. */
/** @file kanal.c
@brief Низкоуровневая передача данных внешним семисегментным каналам. */
/** @file crc16.c
@brief Варианты CRC-16, используемые транспортными и сервисными протоколами. */
/** @file cntrl_adr.c
@brief Хранение и проверка индивидуального и группового адресов контроллера. */
/** @file log_to_mem.c
@brief Инициализация циклического журнала во внешней памяти XINTF. */
/** @file tools.c
@brief Настройка XINTF zone 7 и программные задержки. */
/** @file package.h
@brief Конфигурация варианта Balsam и логическая карта массива Modbus.
@warning Изменение смещений влияет на CAN, RS-485, EEPROM и внешнее ПО. */
/** @file GPIO_table.h
@brief Направления, уровни и мультиплексирование GPIO для всех ролей платы. */
/** @file caliber.h
@brief Заводские калибровочные таблицы вариантов Balsam.
@note Заголовок содержит определения данных и намеренно исключён из разбора
Doxygen, чтобы препроцессор не выбирал несовместимые варианты. */

117
Doc/index.html Normal file

File diff suppressed because one or more lines are too long

70
Doc/mainpage.md Normal file
View File

@@ -0,0 +1,70 @@
# Balsam 167 Peripheral {#mainpage}
Прошивка периферийного контроллера **Balsam 167** для цифрового сигнального
контроллера Texas Instruments TMS320F28335. Контроллер измеряет токи,
напряжения и температуры, анализирует аварийные состояния, управляет
дискретными выходами и обменивается данными по CAN и двум каналам SCI/RS-485.
@tableofcontents
## Назначение
Один исполняемый образ поддерживает несколько аппаратных ролей. Роль платы
определяется входами режима при старте и хранится в `Desk`:
| Значение | Роль | Основная задача |
|---|---|---|
| `dsk_COMM` | COMM | связь и термокалибровка |
| `dsk_BKSD` | BKSD | дискретные сигналы |
| `dsk_BKST` | BKST | температурные каналы |
| `dsk_EPLT` | EPLT | пульт/индикация |
| `dsk_SHKF` | SHKF | шкафной контроллер |
| `dsk_LOAD` | LOAD | нагрузка и аналоговый выход |
Конфигурация изделия выбирается макросом `BALSAM` в `package.h`. Для этого
репозитория задано значение `167`.
## Выполнение программы
1. `main()` настраивает системную тактовую частоту, PIE, внешнюю память,
GPIO, CAN, SCI, EEPROM и измерительные каналы.
2. `timer_Init()` выбирает обработчик Timer1: `cpu_timer1_isr_SENS()` для
измерительных плат или `cpu_timer1_isr_PULT()` для пульта.
3. Быстрый контур АЦП выполняется в `adc_isr()`.
4. Главный цикл передаёт телеметрию, обслуживает команды настройки,
дискретные входы и протокол BIOS/Modbus.
## Карта модулей
- @ref measurement — сбор АЦП, фильтрация, пересчёт физических величин и ошибки.
- @ref communication — SCI/RS-485, разбор команд и Modbus-пакеты.
- @ref can_bus — CAN eCAN-A и циклическая телеметрия.
- @ref storage — SPI EEPROM, параметры и журнал во внешней памяти.
- @ref hardware — GPIO, дискретные выходы, ЦАП и семисегментный канал.
- @ref boot_protocol — команды чтения, записи и запуска ПО через BIOS-протокол.
- @ref utilities — CRC, фильтр Баттерворта, задержки и служебные функции.
## Общая память данных
Массив `modbus` — центральная модель состояния. Макросы из `package.h`
проецируют его участки на флаги ошибок, измерения, уставки, коэффициенты
калибровки и команды. Измерительный контур обновляет модель, а CAN/RS-485
публикуют или изменяют её. При изменении разметки следует одновременно
проверять адреса протокола, `ANSWER_LEN` и таблицу данных изделия в `Doc`.
## Сборка прошивки
Инструкции для Code Composer Studio 12 находятся в
[README_CCS12.md](README_CCS12.md). Старый проект CCS 3 использует
`UKSSTMS320F28335.pjt`; проект CCS 12 расположен в каталоге
`UKSSTMS320F28335`.
## Сборка документации
Запустите `Doc\\build.bat`. Обзор будет помещён в `Doc\\build\\index.html`,
а при наличии Doxygen API-справочник появится в
`Doc\\build\\api\\html\\index.html`.
@warning Обработчики прерываний и функции, меняющие GPIO или регистры
периферии, нельзя вызывать как обычные функции без понимания контекста PIE,
частоты тактирования и выбранной роли платы.

View File

@@ -1,3 +1,13 @@
/**
* @file ADC.c
* @brief Сбор аналоговых отсчётов и первичная обработка измерительных каналов.
*
* Модуль настраивает АЦП TMS320F28335 и обслуживает прерывание окончания
* преобразования. Отсчёты накапливаются в ADC_table и adc_table_lem, после
* чего передаются измерительному контуру. Обработчик работает в контексте
* прерывания, поэтому его время выполнения ограничено частотой дискретизации,
* а разделяемые таблицы должны читаться основным циклом согласованно.
*/
#include "DSP2833x_Device.h" // DSP281x Headerfile Include File
#include "DSP2833x_Examples.h" // DSP281x Examples Include File
#include "DSP2833x_SWPrioritizedIsrLevels.h"
@@ -14,20 +24,37 @@
#include "peripher.h"
/** @brief Сырые отсчёты четырёх силовых каналов, передаваемые Current_count(). */
Uint16 adc_table_lem[4];
Uint16 ADC_table[28]; // Ïîòîìó ÷òî +4 êàëèáð
/** @brief Фильтрованные значения всех рабочих и калибровочных каналов. */
Uint16 ADC_table[28]; // Потому что +4 калибр
/** @brief Число допустимых отсчётов прошлого окна для контроля выбросов. */
Uint16 prev_ok[28];
/** @brief Параметры временного мультиплексирования термоканалов в тиках АЦП. */
unsigned int COUNT_ONE_CANAL;
unsigned int COUNT_DISCHARGE;
unsigned int COUNT_TRANSICIA;
unsigned int FILTER_CLIP;
/** @brief Счётчик прогрева измерений и его начальная длительность. */
long WAKE, WAKE_TIME;
// Prototype statements for functions found within this file.
/** @brief ISR конца последовательности ADC SEQ1. */
interrupt void adc_isr(void);
/**
* @brief Полностью настраивает АЦП, ePWM-триггер и расписание каналов платы.
*
* @details По Desk выбирается физическая последовательность входов ADCINA/B.
* ePWM1 затем запускает SEQ1 с частотой ADC_FREQ. Для мультиплексированных
* термоканалов рассчитываются интервалы разряда, переходного процесса и окна
* накопления; WAKE подавляет использование неустановившихся фильтров при старте.
*
* @pre Desk и признаки TermoAD/TermoSW определены функцией get_Mode().
* @post ADCINT подключён к adc_isr(), SEQ1 и ePWM1 запущены.
*/
void setup_adc()
{
long CLKdiv,HSPCLKdiv,Rate;
@@ -59,21 +86,23 @@ void setup_adc()
// EINT; // Enable Global interrupt INTM
// ERTM; // Enable Global realtime interrupt DBGM
/* Исключаем использование остаточных данных до первого полного цикла АЦП. */
for(i=0;i<28;i++)
ADC_table[i]=
prev_ok[i] = 0;
/* Каждая роль платы имеет собственную разводку входов и длину sequencer. */
// Configure ADC
if(Desk==dsk_LOAD)
{
AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 0x0005; // Setup 2 conv's on SEQ1
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x3; // òåìïåðàòóðà (êîòîðîé íåò)
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x1; // îìåãà
AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 0x5; // íàïðóãà 1
AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 0x4; // íàïðóãà 2
AdcRegs.ADCCHSELSEQ2.bit.CONV04 = 0x7; // òîê 1
AdcRegs.ADCCHSELSEQ2.bit.CONV05 = 0x2; // òîê 2
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x3; // температура (которой нет)
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x1; // омега
AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 0x5; // напруга 1
AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 0x4; // напруга 2
AdcRegs.ADCCHSELSEQ2.bit.CONV04 = 0x7; // ток 1
AdcRegs.ADCCHSELSEQ2.bit.CONV05 = 0x2; // ток 2
}
if(Desk==dsk_BKST)
@@ -87,7 +116,7 @@ void setup_adc()
if(Desk==dsk_COMM)
{
AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 0x0006; // Setup 2 conv's on SEQ1
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x5; // ñíà÷àëà òîæå áóäóò òåìïåðàòóðû
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x5; // сначала тоже будут температуры
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x4; // Setup ADCINA2 as 2nd SEQ1 conv.
AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 0x7; // Setup ADCINA2 as 2nd SEQ1 conv.
AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 0x2; // Setup ADCINA2 as 2nd SEQ1 conv.
@@ -105,23 +134,23 @@ void setup_adc()
if(Desk==dsk_SHKF)
{
AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 0x000F; // Setup 2 conv's on SEQ1
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x2; // 380Â Ô1
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x3; // 380Â Ô2
AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 0x6; // 31Â Ô1
AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 0xC; // 31Â Ô2 ?
AdcRegs.ADCCHSELSEQ2.bit.CONV04 = 0xA; // 31Â UC1
AdcRegs.ADCCHSELSEQ2.bit.CONV05 = 0xB; // 31Â UC2
AdcRegs.ADCCHSELSEQ2.bit.CONV06 = 0x7; // 24Â ÏÓ
AdcRegs.ADCCHSELSEQ2.bit.CONV07 = 0x4; // 24Â ÏÓ
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x2; // 380В Ф1
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x3; // 380В Ф2
AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 0x6; // 31В Ф1
AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 0xC; // 31В Ф2 ?
AdcRegs.ADCCHSELSEQ2.bit.CONV04 = 0xA; // 31В UC1
AdcRegs.ADCCHSELSEQ2.bit.CONV05 = 0xB; // 31В UC2
AdcRegs.ADCCHSELSEQ2.bit.CONV06 = 0x7; // 24В ПУ
AdcRegs.ADCCHSELSEQ2.bit.CONV07 = 0x4; // 24В ПУ
AdcRegs.ADCCHSELSEQ3.bit.CONV08 = 0x5; // 24Â ÏÊ áûëî 5
AdcRegs.ADCCHSELSEQ3.bit.CONV09 = 0x1; // 15Â Äð
AdcRegs.ADCCHSELSEQ3.bit.CONV10 = 0xE; // +24Â Äò
AdcRegs.ADCCHSELSEQ3.bit.CONV11 = 0x0; // +24Â Äò
AdcRegs.ADCCHSELSEQ4.bit.CONV12 = 0x8; // -24Â Äò áûëî 8
AdcRegs.ADCCHSELSEQ3.bit.CONV08 = 0x5; // 24В ПК было 5
AdcRegs.ADCCHSELSEQ3.bit.CONV09 = 0x1; // 15В Др
AdcRegs.ADCCHSELSEQ3.bit.CONV10 = 0xE; // +24В Дт
AdcRegs.ADCCHSELSEQ3.bit.CONV11 = 0x0; // +24В Дт
AdcRegs.ADCCHSELSEQ4.bit.CONV12 = 0x8; // -24В Дт было 8
AdcRegs.ADCCHSELSEQ4.bit.CONV13 = 0xF;//0xF; // ÄÒ° 1
AdcRegs.ADCCHSELSEQ4.bit.CONV14 = 0xD;//0xD; // ÄÒ° 2
AdcRegs.ADCCHSELSEQ4.bit.CONV13 = 0xF;//0xF; // ДТ° 1
AdcRegs.ADCCHSELSEQ4.bit.CONV14 = 0xD;//0xD; // ДТ° 2
AdcRegs.ADCCHSELSEQ4.bit.CONV15 = 0x9;//0x9;
}
@@ -152,6 +181,7 @@ void setup_adc()
if(EPwm1Regs.TBCTL.bit.HSPCLKDIV) HSPCLKdiv = 2*EPwm1Regs.TBCTL.bit.HSPCLKDIV;
else HSPCLKdiv = 1;
/* Период ePWM получается из реальной частоты TBCLK после обоих делителей. */
Rate = (SYSCLKOUT/(HSPCLKdiv*CLKdiv))/ADC_FREQ;
EPwm1Regs.TBPRD = Rate; // Set period for ePWM1
@@ -195,6 +225,18 @@ void setup_adc()
WAKE = WAKE_TIME;
}
/**
* @brief Забирает результаты SEQ1, фильтрует их и запускает расчёт величин.
*
* @details Сначала разрешает вложенные прерывания согласно программному
* приоритету PIE. В зависимости от аппаратной роли обрабатывает силовые каналы,
* нагрузочный аналоговый вход, шкафные каналы либо мультиплексированные
* термодатчики. Выброс принимается только при прохождении FILTER_CLIP; во время
* WAKE фильтр обучается, но наружу выдаётся непосредственный отсчёт.
*
* @note На общем выходе fin всегда сбрасывает SEQ1, подтверждает PIE и
* восстанавливает сохранённую маску PIEIER1, включая путь раннего выхода.
*/
interrupt void adc_isr(void)
{
static int cownt_one_canal=0;
@@ -214,6 +256,7 @@ interrupt void adc_isr(void)
PieCtrlRegs.PIEACK.all = 0xFFFF; // Enable PIE interrupts
EINT;
/* До READY обслуживается только обязательное завершение аппаратного цикла. */
if(WAKE) WAKE--;
if(!READY) goto fin;
@@ -246,7 +289,7 @@ interrupt void adc_isr(void)
{
if (++cownt_cans>=tpl_cans) cownt_cans=0;
// Èìåííî çäåñü íåò äûð
// Именно здесь нет дыр
// if(sens_type[cownt_cans])
{
i++;
@@ -256,6 +299,7 @@ interrupt void adc_isr(void)
else Power_count(cownt_cans);
} } }
/* Программный мультиплексор: переключение, выдержка, накопление и расчёт. */
if(TermoSW)
{
if(cownt_one_canal==COUNT_DISCHARGE)
@@ -265,11 +309,12 @@ interrupt void adc_isr(void)
if(TermoAD)
{
if(cownt_cans == TPL_CANS ) code_tpl_canal = TERMOPAIR-1;
if(cownt_cans == TPL_CANS+1) code_tpl_canal = TERMOPAIR-2; // ïîòîìó ÷òî 300 è 400 íàîáîðîò
if(cownt_cans == TPL_CANS+1) code_tpl_canal = TERMOPAIR-2; // потому что 300 и 400 наоборот
}
select_tpl_canal(code_tpl_canal);
}
/* Отсчёты до окончания переходного процесса намеренно отбрасываются. */
if(cownt_one_canal > COUNT_TRANSICIA)
for(i=0;i<TermoSW;i++)
{
@@ -277,6 +322,7 @@ interrupt void adc_isr(void)
Temper = *(&AdcRegs.ADCRESULT0 + i) >>4;
/* Ограничитель защищает рекурсивный фильтр от одиночного выброса. */
ok = abs(ADC_table[n]-Temper) < FILTER_CLIP;
if(ok) cwnt_ok[i]++;
@@ -325,6 +371,7 @@ for(i=0;i<1;i++)
fin:
/* Единая эпилоговая секция обязательна и после goto при READY == 0. */
// Reinitialize for next ADC sequence
AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1; // Reset SEQ1
AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1; // Clear INT SEQ1 bit

View File

@@ -1,3 +1,13 @@
/**
* @file DAC.c
* @brief Управление внешним ЦАП и формирование аналогового задания нагрузки.
*
* Формирует последовательный поток через GPIO, рассчитывает временные интервалы
* относительно READY_FREQ и выдаёт нормированное значение Anal_output().
* Линии CLK/CS/DATA управляются непосредственно, поэтому порядок переключений
* и программные задержки являются частью аппаратного протокола и не должны
* переставляться без проверки осциллографом.
*/
#include "DSP2833x_Device.h" // DSP281x Headerfile Include File
#include "DSP2833x_SWPrioritizedIsrLevels.h"
#include "filter_bat2.h"
@@ -21,6 +31,10 @@ unsigned long WAKEpowse;
unsigned long IMPowse;
unsigned int period_dac, time_dac;
/**
* @brief Переводит физические интервалы профиля нагрузки в тики READY_FREQ.
* @post Обновлены WAKEpowse, period_dac и time_dac.
*/
void Setup_DAC_time(void)
{
WAKEpowse = 3L * DAC_FREQ +1;
@@ -29,12 +43,14 @@ void Setup_DAC_time(void)
time_dac = LOAD_TIME * DAC_FREQ;
}
/** @brief Устанавливает логический уровень последовательной линии DATA ЦАП. */
void dOUT(int x)
{
if(x) GpioDataRegs.GPBSET.bit.GPIO60=1;
else GpioDataRegs.GPBCLEAR.bit.GPIO60=1;
}
/** @brief Формирует короткую аппаратно-зависимую задержку между фронтами GPIO. */
void wast()
{
int i;
@@ -42,6 +58,11 @@ void wast()
for(i=0;i<25;i++);
}
/**
* @brief Последовательно передаёт одно управляющее слово во внешний ЦАП.
* @param word Слово протокола, выдаваемое по DATA синхронно с CLK.
* @note CS и CLK переключаются программно; порядок фронтов менять нельзя.
*/
void dSEND(unsigned int word)
{
int i;
@@ -57,12 +78,20 @@ void dSEND(unsigned int word)
dCS_1();
}
/** @brief Устанавливает CS, CLK и DATA в безопасные уровни ожидания. */
void Init_DAC(void)
{
dSEND(0x9002|0x0000);
toggle_RES_OUT_1();
}
/**
* @brief Масштабирует прикладное задание и отправляет код во внешний ЦАП.
* @param vrot Требуемое значение в прикладных единицах.
* @param maxx Значение полной шкалы; должно быть больше нуля.
* @details Результат ограничивается аппаратным диапазоном перед формированием
* управляющего слова, поэтому выход не переполняется при завышенном задании.
*/
void Anal_output(long vrot, long maxx)
{
long out;
@@ -70,7 +99,7 @@ void Anal_output(long vrot, long maxx)
out = DAC_min() + vrot * DAC_max() / maxx - vrot * DAC_min() / maxx ;
x=0;// !x; тщетно бытие
x=0;// !x; тщетно бытие
if(cCalibrDac) out=DAC_cal();
@@ -99,6 +128,11 @@ Log_to_mem(modbus[0x1D]);
}
/**
* @brief Выполняет очередной шаг временного профиля аналоговой нагрузки.
* @details Использует IMPowse как фазу профиля и на границах интервалов
* выбирает новое задание, передаваемое через Anal_output().
*/
void Load_runner()
{
static unsigned int count_dac=0, count_load=0, x=1;

View File

@@ -1,8 +1,18 @@
/**
* @file ADC.h
* @brief Публичный интерфейс подсистемы АЦП.
*
* Экспортирует таблицы последних/накопленных отсчётов и временные признаки,
* обновляемые ADC.c. Размеры массивов определены реализацией и аппаратной
* разводкой; потребители должны использовать только известные индексы каналов.
*/
extern Uint16 adc_table_lem[];
extern Uint16 ADC_table[];
/** Инициализирует тактирование, последовательность каналов и прерывание АЦП. */
void setup_adc(void);
/** Счётчик активности и его порог, используемые для контроля измерительного цикла. */
extern long WAKE,WAKE_TIME;

View File

@@ -1,7 +1,23 @@
/**
* @file DAC.h
* @brief Интерфейс внешнего ЦАП и генератора задания нагрузки.
*
* Перед первым выводом требуется Init_DAC() и Setup_DAC_time(). Значение
* Anal_output() масштабируется относительно maxx; maxx должен быть положительным.
*/
/** Выполняет очередной шаг временного профиля аналогового выхода нагрузки. */
void Load_runner(void);
/**
* Выдаёт нормированное значение на ЦАП.
* @param vrot Требуемая величина выхода.
* @param maxx Полная шкала; должна быть больше нуля.
*/
void Anal_output(long vrot, long maxx);
/** Переводит линии внешнего ЦАП в исходное состояние. */
void Init_DAC(void);
/** Пересчитывает интервалы ЦАП из READY_FREQ, DAC_FREQ и LOAD_TIME. */
void Setup_DAC_time(void);
/** Внутренние счётчики профиля, обновляемые DAC.c. */
extern unsigned long IMPowse;
extern unsigned int period_dac, time_dac;

View File

@@ -1,3 +1,12 @@
/**
* @file GPIO_table.h
* @brief Таблицы направления и мультиплексирования GPIO для вариантов плат.
*
* Макросы сгруппированы по аппаратным исполнениям и напрямую соответствуют
* выводам разъёмов. Значения 0/1 кодируют вход/выход либо выбранную функцию MUX.
* Любое изменение требует сверки со схемой конкретной платы и package.h;
* этот заголовок содержит конфигурацию, а не переносимую логику GPIO.
*/
#define COMM_gpio00_dir 0UL
#define COMM_gpio01_dir 0UL
#define COMM_gpio02_dir 0UL
@@ -11,7 +20,7 @@
#define COMM_gpio10_dir 0UL
#define COMM_gpio11_dir 0UL
#define COMM_gpio19_dir 1UL // 63 — SPI
#define COMM_gpio19_dir 1UL // 63 — SPI
#define COMM_gpio20_dir 0UL // 64 2:9B mode 2
#define COMM_gpio21_dir 0UL // 65 2:9A mode 4
#define COMM_gpio22_dir 0UL // 66 2:12C mode 1
@@ -23,7 +32,7 @@
#define COMM_gpio32_dir 1UL // 74 2:10B DIOD green
#define COMM_gpio33_dir 0UL
#define COMM_gpio34_dir 1UL // 142 — SCI
#define COMM_gpio34_dir 1UL // 142 — SCI
#define COMM_gpio48_dir 1UL // 88 2:14C DIOD red
#define COMM_gpio49_dir 1UL // 89 2:14B select
@@ -54,7 +63,7 @@
#define BKSD_gpio10_dir 0UL // 19 2:6C oil
#define BKSD_gpio11_dir 1UL // 20 2:3C select
#define BKSD_gpio19_dir 1UL // 63 — SPI
#define BKSD_gpio19_dir 1UL // 63 — SPI
#define BKSD_gpio20_dir 0UL // 64 2:9B mode 2
#define BKSD_gpio21_dir 0UL // 65 2:9A mode 4
#define BKSD_gpio22_dir 0UL // 66 2:12C mode 1
@@ -66,7 +75,7 @@
#define BKSD_gpio32_dir 1UL // 74 2:10B DIOD green
#define BKSD_gpio33_dir 0UL
#define BKSD_gpio34_dir 1UL // 142 — SCI
#define BKSD_gpio34_dir 1UL // 142 — SCI
#define BKSD_gpio48_dir 1UL // 88 2:14C DIOD red
#define BKSD_gpio49_dir 0UL // 89 2:14B input 2
@@ -97,7 +106,7 @@
#define BKST_gpio10_dir 0UL
#define BKST_gpio11_dir 1UL // 20 2:3C select
#define BKST_gpio19_dir 1UL // 63 — SPI
#define BKST_gpio19_dir 1UL // 63 — SPI
#define BKST_gpio20_dir 0UL // 64 2:9B mode 2
#define BKST_gpio21_dir 0UL // 65 2:9A mode 4
#define BKST_gpio22_dir 0UL // 66 2:12C mode 1
@@ -109,7 +118,7 @@
#define BKST_gpio32_dir 1UL // 74 2:10B DIOD green
#define BKST_gpio33_dir 0UL
#define BKST_gpio34_dir 1UL // 142 — SCI
#define BKST_gpio34_dir 1UL // 142 — SCI
#define BKST_gpio48_dir 1UL // 88 2:14C DIOD red
#define BKST_gpio49_dir 0UL
@@ -140,7 +149,7 @@
#define PULT_gpio10_dir 0UL
#define PULT_gpio11_dir 0UL
#define PULT_gpio19_dir 1UL // 63 — SPI
#define PULT_gpio19_dir 1UL // 63 — SPI
#define PULT_gpio20_dir 0UL // 64 2:9B mode 2
#define PULT_gpio21_dir 0UL // 65 2:9A mode 4
#define PULT_gpio22_dir 0UL // 66 2:12C mode 1
@@ -152,7 +161,7 @@
#define PULT_gpio32_dir 1UL // 74 2:10B DIOD green
#define PULT_gpio33_dir 0UL
#define PULT_gpio34_dir 1UL // 142 — SCI
#define PULT_gpio34_dir 1UL // 142 — SCI
#define PULT_gpio48_dir 1UL // 88 2:14C DIOD red
#define PULT_gpio49_dir 0UL // 89 2:14B button
@@ -183,7 +192,7 @@
#define PLT2_gpio10_dir 0UL
#define PLT2_gpio11_dir 0UL
#define PLT2_gpio19_dir 1UL // 63 × SPI
#define PLT2_gpio19_dir 1UL // 63 Ч SPI
#define PLT2_gpio20_dir 0UL // 64 2:9B mode 2
#define PLT2_gpio21_dir 0UL // 65 2:9A mode 4
#define PLT2_gpio22_dir 0UL // 66 2:12C mode 1
@@ -195,7 +204,7 @@
#define PLT2_gpio32_dir 1UL // 74 2:10B DIOD green
#define PLT2_gpio33_dir 0UL // 75 2:10C button
#define PLT2_gpio34_dir 1UL // 142 × SCI
#define PLT2_gpio34_dir 1UL // 142 Ч SCI
#define PLT2_gpio48_dir 0UL // 88 2:14C button
#define PLT2_gpio49_dir 0UL // 89 2:14B button
@@ -226,7 +235,7 @@
#define SHKF_gpio10_dir 0UL // 19 2:6C input
#define SHKF_gpio11_dir 0UL // 20 2:3C input
#define SHKF_gpio19_dir 1UL // 63 — SPI
#define SHKF_gpio19_dir 1UL // 63 — SPI
#define SHKF_gpio20_dir 0UL // 64 2:9B mode 2
#define SHKF_gpio21_dir 0UL // 65 2:9A mode 4
#define SHKF_gpio22_dir 0UL // 66 2:12C mode 1
@@ -238,7 +247,7 @@
#define SHKF_gpio32_dir 0UL // 74 2:10B input
#define SHKF_gpio33_dir 0UL // 75 2:10C input
#define SHKF_gpio34_dir 1UL // 142 — SCI
#define SHKF_gpio34_dir 1UL // 142 — SCI
#define SHKF_gpio48_dir 1UL // 88 2:14C DIOD red
#define SHKF_gpio49_dir 0UL // 89 2:14B input
@@ -269,7 +278,7 @@
#define LOAD_gpio10_dir 0UL
#define LOAD_gpio11_dir 0UL // 20 2:3C res_in 0
#define LOAD_gpio19_dir 1UL // 63 — SPI
#define LOAD_gpio19_dir 1UL // 63 — SPI
#define LOAD_gpio20_dir 0UL // 64 2:9B mode 2
#define LOAD_gpio21_dir 0UL // 65 2:9A mode 4
#define LOAD_gpio22_dir 0UL // 66 2:12C mode 1
@@ -281,7 +290,7 @@
#define LOAD_gpio32_dir 1UL // 74 2:10B DIOD green
#define LOAD_gpio33_dir 0UL
#define LOAD_gpio34_dir 1UL // 142 — SCI
#define LOAD_gpio34_dir 1UL // 142 — SCI
#define LOAD_gpio48_dir 1UL // 88 2:14C DIOD red
#define LOAD_gpio49_dir 0UL

View File

@@ -1,11 +1,20 @@
/**
* @file RS485.h
* @brief Состояние портов SCI/RS-485, ограничения буферов и API обмена.
*
* RS_DATA объединяет регистры SCI, буферы, счётчики и состояние конечного
* автомата. Память буферов принадлежит вызывающему коду и должна оставаться
* доступной до завершения операции. MAX_RECEIVE_LENGTH/MAX_SEND_LENGTH задают
* жёсткую границу, которую необходимо проверить до помещения данных в буфер.
*/
/****************************************************************/
/* TMS320C32 */
/* ====== BIOS, КЛАИН, КЛВСП ====== */
/* ЦНИИ СЭТ (с) 1998-2000 г. */
/* ====== BIOS, КЛАИН, КЛВСП ====== */
/* ЦНИИ СЭТ (с) 1998-2000 г. */
/****************************************************************
RS485.h
****************************************************************
* Процедуры работы с UART *
* Процедуры работы с UART *
****************************************************************/
#ifndef _RS485
#define _RS485
@@ -38,30 +47,30 @@ typedef struct
{
volatile struct SCI_REGS *SciRegs;
unsigned int commnumber; // Номер порта
unsigned long RS_Length; // Длина пакета
unsigned int commnumber; // Номер порта
unsigned long RS_Length; // Длина пакета
unsigned int *pRS_RecvPtr; // Буфер приема
unsigned int *pRS_SendPtr; // Буфер посылки
unsigned int *pRS_RecvPtr; // Буфер приема
unsigned int *pRS_SendPtr; // Буфер посылки
unsigned int *pRecvPtr;
unsigned int RS_PrevCmd; // Предыдущаа комманда
unsigned int RS_Cmd; // Текущаа комманда
unsigned int RS_Header[MAX_RECEIVE_LENGTH]; // Заголовок
unsigned int flag_TIMEOUT_to_Send; // Флаг ожиданиа таймаута на отсылку
unsigned int flag_TIMEOUT_to_Receive; // Флаг ожиданиа таймаута на прием
unsigned int RS_DataReady; // Флаг готовности RS данных
unsigned int buffer[MAX_SEND_LENGTH]; // Буфер дла отсылки по RS
unsigned int RS_PrevCmd; // Предыдущаа комманда
unsigned int RS_Cmd; // Текущаа комманда
unsigned int RS_Header[MAX_RECEIVE_LENGTH]; // Заголовок
unsigned int flag_TIMEOUT_to_Send; // Флаг ожиданиа таймаута на отсылку
unsigned int flag_TIMEOUT_to_Receive; // Флаг ожиданиа таймаута на прием
unsigned int RS_DataReady; // Флаг готовности RS данных
unsigned int buffer[MAX_SEND_LENGTH]; // Буфер дла отсылки по RS
unsigned int addr_answer; // адрес куда отвечать в режиме ведущего
unsigned int addr_recive; // адрес по которому нас запросили
unsigned int flag_LEADING; // Флаг режима контроллера (по умолчанию ведомый)
unsigned int addr_answer; // адрес куда отвечать в режиме ведущего
unsigned int addr_recive; // адрес по которому нас запросили
unsigned int flag_LEADING; // Флаг режима контроллера (по умолчанию ведомый)
unsigned long RS_RecvLen;
unsigned long RS_SLength; // Длина пакета дла посылки
unsigned long RS_SendLen; // Количество байт уже передали
char RS_SendBlockMode; // Режим передачи
char RS_Flag9bit; // дла RS485????????
int BS_LoadOK; // Флаг успешности приема блока
unsigned long RS_SLength; // Длина пакета дла посылки
unsigned long RS_SendLen; // Количество байт уже передали
char RS_SendBlockMode; // Режим передачи
char RS_Flag9bit; // дла RS485????????
int BS_LoadOK; // Флаг успешности приема блока
int RS_FlagBegin;
int RS_HeaderCnt;
int RS_FlagSkiping;
@@ -73,61 +82,72 @@ typedef struct
extern RS_DATA rs_a,rs_b;
extern unsigned int
RS_Len[70]; /* Действительнаа длина команды (отладочной) + 1 */
RS_Len[70]; /* Действительнаа длина команды (отладочной) + 1 */
/** ISR приёма и передачи SCI-A/SCI-B; вызывают общий автомат соответствующего порта. */
interrupt void RSA_RX_Handler(void);
interrupt void RSA_TX_Handler(void);
interrupt void RSB_RX_Handler(void);
interrupt void RSB_TX_Handler(void);
/* иницилизациа переменных rs_a,rs_b*/
/* иницилизациа переменных rs_a,rs_b*/
/** Создаёт исходное состояние обоих RS_DATA и задаёт размер команды 15. */
void create_uart_vars(char size_cmd15);
/** Повторнаа инициализациа последовательного порта, используетса после подвиса */
/** Настройка режима приема/передачи */
/** Повторнаа инициализациа последовательного порта, используетса после подвиса */
/** Настройка режима приема/передачи */
void RS_SetBitMode(RS_DATA *rs_arr, int n);
/** Посылка блока байтов.
Посылает массива 32-битных целых чисел старшие биты должны быть 0.
@precondition Работа ф-ции зависит от макро RS_TRANSMIT_INTR
@param buf адрес массива
@param len количество байт
/** Посылка блока байтов.
Посылает массива 32-битных целых чисел старшие биты должны быть 0.
@precondition Работа ф-ции зависит от макро RS_TRANSMIT_INTR
@param rs_arr Состояние выбранного порта.
@param pBuf Адрес массива слов, каждое из которых содержит один байт.
@param len количество байт
@return Ненулевое значение, если передача принята драйвером.
@see RS_BSend, RS_TRANSMIT_INTR
*/
int RS_Send(RS_DATA *rs_arr,unsigned int *pBuf, unsigned long len);
/** Посылка блока упакованных байтов.
@precondition Работа ф-ции зависит от макро RS_TRANSMIT_INTR
@param buf адрес массива
@param len количество 8-битных байт
/** Посылка блока упакованных байтов.
@precondition Работа ф-ции зависит от макро RS_TRANSMIT_INTR
@param rs_arr Состояние выбранного порта.
@param pBuf Адрес массива с упакованными байтами.
@param len количество 8-битных байт
@return Ненулевое значение, если передача принята драйвером.
@see RS_Send, RS_TRANSMIT_INTR
*/
int RS_BSend(RS_DATA *rs_arr,unsigned int *pBuf, unsigned long len);
/** Инициализациа последовательного порта */
void setup_uart(char commnumber,unsigned long speed_baud); /* speed_baud - скорость линии в бодах */
/** Инициализациа последовательного порта */
/** @param commnumber COM_1 или COM_2. @param speed_baud Скорость линии в бодах. */
void setup_uart(char commnumber,unsigned long speed_baud); /* speed_baud - скорость линии в бодах */
/** Настраивает число бит данных, чётность и число стоп-битов выбранного SCI. */
void RS_SetLineMode(RS_DATA *rs_arr, int bit, char parity, int stop);
/** Меняет скорость уже созданного порта и обновляет curr_baud. */
void RS_SetLineSpeed(RS_DATA *rs_arr, unsigned long speed);
// Transmit a character from the SCI'
#define SCI_send(x,y) x->SciRegs->SCITXBUF=(unsigned char)(y)
// Ожидание завершениа передачи UART
// Ожидание завершениа передачи UART
// wait for TRDY =1 for empty state
#define RS_Wait4OK(x) while(!(x->SciRegs->SCICTL2.bit.TXEMPTY))
/** Переключение линии на прием */
/** Переключение линии на прием */
#define RS_Line_to_receive(x) if(x->commnumber==COM_2) GpioDataRegs.GPBDAT.bit.GPIO34 = 1;
/** Переключение линии на передачу */
/** Переключение линии на передачу */
#define RS_Line_to_send(x) if(x->commnumber==COM_2) GpioDataRegs.GPBDAT.bit.GPIO34 = 0;
/** Разрешение прерываний по получению символа и ошибкам от UART */
/** Разрешение прерываний по получению символа и ошибкам от UART */
#define enableUARTInt(x) x->SciRegs->SCICTL2.all=2
#define enableUARTIntW(x) x->SciRegs->SCICTL2.all=1
/** Сбрасывает таймер контроля активности после корректного обмена. */
void clear_timer_rs_live(RS_DATA *rs_arr);
/** Обновляет признак потери связи по тайм-ауту RS_TIME_OUT. */
void test_rs_live(RS_DATA *rs_arr);
#ifdef __cplusplus

View File

@@ -1,11 +1,19 @@
/**
* @file bios_dsp.h
* @brief Команды сервисного BIOS и API удалённой работы с памятью.
*
* Объявляет номера команд загрузчика и обработчики, работающие с RS_DATA.
* Интерфейс предоставляет прямые операции с адресным пространством DSP и
* предназначен только для доверенных, уже проверенных протокольных запросов.
*/
/****************************************************************/
/* TMS320C32 */
/* ====== BIOS, КЛАИН, КЛВСП ====== */
/* ЦНИИ СЭТ (с) 1998-2000 г. */
/* ====== BIOS, КЛАИН, КЛВСП ====== */
/* ЦНИИ СЭТ (с) 1998-2000 г. */
/****************************************************************/
/* Bios_dsp.h */
/****************************************************************/
/* Основные комманды BIOS */
/* Основные комманды BIOS */
/****************************************************************/
#ifndef _BIOS_DSP
#define _BIOS_DSP
@@ -42,71 +50,85 @@ enum {
CMD_VECTOR=61,
CMD_IMPULSE,
/* стандартные команды */
/* стандартные команды */
CMD_STD=65, CMD_STD_ANS
};
enum {false=0, true};
/** Возвращает номер комманды, если есть или -1 если транзакций не было */
/** Возвращает номер комманды, если есть или -1 если транзакций не было */
/** Проверяет кадр и выполняет содержащуюся в нём сервисную команду. */
int get_command(RS_DATA *rs_arr);
/** Стандартный ответ, без параметров */
/** Стандартный ответ, без параметров */
/** Формирует и отправляет ответ длиной n слов через заданный порт. */
void Answer(RS_DATA *rs_arr,int n);
/* начальные установки (не работает)*/
/* начальные установки (не работает)*/
/** Сбрасывает состояние текущей сессии загрузчика. */
void init(RS_DATA *rs_arr);
/**@name Комманды
* Комманды, вызываемые через последовательный канал*/
/**@name Комманды
* Комманды, вызываемые через последовательный канал*/
//@{
/** Инициировать загрузку блока.
Настраивает прием блока данных */
/** Инициировать загрузку блока.
Настраивает прием блока данных */
/** Подготавливает диапазон памяти к приёму нового образа. */
void initload(RS_DATA *rs_arr);
/** Загрузка блока.
Вызываетса после загрузки блока через RS */
/** Загрузка блока.
Вызываетса после загрузки блока через RS */
/** Записывает очередной проверенный блок образа в память. */
void load(RS_DATA *rs_arr);
/** Выполнить программу в формате Serial Boot.
@precondition Должна быть произведена загрузка блока
Адрес программы беретса из заголовка и
сравниваетса с переменной RecvPtr, заполнаемой в ф-ции load
/** Выполнить программу в формате Serial Boot.
@precondition Должна быть произведена загрузка блока
Адрес программы беретса из заголовка и
сравниваетса с переменной RecvPtr, заполнаемой в ф-ции load
@see load */
/** Передаёт управление по адресу, заданному сервисной командой. */
void run (RS_DATA *rs_arr);
/** Прочитать ачейку памати */
/** Прочитать ачейку памати */
/** Читает запрошенный диапазон памяти и возвращает его ведущему узлу. */
void peek(RS_DATA *rs_arr);
/** Записать в ачейку памати */
/** Записать в ачейку памати */
/** Записывает одно или несколько слов по адресу из команды. */
void poke(RS_DATA *rs_arr);
/** Передать блок памати */
/** Передать блок памати */
/** Передаёт ведущему узлу очередной блок содержимого памяти. */
void upload(RS_DATA *rs_arr);
/** Прошить XILINX.
@precondition Должна быть произведена загрузка блока
Адрес и длина прошивки беретса из заголовка и
сравниваетса с переменными RecvPtr и Length, заполнаемыми в ф-ции load,
так же смотрит магическое слово в начале прошивки
/** Прошить XILINX.
@precondition Должна быть произведена загрузка блока
Адрес и длина прошивки беретса из заголовка и
сравниваетса с переменными RecvPtr и Length, заполнаемыми в ф-ции load,
так же смотрит магическое слово в начале прошивки
@see load */
/** Обрабатывает команду доступа к внешней Flash-памяти. */
void xflash(RS_DATA *rs_arr);
/** Прошить TMS.
@precondition Должна быть произведена загрузка блока
Адрес и длина прошивки беретса из заголовка и
сравниваетса с переменными RecvPtr и Length, заполнаемыми в ф-ции load
/** Прошить TMS.
@precondition Должна быть произведена загрузка блока
Адрес и длина прошивки беретса из заголовка и
сравниваетса с переменными RecvPtr и Length, заполнаемыми в ф-ции load
@see load */
/** Обрабатывает команду доступа к целевой Flash-памяти. */
void tflash(RS_DATA *rs_arr);
/* расширенные команды дла биоса */
/* расширенные команды дла биоса */
/** Обрабатывает расширенную сервисную команду BIOS. */
void extendbios(RS_DATA *rs_arr);
/** Записывает слово по физическому адресу; адрес обязан быть допустимым. */
void write_memory(unsigned long addr, unsigned int data);
/** Читает слово по физическому адресу; адрес обязан быть допустимым. */
unsigned int read_memory(unsigned long addr);

View File

@@ -1,22 +1,31 @@
/**
* @file caliber.h
* @brief Заводские калибровочные таблицы для вариантов семейства Balsam.
*
* Ровно одна таблица def_cal включается по значению BALSAM. Строки и столбцы
* имеют фиксированный смысл для измерительных каналов и диапазонов; их порядок
* согласован с message.c и внешней методикой калибровки. Файл определяет данные,
* поэтому должен подключаться только одним модулем реализации.
*/
#if BALSAM == 167 //-------------------------------------------------------------
int def_cal[][8] = {
// ÁÊÑÑ òðàíñ 100 1 100 2 150 1 150 2
0, 0, 0, 0, 455, 441, 2459, 2392, // Áîðò 0
0, 0, 0, 0, 462, 439, 2442, 2400, // Áîðò 1
// БКСС транс 100 1 100 2 150 1 150 2
0, 0, 0, 0, 455, 441, 2459, 2392, // Борт 0
0, 0, 0, 0, 462, 439, 2442, 2400, // Борт 1
// Ñèëîâîé UA1 UB1 UA2 UB2 300 1 300 2 400 1 400 2
4936, 4877, 4972, 4857, 1886, 1898, 2517, 2535, // Áîðò 0
4876, 5005, 4876, 4847, 1898, 1873, 2535, 2502, // Áîðò 1
// Силовой UA1 UB1 UA2 UB2 300 1 300 2 400 1 400 2
4936, 4877, 4972, 4857, 1886, 1898, 2517, 2535, // Борт 0
4876, 5005, 4876, 4847, 1898, 1873, 2535, 2502, // Борт 1
// ÓÌÏ UA UC IA IC 20mA O 4mA O 20mA I 4mA I
3820, 3790, 3505, 3380, 1736, 683, 854, 14, // Áîðò 0 1062 1980
3835, 3785, 3522, 3332, 1745, 689, 860, 17, // Áîðò 1 1720 2405
// УМП UA UC IA IC 20mA O 4mA O 20mA I 4mA I
3820, 3790, 3505, 3380, 1736, 683, 854, 14, // Борт 0 1062 1980
3835, 3785, 3522, 3332, 1745, 689, 860, 17, // Борт 1 1720 2405
// ÁÊÑÑÄ 100 Ohm 150 Ohm
// БКССД 100 Ohm 150 Ohm
1281, 1272, 0, 0, 622, 2263, 0, 0,
// ÂÝÏ 380Ô1 380Ô2 300 1 300 2 400 1 400 2
// ВЭП 380Ф1 380Ф2 300 1 300 2 400 1 400 2
1290, 1285, 0, 0, 2045, 2045, 2725, 2725 };
#endif //---------------------------------------------------------------------
@@ -24,45 +33,45 @@
#if BALSAM == 166 //-------------------------------------------------------------
int def_cal[][8] = {
// Ñèëîâîé UA1 UB1 UA2 UB2 300 1 300 2 400 1 400 2
4936, 4877, 4972, 4857, 1960, 1920, 2610, 2570, // Áîðò 0
4876, 5005, 4876, 4847, 1950, 1965, 2600, 2615, // Áîðò 1
// Силовой UA1 UB1 UA2 UB2 300 1 300 2 400 1 400 2
4936, 4877, 4972, 4857, 1960, 1920, 2610, 2570, // Борт 0
4876, 5005, 4876, 4847, 1950, 1965, 2600, 2615, // Борт 1
// ÓÌÏ UA UC IA IC 20mA O 4mA O 20mA I 4mA I
3820, 3790, 3505, 3380, 1718, 673, 854, 14, // Áîðò 0
3835, 3785, 3522, 3332, 1697, 653, 860, 17, // Áîðò 1
// УМП UA UC IA IC 20mA O 4mA O 20mA I 4mA I
3820, 3790, 3505, 3380, 1718, 673, 854, 14, // Борт 0
3835, 3785, 3522, 3332, 1697, 653, 860, 17, // Борт 1
// ÂÝÏ 380Ô1 380Ô2 300 1 300 2 400 1 400 2
// ВЭП 380Ф1 380Ф2 300 1 300 2 400 1 400 2
1281, 1272, 0, 0, 2049, 2050, 2749, 2750 };
#endif //---------------------------------------------------------------------
#if BALSAM == 165 //-------------------------------------------------------------
int def_cal[][8] = {
// Ñèëîâîé UA1 UB1 UA2 UB2 300 1 300 2 400 1 400 2
5130, 4960, 4925, 5070, 1881, 1881, 2506, 2516, // Áîðò 0
5020, 5040, 4950, 5030, 1871, 1879, 2507, 2514, // Áîðò 1
// Силовой UA1 UB1 UA2 UB2 300 1 300 2 400 1 400 2
5130, 4960, 4925, 5070, 1881, 1881, 2506, 2516, // Борт 0
5020, 5040, 4950, 5030, 1871, 1879, 2507, 2514, // Борт 1
// ÓÌÏ UA UC IA IC 20mA O 4mA O 20mA I 4mA I
3890, 3865, 5100, 4850, 1730, 675, 854, 14, // Áîðò 0
3845, 3800, 3250, 3100, 1044, 0, 860, 17, // Áîðò 1
// УМП UA UC IA IC 20mA O 4mA O 20mA I 4mA I
3890, 3865, 5100, 4850, 1730, 675, 854, 14, // Борт 0
3845, 3800, 3250, 3100, 1044, 0, 860, 17, // Борт 1
// ÂÝÏ 380Ô1 380Ô2 300 1 300 2 400 1 400 2
// ВЭП 380Ф1 380Ф2 300 1 300 2 400 1 400 2
1289, 1280, 0, 0, 2073, 2071, 2773, 2771 };
#endif //---------------------------------------------------------------------
#if BALSAM == 164 //-------------------------------------------------------------
int def_cal[][8] = {
// Ñèëîâîé UA1 UB1 UA2 UB2 300 1 300 2 400 1 400 2
7573, 7573, 7573, 7573, 1979, 1888, 2592, 2513, // Áîðò 0
7573, 7573, 7573, 7573, 1970, 1907, 2584, 2558, // Áîðò 1
// Силовой UA1 UB1 UA2 UB2 300 1 300 2 400 1 400 2
7573, 7573, 7573, 7573, 1979, 1888, 2592, 2513, // Борт 0
7573, 7573, 7573, 7573, 1970, 1907, 2584, 2558, // Борт 1
// ÓÌÏ UA UC IA IC 20mA O 4mA O 20mA I 4mA I
3920, 3905, 3300, 3220, 1015, 0, 854, 14, // Áîðò 0
3835, 3785, 5100, 4600, 1750, 680, 860, 17, // Áîðò 1
// УМП UA UC IA IC 20mA O 4mA O 20mA I 4mA I
3920, 3905, 3300, 3220, 1015, 0, 854, 14, // Борт 0
3835, 3785, 5100, 4600, 1750, 680, 860, 17, // Борт 1
// ÂÝÏ 380Ô1 380Ô2 300 1 300 2 400 1 400 2
// ВЭП 380Ф1 380Ф2 300 1 300 2 400 1 400 2
1266, 1267, 0, 0, 2063, 2040, 2678, 2655 };
#endif //---------------------------------------------------------------------
@@ -71,15 +80,15 @@
#if PXXXXX == 1 //-------------------------------------------------------------
int def_cal[][6] = {
// Ñèëîâîé UA1 UB1 UA2 UB2 300 1 300 2 400 1 400 2
0400, 0400, 0400, 0400, 1950, 1950, 2600, 2600, // Áîðò 0
0400, 0400, 0400, 0400, 1950, 1950, 2600, 2600, // Áîðò 1
// Силовой UA1 UB1 UA2 UB2 300 1 300 2 400 1 400 2
0400, 0400, 0400, 0400, 1950, 1950, 2600, 2600, // Борт 0
0400, 0400, 0400, 0400, 1950, 1950, 2600, 2600, // Борт 1
// ÓÌÏ UA UC IA IC 20mA 4mA
0400, 0400, 4800, 4800, 0, 0, 0, 0, // Áîðò 0
0400, 0400, 4800, 4800, 0, 0, 0, 0, // Áîðò 1
// УМП UA UC IA IC 20mA 4mA
0400, 0400, 4800, 4800, 0, 0, 0, 0, // Борт 0
0400, 0400, 4800, 4800, 0, 0, 0, 0, // Борт 1
// ÂÝÏ 380Ô1 380Ô2 300 1 300 2 400 1 400 2
// ВЭП 380Ф1 380Ф2 300 1 300 2 400 1 400 2
1270, 1270, 0, 0, 1950, 1950, 2600, 2600 };
#endif //---------------------------------------------------------------------
*/

View File

@@ -1,11 +1,18 @@
/**
* @file cntrl_adr.h
* @brief Адреса узла в сервисном протоколе и функция их установки.
*
* Константные адреса задают специальные роли, CNTRL_ADDR и ADDR_FOR_ALL могут
* меняться во время настройки. Заголовок не выполняет валидацию диапазонов.
*/
/****************************************************************/
/* TMS320C32 */
/* ====== BIOS, КЛАИН, КЛВСП ====== */
/* ЦНИИ СЭТ (с) 1998-2000 г. */
/* ====== BIOS, КЛАИН, КЛВСП ====== */
/* ЦНИИ СЭТ (с) 1998-2000 г. */
/****************************************************************
cntrl_adr.h
****************************************************************
* Адрес контроллера *
* Адрес контроллера *
****************************************************************/
#ifndef _CNTRL_ADR
@@ -15,22 +22,26 @@
extern "C" {
#endif
/** адрес контроллера дла посылки всем АИНам */
/** адрес контроллера дла посылки всем АИНам */
extern int ADDR_FOR_ALL;
/** адрес контроллера дла посылки ответа */
/** адрес контроллера дла посылки ответа */
extern const int ADDR_ANSWER;
/** адреса терминала дла посылки ответа */
/** адреса терминала дла посылки ответа */
extern const int ADDR_TERMINAL;
/* Адрес контроллера */
/* Адрес контроллера */
extern int CNTRL_ADDR;
/* Универсальный адрес контроллера */
/* Универсальный адрес контроллера */
extern const int CNTRL_ADDR_UNIVERSAL;
/** Установка адреса контроллера дла прошивки */
/** Установка адреса контроллера дла прошивки */
/**
* @param cntrl_addr Новый индивидуальный адрес узла.
* @param cntrl_addr_for_all Новый адрес групповых команд.
*/
void set_cntrl_addr (int cntrl_addr,int cntrl_addr_for_all);

View File

@@ -1,8 +1,20 @@
/**
* @file crc16.h
* @brief Интерфейс алгоритмов CRC, используемых коммуникационными модулями.
*
* Буфер представлен словами целевого DSP; параметр size трактуется реализацией
* как число обрабатываемых элементов. Вызывающий код выбирает вариант CRC и
* передаёт требуемое протоколом начальное значение.
*/
typedef unsigned short WORD;
typedef unsigned char byte;
/** Вычисляет CRC-CCITT для size слов, продолжая переданное значение crc. */
unsigned int get_crc_ccitt(unsigned int crc, unsigned int *buf, unsigned long size );
/** Вычисляет табличный CRC-16 (полином 0xA001) для size слов. */
unsigned int get_crc_16(unsigned int crc,unsigned int *buf,unsigned long size );
/** Вычисляет побитовый вариант CRC-16 для проверки табличной реализации. */
unsigned int get_crc_16b(unsigned int crc,unsigned int *buf,unsigned long size );
/** Возвращает CRC-16 кадра с принятым в проекте начальным значением. */
int get_crc16(unsigned int *buf, int size );

View File

@@ -1,3 +1,22 @@
/**
* @file ecan.h
* @brief Минимальный публичный интерфейс обмена по CAN.
*
* Port выбирает аппаратный контроллер, DevNum/Addr определяют логический узел.
* Массив данных должен содержать формат кадра, ожидаемый реализацией ecan.c.
*/
/**
* Настраивает CAN-порт и почтовые ящики логического устройства.
* @param Port Номер аппаратного CAN-контроллера.
* @param DevNum Номер узла; реализация ограничивает его диапазоном 1..16.
*/
void InitCan(int Port, int DevNum);
/**
* Ставит прикладные данные в передающий mailbox.
* @param Port Номер аппаратного CAN-контроллера.
* @param data Массив слов кадра в формате проекта.
* @param Addr Адрес получателя.
*/
void CAN_send(int Port, int data[], int Addr);
/** Последние прикладные слова, принятые обработчиком CAN. */
extern int CAN_input_data[];

View File

@@ -1,3 +1,11 @@
/**
* @file filter_bat2.h
* @brief Коэффициенты и состояние рекурсивного НЧ-фильтра второго порядка.
*
* Наборы K*_FILTER_BATTER2_* рассчитаны для заданных частот среза при штатной
* частоте дискретизации. DEF_FILTERBAT создаёт нулевое состояние для 3 Гц;
* один экземпляр FILTERBAT нельзя одновременно использовать для разных каналов.
*/
#ifndef _FILTER_BAT2
#define _FILTER_BAT2
@@ -38,6 +46,12 @@ typedef struct { float k_0;
K3_FILTER_BATTER2_3HZ, \
0,0,0,0,0,0}
/**
* Выполняет один шаг фильтра и обновляет его историю на месте.
* @param b Уникальное состояние фильтра конкретного канала.
* @param InpVarCurr Текущий входной отсчёт.
* @return Новый фильтрованный отсчёт.
*/
float filterbat(FILTERBAT *b, float InpVarCurr);

View File

@@ -1 +1,13 @@
/**
* @file kanal.h
* @brief Интерфейс вывода значения на адресуемый канальный индикатор.
*
* adr выбирает индикатор, dat содержит отображаемое число, dot — позицию точки.
*/
/**
* Передаёт число адресуемому индикатору.
* @param adr Аппаратный адрес канала.
* @param dat Отображаемое целое значение.
* @param dot Позиция десятичной точки в протокольном формате индикатора.
*/
void kanal_Send(int adr, long dat, int dot);

View File

@@ -1,11 +1,19 @@
/**
* @file log_to_mem.h
* @brief Разметка внешней памяти и состояние кольцевого журнала.
*
* LOG_PAGE_START и LOG_PAGE_LEN обязаны совпадать с картой линковщика. Флаги
* no_write/never_write запрещают запись на разных уровнях, а clear_mem()
* полностью обнуляет область и потому является длительной операцией.
*/
/****************************************************************/
/* TMS320C32 */
/* ====== BIOS, КЛАИН, КЛВСП ====== */
/* ЦНИИ СЭТ (с) 1998-2001г. */
/* ====== BIOS, КЛАИН, КЛВСП ====== */
/* ЦНИИ СЭТ (с) 1998-2001г. */
/****************************************************************/
/* log_to_mem.h
****************************************************************
* Запись логов в памyть *
* Запись логов в памyть *
****************************************************************/
#ifndef _LOG_TO_MEM
@@ -15,11 +23,11 @@
extern "C" {
#endif
/* Определениa длa работы логгера */
/* Определениa длa работы логгера */
#define LOG_PAGE_START 0x0200000
#define LOG_PAGE_LEN 0xFA00
extern int no_write, never_write; // Флаги, чтобы не писать (если что)
extern int no_write, never_write; // Флаги, чтобы не писать (если что)
typedef struct
{
@@ -32,14 +40,15 @@ typedef struct
extern LOG Log;
/* Запись словa в памать, где логи лежат */
/* Запись словa в памать, где логи лежат */
#define Log_to_mem(x) *(int *)(Log.Adres++) = x
/* Проверка границы памати дла логов */
/* Проверка границы памати дла логов */
//#define Test_mem_limit(x) if(Log.Adres > (Log.Finis - (x))) Log.Adres = Log.Start
#define Test_mem_limit(x) if(Log.Adres > (Log.Finis - (x))) Log.Adres -=(x)
/* Очистка памати (обнуление) */
/* Очистка памати (обнуление) */
/** Полностью обнуляет область журнала и возвращает указатель записи в начало. */
void clear_mem();
#ifdef __cplusplus

View File

@@ -1,19 +1,41 @@
// âãâ
// вгв
#ifndef _MEASURE
#define _MEASURE
/**
* @file measure.h
* @brief Общие типы, состояния и API измерительно-диагностической подсистемы.
*
* Объявляет этапы обработки каналов, битовые флаги ошибок и разделяемые данные,
* используемые ISR, протоколом и главным циклом. Номера каналов и позиции битов
* являются частью внешнего формата сообщений; менять их независимо нельзя.
*/
/** ISR Timer1 для периодического запуска измерительных и диагностических этапов. */
interrupt void cpu_timer1_isr_SENS(void);
/** Инициализирует основные структуры, фильтры и состояния датчиков. */
void Init_sensors(void);
/** Выполняет дополнительную инициализацию, зависящую от типа платы. */
void Init_sensors_more(void);
/** Строит маски упаковки каналов для внешних сообщений. */
void Init_packMask(void);
/** Рассчитывает температуру канала; ovn выбирает источник/режим измерения. */
void Temper_count(int chan, int ovn);
/** Рассчитывает ток и обновляет диагностику заданного канала. */
void Current_count(int chan);
/** Рассчитывает электрическую мощность заданного канала. */
void Power_count(int chan);
/** Пересчитывает масштабные коэффициенты из калибровочных параметров. */
void calc_sensor_koef(void);
/** Обновляет признаки превышения допустимого напряжения. */
void Is_Voltage_Hi(void);
/** Пересчитывает пороги напряжения для текущего режима изделия. */
void calc_volta_edge(void);
/**
* Выполняет временную проверку порога ошибки.
* @return Ненулевое значение после требуемого числа последовательных нарушений.
*/
int er_anal(int term, unsigned int * count, long edge, int pre);
typedef union
@@ -64,14 +86,14 @@ typedef union
#define NOER 0xE000//C
#define EROR 0x01FF
#define READY_FREQ (500.0 * 2)// Ãö
#define BLINK_FREQ 2 // Ãö
#define READY_FREQ (500.0 * 2)// Гц
#define BLINK_FREQ 2 // Гц
#define BLINK_TIME (READY_FREQ / BLINK_FREQ)
#define CANPOWSE 20
#define ADC_FREQ 3750 //3885.0//777//2000//20000 //777 //3885 // Ãö (777*5)
#define DAC_FREQ 200//5 // Ãö
#define ADC_FREQ 3750 //3885.0//777//2000//20000 //777 //3885 // Гц (777*5)
#define DAC_FREQ 200//5 // Гц
#define LOAD_TIME 30//15 // sec
@@ -86,23 +108,23 @@ extern unsigned int Caliber_time;
#define ZERO 27
#define Cooling 5 // (°Ñ) Ãèñòåðåçèñ ïî ñíàòèþ ïåðåãðåâà
#define Cooling 5 // (°С) Гистерезис по снатию перегрева
#define COSPi6 0.86602540378443864676372317075294
#define RADIX2 1.4142135623730950488016887242097
#define POWER_380 0 // ïèòàíèå 380Â
#define POWER_38O 1 // ïèòàíèå 380Â
#define CURRENT 2 // òîê
#define VOLTAGE 3 // íàïðàæåíèå
#define POWER_31 4 // ïèòàíèå 31Â
#define POWER_27 5 // ïèòàíèå 24Â
#define POWER_24 6 // ïèòàíèå 24Â
#define POWER_15 7 // ïèòàíèå 15Â
#define TERMO_AD 8 // òåðìîäàò÷èê ìåëêîñõåìà
#define TERMO_RS 9 // òåðìîðåçèñòîð
#define TRM_OBEH 10 // òåðìîäàò÷èê îâåí
#define POWER_380 0 // питание 380В
#define POWER_38O 1 // питание 380В
#define CURRENT 2 // ток
#define VOLTAGE 3 // напражение
#define POWER_31 4 // питание 31В
#define POWER_27 5 // питание 24В
#define POWER_24 6 // питание 24В
#define POWER_15 7 // питание 15В
#define TERMO_AD 8 // термодатчик мелкосхема
#define TERMO_RS 9 // терморезистор
#define TRM_OBEH 10 // термодатчик овен
extern int TPL_CANS,tpl_cans;

View File

@@ -1,3 +1,11 @@
/**
* @file message.h
* @brief Форматы команд, размеры ответов и API обработки Modbus-сообщений.
*
* CMD_TO_TMS описывает точную раскладку принятого кадра, включая CRC. Поля и
* ANSWER_LEN/REPLY_LEN согласованы с удалённым мастером; структура использует
* целевые типы DSP, поэтому не должна сериализоваться простым memcpy на PC.
*/
#ifndef MESSAGE_H
#define MESSAGE_H
@@ -11,8 +19,8 @@ typedef unsigned char CHAR;
typedef struct
{
unsigned char Address; // Àäðåñ êîíòðîëëåðà
unsigned char Number; // Íîìåð êîìàíäû
unsigned char Address; // Адрес контроллера
unsigned char Number; // Номер команды
BAITE byte0;
BAITE byte1;
@@ -30,13 +38,20 @@ typedef struct
extern int modbus[];
/** Принимает запрос чтения регистров и подготавливает соответствующий ответ. */
void ReceiveCommandModbus3(RS_DATA *rs_arr);
/** Принимает запрос записи одного регистра и применяет разрешённый параметр. */
void ReceiveCommandModbus6(RS_DATA *rs_arr);
/** Отправляет текущий блок телеметрии ответом прикладной функции 4. */
void SendCommandModbus4(RS_DATA *rs_arr);
/** Разбирает ответ функции 4, полученный от ведомого устройства. */
void ReceiveAnswerModbus4(RS_DATA *rs_arr);
/** Сохраняет текущий массив параметров в EEPROM. */
void Save_params(void);
/** Загружает параметры из EEPROM и заменяет некорректные значения безопасными. */
void Load_params(void);
/** Заполняет параметры значениями для выбранного в package.h варианта платы. */
void Default_params(void);
#endif //MESSAGE_H

View File

@@ -1,3 +1,12 @@
/**
* @file package.h
* @brief Центральная конфигурация изделия Balsam и адресов его подсистем.
*
* Значение BALSAM выбирает аппаратный вариант, направление вращения и набор
* возможностей. adr_* и последующие константы связывают физические модули с
* протоколом и массивами измерений. Изменение этого файла влияет на всю прошивку
* и должно сопровождаться проверкой схемы, карты CAN/Modbus и калибровки.
*/
#ifndef PACKAGE
#define PACKAGE
@@ -91,15 +100,15 @@
#define bSecretBt Buttons.bit.bit1
#define bTermoCal Buttons.bit.bit2
#define Cancount (modbus+0x60) // ïàóçà ìåæäó I ïîñûëêàìè CAN
#define Bright (modbus+0x62) // àðêîñòü ñèãíàëüíûõ ëàìïî÷åê
#define Brightness modbus[0x62] // àðêîñòü ñèãíàëüíûõ ëàìïî÷åê
#define Cancount (modbus+0x60) // пауза между I посылками CAN
#define Bright (modbus+0x62) // аркость сигнальных лампочек
#define Brightness modbus[0x62] // аркость сигнальных лампочек
#define Owncount modbus[0x63] // ïàóçà ìåæäó îïðîñàìè OWEN
#define Owncount modbus[0x63] // пауза между опросами OWEN
/* Íå âðåìà!
#define DAC_go modbus[0x64] // íà÷àëî çàðàäà
#define DAC_stop modbus[0x65] // êîíåö çàðàäà
/* Не врема!
#define DAC_go modbus[0x64] // начало зарада
#define DAC_stop modbus[0x65] // конец зарада
*/
#define m_FAST 0

View File

@@ -1,3 +1,12 @@
/**
* @file peripher.h
* @brief GPIO-примитивы и интерфейс определения аппаратного режима.
*
* Inline-функции учитывают различия разводки плат через глобальный Desk.
* Большинство выходов активны низким уровнем, поэтому set/clear не всегда
* совпадают с физическими SET/CLEAR регистрами. Desk должен быть определён
* get_Mode() до первого управления линиями.
*/
#include "DSP2833x_Device.h" // DSP281x Headerfile Include File
extern int Mode,Desk,TermoAD,TermoRS,TermoSW,Kurrent,Kalibro,Owen;

View File

@@ -1,3 +1,10 @@
/**
* @file pulto.h
* @brief Обработчик периодического таймера для режима пульта.
*
* Функция устанавливается в вектор Timer1 только для соответствующего Desk.
*/
//void what_is(void);
/** ISR таймера CPU1: обновляет индикацию и контроль связи в режиме пульта. */
interrupt void cpu_timer1_isr_PULT(void);

View File

@@ -1,3 +1,12 @@
/**
* @file spise2p.h
* @brief Конфигурация SPI EEPROM и структуры её конечного автомата.
*
* Определяет команды микросхемы, ширину адреса/данных, скорость SPI и состояния
* транзакции. SPISE2P_DRV_DEFAULTS создаёт единственный драйвер с callback-функциями;
* перед чтением или записью требуется InitSeeprom(), а диапазон операции должен
* оставаться внутри SEEPROM_LEN.
*/
/*=====================================================================
File name : SPISE2P.H
@@ -17,7 +26,7 @@ Date : 30/6/2003 (DD/MM/YYYY)
#define __SPISE2P_H__
// Ёмкость памати в байтах
// Ёмкость памати в байтах
#define SEEPROM_LEN 0x10000
#define NULL 0
@@ -90,13 +99,28 @@ typedef struct {
typedef SPISE2P_DRV *SPISE2P_DRV_handle;
/** Сбрасывает поля конечного автомата и конфигурирует SPI. */
void SPISE2P_DRV_init(SPISE2P_DRV * );
/** Продвигает конечный автомат EEPROM на один таймерный такт. */
void SPISE2P_DRV_tick(SPISE2P_DRV *);
/** Деактивирует аппаратный chip-select EEPROM. */
void SPISE2P_DRV_csset(void);
/** Активирует аппаратный chip-select EEPROM. */
void SPISE2P_DRV_csclr(void);
/** @return Ненулевое значение, когда драйвер готов принять новую операцию. */
unsigned int spiSe2pFree(SPISE2P_DRV *se2p);
/**
* Запускает асинхронную запись.
* @param se2p Экземпляр конечного автомата.
* @param msgPtr Описание адреса, буфера и длины блока.
*/
void spiSe2pWrite(SPISE2P_DRV *se2p, SE2P_DATA *data);
/**
* Запускает асинхронное чтение.
* @param se2p Экземпляр конечного автомата.
* @param msgPtr Описание адреса, буфера и длины блока.
*/
void spiSe2pRead(SPISE2P_DRV *se2p, SE2P_DATA *data);
#if(SPISE2P_DATA_WIDTH==SIXTEEN_BIT)
@@ -109,25 +133,27 @@ void spiSe2pRead(SPISE2P_DRV *se2p, SE2P_DATA *data);
#define WORD_LEN 1
#endif
/* Установка драйвера сериальной EEPROM. **
** Инициализациа SPI и проч. Также настройка таймера. **
** Драйвер работает на прерываниах от таймера 2! */
/* Установка драйвера сериальной EEPROM. **
** Инициализациа SPI и проч. Также настройка таймера. **
** Драйвер работает на прерываниах от таймера 2! */
void InitSeeprom(void);
/* Запись блока в SEEPROM. Параметры таковы: **
** adres - адрес в епромке, куда писать. **
** adres = 0..0x8000, если длина слова 8 бит **
** adres = 0..0x4000, если длина слова 16 бит **
** buf - указатель на памать, откуда писать. **
** size - длина блока в байтах. По-любому в байтах! */
/* Запись блока в SEEPROM. Параметры таковы: **
** adres - адрес в епромке, куда писать. **
** adres = 0..0x8000, если длина слова 8 бит **
** adres = 0..0x4000, если длина слова 16 бит **
** buf - указатель на памать, откуда писать. **
** size - длина блока в байтах. По-любому в байтах! */
/** @param size Размер блока в байтах, независимо от SPISE2P_DATA_WIDTH. */
void Seeprom_write(unsigned int adres, unsigned int buf[], unsigned int size);
/* Чтение блока из SEEPROM. Параметры таковы: **
** adres - адрес в епромке, откуда читать. **
** adres = 0..0x8000, если длина слова 8 бит **
** adres = 0..0x4000, если длина слова 16 бит **
** buf - указатель на памать, куда читать. **
** size - длина блока в байтах. По-любому в байтах! */
/* Чтение блока из SEEPROM. Параметры таковы: **
** adres - адрес в епромке, откуда читать. **
** adres = 0..0x8000, если длина слова 8 бит **
** adres = 0..0x4000, если длина слова 16 бит **
** buf - указатель на памать, куда читать. **
** size - длина блока в байтах. По-любому в байтах! */
/** @param size Размер блока в байтах, независимо от SPISE2P_DATA_WIDTH. */
void Seeprom_read(unsigned int adres, unsigned int buf[], unsigned int size);
#endif

View File

@@ -1,7 +1,18 @@
/**
* @file tools.h
* @brief Низкоуровневые вспомогательные функции платформы TMS320F28335.
*
* init_zone7() настраивает внешнюю шину, pause_us() выполняет активную задержку.
*/
#ifndef TOOLS_H
#define TOOLS_H
/** Настраивает 16-битную внешнюю шину XINTF Zone 7. */
void init_zone7(void);
/**
* Выполняет активную задержку.
* @param t Длительность в микросекундах при штатной частоте CPU.
*/
void pause_us(unsigned long t);
#endif //TOOLS_H

View File

@@ -1,11 +1,21 @@
/**
* @file RS485.c
* @brief Драйвер двух каналов SCI/RS-485 и автоматы приёма/передачи кадров.
*
* Настраивает SCI-A/SCI-B, обслуживает RX/TX-прерывания, контролирует длины
* команд и тайм-аут активности, а также переключает формат и скорость линии.
* RS_DATA содержит разделяемое состояние конечного автомата; буферы должны
* жить до завершения передачи и не превышать MAX_*_LENGTH. Обработчики обязаны
* подтверждать PIE и корректно возвращать приоритеты прерываний.
*/
/****************************************************************/
/* TMS320C32 */
/* ====== BIOS, КЛАИН, КЛВСП ====== */
/* ЦНИИ СЭТ (с) 1998-2000 г. */
/* ====== BIOS, КЛАИН, КЛВСП ====== */
/* ЦНИИ СЭТ (с) 1998-2000 г. */
/****************************************************************
RS485.с
RS485.с
****************************************************************
* Процедуры работы с UART *
* Процедуры работы с UART *
****************************************************************/
//#include "big_dsp_module.h"
@@ -32,7 +42,12 @@ static char size_cmd15=1;
void RS_RX_Handler(RS_DATA *rs_arr);
void RS_TX_Handler(RS_DATA *rs_arr);
/** Обработчик прерываний UART - принато */
/** Обработчик прерываний UART - принато */
/**
* @brief ISR приёма SCI-A.
* @details Временно ограничивает приоритеты группы PIE9, вызывает общий
* RS_RX_Handler() для rs_a, затем восстанавливает маску и подтверждает группу.
*/
interrupt void RSA_RX_Handler(void)
{
// Set interrupt priority:
@@ -51,6 +66,7 @@ interrupt void RSA_RX_Handler(void)
PieCtrlRegs.PIEIER9.all = TempPIEIER;
}
/** @brief ISR приёма SCI-B; адаптер общего автомата к состоянию rs_b. */
interrupt void RSB_RX_Handler(void)
{
// Set interrupt priority:
@@ -69,6 +85,7 @@ interrupt void RSB_RX_Handler(void)
PieCtrlRegs.PIEIER9.all = TempPIEIER;
}
/** @brief ISR освобождения TX SCI-A; продолжает передачу буфера rs_a. */
interrupt void RSA_TX_Handler(void)
{
// Set interrupt priority:
@@ -87,6 +104,7 @@ interrupt void RSA_TX_Handler(void)
PieCtrlRegs.PIEIER9.all = TempPIEIER;
}
/** @brief ISR освобождения TX SCI-B; продолжает передачу буфера rs_b. */
interrupt void RSB_TX_Handler(void)
{
// Set interrupt priority:
@@ -105,7 +123,15 @@ interrupt void RSB_TX_Handler(void)
PieCtrlRegs.PIEIER9.all = TempPIEIER;
}
/** Обработчик прерываний UART - принато */
/** Обработчик прерываний UART - принато */
/**
* @brief Принимает очередной символ и продвигает автомат сборки кадра.
* @param rs_arr Состояние порта, вызвавшего прерывание.
* @details Проверяет аппаратные ошибки, адресный девятый бит, ожидаемую длину
* команды и границы буфера. После полного кадра выставляет признак готовности
* для foreground-кода; при нарушении синхронизации возвращается к ожиданию адреса.
* @warning Выполняется из ISR и не должен вызывать блокирующие операции.
*/
void RS_RX_Handler(RS_DATA *rs_arr)
{
char Rc;
@@ -113,16 +139,16 @@ void RS_RX_Handler(RS_DATA *rs_arr)
// led1_on();
for(;;) // 'goto' это не оператор азыка С
for(;;) // 'goto' это не оператор азыка С
{
if(!rs_arr->SciRegs->SCIRXST.bit.RXRDY) // Receiver ready flag
{
PieCtrlRegs.PIEACK.bit.ACK9 |= 1;
rs_arr->SciRegs->SCIFFRX.bit.RXFFINTCLR=1; // Clear INT flag
return; // кстати это единственный выход из прерываниа
return; // кстати это единственный выход из прерываниа
}
Rc = rs_arr->SciRegs->SCIRXBUF.bit.RXDT; // Читаем символ в любом случае
Rc = rs_arr->SciRegs->SCIRXBUF.bit.RXDT; // Читаем символ в любом случае
if(rs_arr->SciRegs->SCIRXST.bit.RXERROR) // Receiver error flag
{
@@ -132,42 +158,42 @@ void RS_RX_Handler(RS_DATA *rs_arr)
continue;
}
if(rs_arr->RS_DataReady) continue; // Не забрали данные
if(rs_arr->RS_DataReady) continue; // Не забрали данные
if (rs_arr->RS_Flag9bit==1) // дла RS485????????
if (rs_arr->RS_Flag9bit==1) // дла RS485????????
{
// Инициализируем переменные и флаги
rs_arr->RS_FlagBegin = true; // Ждем заголовок
// Инициализируем переменные и флаги
rs_arr->RS_FlagBegin = true; // Ждем заголовок
rs_arr->RS_RecvLen = 0;
rs_arr->RS_FlagSkiping = false;
rs_arr->RS_HeaderCnt = 0;
rs_arr->RS_Cmd = 0;
}
if(rs_arr->RS_FlagSkiping) continue; // Не нам
if(rs_arr->RS_FlagSkiping) continue; // Не нам
if (rs_arr->RS_FlagBegin) // Заголовок
if (rs_arr->RS_FlagBegin) // Заголовок
{
if (rs_arr->RS_HeaderCnt==0) // Адрес контроллера или стандартнаа команда
if (rs_arr->RS_HeaderCnt==0) // Адрес контроллера или стандартнаа команда
{
if( (Rc == CNTRL_ADDR_UNIVERSAL) || (Rc == CNTRL_ADDR && CNTRL_ADDR!=0) || ((Rc == rs_arr->addr_answer) && rs_arr->flag_LEADING)
|| ((Rc == ADDR_FOR_ALL && ADDR_FOR_ALL!=0) && !rs_arr->flag_LEADING))
{
rs_arr->addr_recive=Rc; // запомнили адрес по которому нас запросили
rs_arr->RS_Header[rs_arr->RS_HeaderCnt++] = Rc; // Первый байт
RS_SetBitMode(rs_arr,8); // перестроились в 8-бит режим
rs_arr->addr_recive=Rc; // запомнили адрес по которому нас запросили
rs_arr->RS_Header[rs_arr->RS_HeaderCnt++] = Rc; // Первый байт
RS_SetBitMode(rs_arr,8); // перестроились в 8-бит режим
}
else
{
rs_arr->RS_FlagSkiping = true; // Не нашему контроллеру
rs_arr->RS_FlagBegin = false; // остались в 9-бит режиме
rs_arr->RS_FlagSkiping = true; // Не нашему контроллеру
rs_arr->RS_FlagBegin = false; // остались в 9-бит режиме
// led1_off();
}
}
else
{
rs_arr->RS_Header[rs_arr->RS_HeaderCnt++] = Rc; // Второй байт и т.д.
rs_arr->RS_Header[rs_arr->RS_HeaderCnt++] = Rc; // Второй байт и т.д.
if (rs_arr->RS_HeaderCnt == 7 && rs_arr->RS_Cmd==CMD_MODBUS_16 && !rs_arr->flag_LEADING)
{
@@ -180,68 +206,75 @@ void RS_RX_Handler(RS_DATA *rs_arr)
RS_Len[rs_arr->RS_Cmd] = Rc + 5;
}
// если второй байт - это команда
// если второй байт - это команда
if (rs_arr->RS_HeaderCnt == 2)
{
rs_arr->RS_Cmd = Rc;
// Проверка длины посылки
// CMD_LOAD - младшаа на данный момент
// CMD_STD_ANS - старшаа на данный момент
// Проверка длины посылки
// CMD_LOAD - младшаа на данный момент
// CMD_STD_ANS - старшаа на данный момент
if ((rs_arr->RS_Cmd < CMD_MODBUS_3) || (rs_arr->RS_Cmd > CMD_STD_ANS) || (RS_Len[rs_arr->RS_Cmd]<3)
|| ((rs_arr->RS_Cmd == CMD_LOAD)&&(rs_arr->RS_PrevCmd != CMD_INITLOAD))
)
{
RS_SetBitMode(rs_arr,9); // Получили все перестроились в 9-бит дла RS485?
rs_arr->RS_HeaderCnt = 0; // Потому что команда не та
RS_SetBitMode(rs_arr,9); // Получили все перестроились в 9-бит дла RS485?
rs_arr->RS_HeaderCnt = 0; // Потому что команда не та
rs_arr->RS_FlagBegin = true;
rs_arr->RS_FlagSkiping = false;
rs_arr->RS_Cmd=0;
// led1_off();
continue;
}
if (rs_arr->RS_Cmd == CMD_LOAD) // Дла этой команды заголовок очень короткий
rs_arr->RS_FlagBegin = false;// дальше идут данные
if (rs_arr->RS_Cmd == CMD_LOAD) // Дла этой команды заголовок очень короткий
rs_arr->RS_FlagBegin = false;// дальше идут данные
}
if( (rs_arr->RS_HeaderCnt >= RS_Len[rs_arr->RS_Cmd]) ||
(rs_arr->RS_HeaderCnt >= sizeof(rs_arr->RS_Header)))
{ // Получили заголовок
RS_SetBitMode(rs_arr,9); // Получили все перестроились в 9-бит дла RS485?
{ // Получили заголовок
RS_SetBitMode(rs_arr,9); // Получили все перестроились в 9-бит дла RS485?
rs_arr->RS_FlagBegin = false;
rs_arr->RS_FlagSkiping = true;
rs_arr->RS_DataReady = true;
rs_arr->RS_Cmd=0;
// led1_off();
} } }
else // Поток данных
else // Поток данных
{
if(rs_arr->pRS_RecvPtr<(unsigned int *)Rec_Bloc_Begin || rs_arr->pRS_RecvPtr>(unsigned int *)Rec_Bloc_End)
{
rs_arr->pRS_RecvPtr = (unsigned int *)Rec_Bloc_Begin; // На программу надейса, а сам не плошай
rs_arr->pRecvPtr = (unsigned int *)Rec_Bloc_Begin; // На программу надейса, а сам не плошай
rs_arr->pRS_RecvPtr = (unsigned int *)Rec_Bloc_Begin; // На программу надейса, а сам не плошай
rs_arr->pRecvPtr = (unsigned int *)Rec_Bloc_Begin; // На программу надейса, а сам не плошай
}
if(rs_arr->RS_PrevCmd != CMD_INITLOAD) continue; // Мы здесь оказались по какой-то чудовищной ошибке
if(rs_arr->RS_PrevCmd != CMD_INITLOAD) continue; // Мы здесь оказались по какой-то чудовищной ошибке
if(rs_arr->RS_DataReady) // Если данные в основном цикле не забраны,
{ // то пропускаем следующую посылку
rs_arr->RS_FlagSkiping = true; // Игнорируем до следующего заголовка
if(rs_arr->RS_DataReady) // Если данные в основном цикле не забраны,
{ // то пропускаем следующую посылку
rs_arr->RS_FlagSkiping = true; // Игнорируем до следующего заголовка
// led1_off();
continue;
}
RS_BytePtr = rs_arr->RS_RecvLen++ % 2;
if(RS_BytePtr) *rs_arr->pRS_RecvPtr++ |= Rc; // Получили слово
if(RS_BytePtr) *rs_arr->pRS_RecvPtr++ |= Rc; // Получили слово
else *rs_arr->pRS_RecvPtr = Rc<<8;
if(rs_arr->RS_Length <= rs_arr->RS_RecvLen) // Конец посылки
if(rs_arr->RS_Length <= rs_arr->RS_RecvLen) // Конец посылки
{
rs_arr->RS_PrevCmd = rs_arr->RS_Header[1] = CMD_LOAD;
RS_SetBitMode(rs_arr,9); // Получили все данные перестроились в 9-бит дла RS485?
rs_arr->RS_FlagSkiping = true; // Игнорируем до следующего заголовка
rs_arr->RS_DataReady = true; // Флаг в основной цикл - данные получены
RS_SetBitMode(rs_arr,9); // Получили все данные перестроились в 9-бит дла RS485?
rs_arr->RS_FlagSkiping = true; // Игнорируем до следующего заголовка
rs_arr->RS_DataReady = true; // Флаг в основной цикл - данные получены
// led1_off();
} } } }
/** Обработчик прерываний UART - послано */
/** Обработчик прерываний UART - послано */
/**
* @brief Передаёт следующий элемент активного RS-485 буфера.
* @param rs_arr Состояние обслуживаемого SCI.
* @details Пока счётчик не достиг RS_Length, пишет следующий байт в SCITXBUF.
* После последнего байта дожидается освобождения линии, возвращает трансивер
* в режим приёма и сбрасывает состояние передачи.
*/
void RS_TX_Handler(RS_DATA *rs_arr)
{
char RS_BytePtr;
@@ -251,18 +284,18 @@ void RS_TX_Handler(RS_DATA *rs_arr)
{
if(++rs_arr->RS_SendLen >= rs_arr->RS_SLength)
{
enableUARTInt(rs_arr); /* Запрещаем прерываниа по передаче */
enableUARTInt(rs_arr); /* Запрещаем прерываниа по передаче */
}
SCI_send(rs_arr,*(rs_arr->pRS_SendPtr++));
if(rs_arr->RS_SendLen >= rs_arr->RS_SLength)
{
RS_Wait4OK(rs_arr);
// for (i=0; i <= TIME_WAIT_RS_BYTE_OUT; i++){} /* Пауза дла PC */
RS_SetBitMode(rs_arr,9); /* Передали все перестроились в 9-бит дла RS485?*/
RS_Line_to_receive(rs_arr); /* режим приема RS485 */
// for (i=0; i <= TIME_WAIT_RS_BYTE_OUT; i++){} /* Пауза дла PC */
RS_SetBitMode(rs_arr,9); /* Передали все перестроились в 9-бит дла RS485?*/
RS_Line_to_receive(rs_arr); /* режим приема RS485 */
rs_arr->flag_TIMEOUT_to_Send=false; /* сбросили флаг ожиданиа таймаута */
rs_arr->flag_TIMEOUT_to_Send=false; /* сбросили флаг ожиданиа таймаута */
}
}
else /* BM_PACKED */
@@ -271,7 +304,7 @@ void RS_TX_Handler(RS_DATA *rs_arr)
RS_BytePtr = (rs_arr->RS_SendLen++) % 2;
if(rs_arr->RS_SendLen >= rs_arr->RS_SLength)
{
enableUARTInt(rs_arr); /* Запрещаем прерываниа по передаче */
enableUARTInt(rs_arr); /* Запрещаем прерываниа по передаче */
}
if(RS_BytePtr) SCI_send(rs_arr, LOBYTE( *(rs_arr->pRS_SendPtr++) ));
else SCI_send(rs_arr, HIBYTE( *rs_arr->pRS_SendPtr ));
@@ -279,9 +312,9 @@ void RS_TX_Handler(RS_DATA *rs_arr)
if(rs_arr->RS_SendLen >= rs_arr->RS_SLength)
{
RS_Wait4OK(rs_arr);
// for (i=0; i <= TIME_WAIT_RS_BYTE_OUT; i++){} /* Пауза дла PC */
// RS_SetBitMode(rs_arr,9); /* Передали все перестроились в 9-бит дла RS485?*/
// RS_Line_to_receive(); /* режим приема RS485 */
// for (i=0; i <= TIME_WAIT_RS_BYTE_OUT; i++){} /* Пауза дла PC */
// RS_SetBitMode(rs_arr,9); /* Передали все перестроились в 9-бит дла RS485?*/
// RS_Line_to_receive(); /* режим приема RS485 */
}
}
@@ -290,7 +323,11 @@ void RS_TX_Handler(RS_DATA *rs_arr)
// rs_arr->SciRegs->SCIFFTX.bit.TXINTCLR=1; // Clear INT flag
}
/** Инициализациа массива длин команд */
/** Инициализациа массива длин команд */
/**
* @brief Заполняет таблицу ожидаемых длин сервисных и Modbus-команд.
* @post RS_Len[код] содержит полную длину кадра для поддерживаемых команд.
*/
void setup_arr_cmd_length()
{
int i;
@@ -317,86 +354,105 @@ void setup_arr_cmd_length()
RS_Len[CMD_EXTEND] = 18;
}
/** Настройка режима приема/передачи */
/** Настройка режима приема/передачи */
/**
* @brief Переключает SCI между 8- и 9-битным форматом символа.
* @param rs_arr Настраиваемый порт.
* @param n Требуемое число бит данных.
*/
void RS_SetBitMode(RS_DATA *rs_arr,int n)
{
if(n == 8)
{
RS_SetLineMode(rs_arr,8,'N',1); /* режим линии */
RS_SetLineMode(rs_arr,8,'N',1); /* режим линии */
rs_arr->RS_Flag9bit=0;
}
if(n == 9)
{
RS_SetLineMode(rs_arr,8,'N',1); /* режим линии */
RS_SetLineMode(rs_arr,8,'N',1); /* режим линии */
rs_arr->RS_Flag9bit=1;
} }
/** Посылка блока байтов.
Посылает массива 32-битных целых чисел старшие биты должны быть 0.
@precondition Работа ф-ции зависит от макро RS_TRANSMIT_INTR
@param buf адрес массива
@param len количество байт
/** Посылка блока байтов.
Посылает массива 32-битных целых чисел старшие биты должны быть 0.
@precondition Работа ф-ции зависит от макро RS_TRANSMIT_INTR
@param buf адрес массива
@param len количество байт
@see RS_BSend, RS_TRANSMIT_INTR */
/**
* @brief Запускает передачу массива, где один элемент хранит один байт.
* @return 1 после успешного запуска, 0 если порт занят или длина недопустима.
* @note Первый адресный символ отправляется в 9-битном режиме, оставшиеся — в 8-битном.
*/
int RS_Send(RS_DATA *rs_arr,unsigned int *pBuf,unsigned long len)
{
unsigned int i;
for (i=0; i <= 30000; i++){} /* Пауза дла PC */
for (i=0; i <= 30000; i++){} /* Пауза дла PC */
RS_Line_to_send(rs_arr); /* режим передачи RS485 */
RS_Line_to_send(rs_arr); /* режим передачи RS485 */
for (i=0; i <= 10000; i++){} /* Пауза дла PC */
for (i=0; i <= 10000; i++){} /* Пауза дла PC */
rs_arr->RS_SLength = len; /* Настраиваем переменные */
rs_arr->RS_SLength = len; /* Настраиваем переменные */
rs_arr->pRS_SendPtr = pBuf + 1;
rs_arr->RS_SendBlockMode = BM_CHAR32;
RS_Wait4OK(rs_arr); /* Дожидаемса ухода */
RS_SetBitMode(rs_arr,8); /* Остальные в 8-бит режиме */
RS_Wait4OK(rs_arr); /* Дожидаемса ухода */
RS_SetBitMode(rs_arr,8); /* Остальные в 8-бит режиме */
rs_arr->RS_SendLen = 1; /* Два байта уже передали */
rs_arr->RS_SendLen = 1; /* Два байта уже передали */
if(len > 1)
{
enableUARTIntW(rs_arr); /* Разрешаем прерываниа по передаче */
SCI_send(rs_arr, *pBuf); // Передаем второй байт по прерыванию
enableUARTIntW(rs_arr); /* Разрешаем прерываниа по передаче */
SCI_send(rs_arr, *pBuf); // Передаем второй байт по прерыванию
}
else
{
SCI_send(rs_arr, *pBuf); // Передаем второй байт по прерыванию
RS_Wait4OK(rs_arr); /* Дожидаемса ухода без прерываниа */
for (i=0; i <= TIME_WAIT_RS_BYTE_OUT; i++){} /* Пауза дла PC */
RS_SetBitMode(rs_arr,9); /* Обратно в 9-бит режим */
RS_Line_to_receive(rs_arr); /* режим приема RS485 */
SCI_send(rs_arr, *pBuf); // Передаем второй байт по прерыванию
RS_Wait4OK(rs_arr); /* Дожидаемса ухода без прерываниа */
for (i=0; i <= TIME_WAIT_RS_BYTE_OUT; i++){} /* Пауза дла PC */
RS_SetBitMode(rs_arr,9); /* Обратно в 9-бит режим */
RS_Line_to_receive(rs_arr); /* режим приема RS485 */
}
return 0;
}
// Посылка блока упакованных байтов
// Посылка блока упакованных байтов
/**
* @brief Запускает передачу массива с байтами, упакованными в слова DSP.
* @return 1 после успешного запуска, 0 при занятом порте или ошибке длины.
*/
int RS_BSend(RS_DATA *rs_arr,unsigned int *pBuf, unsigned long len)
{
RS_Line_to_send(rs_arr); // режим передачи RS485
RS_Line_to_send(rs_arr); // режим передачи RS485
rs_arr->RS_SLength = len; // Настраиваем переменные
rs_arr->RS_SLength = len; // Настраиваем переменные
rs_arr->pRS_SendPtr = pBuf;
rs_arr->RS_SendBlockMode = BM_PACKED;
RS_Wait4OK(rs_arr); // Ожидаем очистки и ухода последнего байта
RS_SetBitMode(rs_arr,8); /* Остальные в 8-бит режиме */
RS_Wait4OK(rs_arr); // Ожидаем очистки и ухода последнего байта
RS_SetBitMode(rs_arr,8); /* Остальные в 8-бит режиме */
rs_arr->RS_SendLen = 1; // Один байт уже передали
rs_arr->RS_SendLen = 1; // Один байт уже передали
enableUARTIntW(rs_arr); /* Разрешаем прерываниа по передаче */
enableUARTIntW(rs_arr); /* Разрешаем прерываниа по передаче */
SCI_send(rs_arr,HIBYTE(*pBuf));// Передаем первый байт
SCI_send(rs_arr,HIBYTE(*pBuf));// Передаем первый байт
return 0;
}
/** Устанавливает скорость обмена.
@param speed скорость RS в бод */
/** Устанавливает скорость обмена.
@param speed скорость RS в бод */
/** Устанавливает скорость обмена.
@param speed скорость RS в бод */
/** Устанавливает скорость обмена.
@param speed скорость RS в бод */
/**
* @brief Пересчитывает делитель baud-rate выбранного SCI.
* @param speed Требуемая скорость в бодах.
* @pre speed больше нуля и достижима от LSPCLK.
*/
void RS_SetLineSpeed(RS_DATA *rs_arr,unsigned long speed)
{
long SciBaud;
@@ -410,7 +466,11 @@ void RS_SetLineSpeed(RS_DATA *rs_arr,unsigned long speed)
}
/** Инициализациа последовательного порта */
/** Инициализациа последовательного порта */
/**
* @brief Обнуляет состояния обоих портов и создаёт таблицу длин команд.
* @param size_cmd15_set Прикладная длина команды функции 15.
*/
void create_uart_vars(char size_cmd15_set)
{
size_cmd15=size_cmd15_set;
@@ -420,8 +480,13 @@ void create_uart_vars(char size_cmd15_set)
/** Инициализациа последовательного порта */
/** Инициализациа последовательного порта */
/**
* @brief Назначает GPIO/векторы и запускает один из двух SCI-портов.
* @param commnumber COM_1 для SCI-A либо COM_2 для SCI-B.
* @param speed_baud Начальная скорость линии.
*/
void setup_uart(char commnumber, unsigned long speed_baud)
{
volatile struct SCI_REGS *SciRegs;
@@ -485,14 +550,14 @@ void setup_uart(char commnumber, unsigned long speed_baud)
SciRegs->SCICTL1.bit.RXENA=1;
SciRegs->SCIFFTX.bit.SCIFFENA=0; // fifo off
SciRegs->SCIFFRX.bit.RXFFIL=1; // Длина наименьшей команды
SciRegs->SCIFFRX.bit.RXFFIL=1; // Длина наименьшей команды
setup_arr_cmd_length();
RS_SetLineSpeed(rs_arr,speed_baud); // скорость линии
RS_Line_to_receive(rs_arr); // режим приема RS485
enableUARTInt(rs_arr); // разрешение прерываний UART
RS_SetLineSpeed(rs_arr,speed_baud); // скорость линии
RS_Line_to_receive(rs_arr); // режим приема RS485
enableUARTInt(rs_arr); // разрешение прерываний UART
RS_SetBitMode(rs_arr,9);
rs_arr->RS_PrevCmd = 0; // не было никаких команд
rs_arr->RS_PrevCmd = 0; // не было никаких команд
rs_arr->flag_TIMEOUT_to_Send = 0;
rs_arr->flag_LEADING = 0;
@@ -500,10 +565,11 @@ void setup_uart(char commnumber, unsigned long speed_baud)
SciRegs->SCICTL1.bit.SWRESET=1; // Relinquish SCI from Reset
}
/** Настройка режима линии.
@param bit количество бит данных
@param parity режим четности (N,O,E,M,S)
@param stop количество стоповых бит */
/** Настройка режима линии.
@param bit количество бит данных
@param parity режим четности (N,O,E,M,S)
@param stop количество стоповых бит */
/** @brief Программирует длину слова, чётность и число стоп-битов SCI. */
void RS_SetLineMode(RS_DATA *rs_arr, int bit, char parity, int stop)
{
volatile struct SCI_REGS *SciRegs;
@@ -541,12 +607,14 @@ Bit Bit Name Designation Functions
SciRegs->SCICCR.bit.ADDRIDLE_MODE = 0;
}
/** @brief Отмечает успешную активность порта и сбрасывает его watchdog связи. */
void clear_timer_rs_live(RS_DATA *rs_arr)
{
rs_arr->time_wait_rs_out=0;
}
/* проверка на живучесть RS */
/* проверка на живучесть RS */
/** @brief Увеличивает watchdog связи и фиксирует тайм-аут удалённого узла. */
void test_rs_live(RS_DATA *rs_arr)
{
/* if (rs_arr->time_wait_rs_out < RS_TIME_OUT)
@@ -554,6 +622,6 @@ void test_rs_live(RS_DATA *rs_arr)
else
{
rs_arr->time_wait_rs_out=0;
RS_Line_to_receive(rs_arr); // режим приема RS485
RS_Line_to_receive(rs_arr); // режим приема RS485
RS_SetBitMode(rs_arr,9);
}*/ }

View File

@@ -1,12 +1,22 @@
/**
* @file bios.c
* @brief Сервисные команды удалённого доступа к памяти и загрузки программы.
*
* Реализует разбор BIOS-команд, формирование ответов, чтение/запись памяти,
* загрузку блоков и запуск кода через транспорт RS-485. Адреса и размеры
* приходят извне, поэтому корректность пакета и CRC должна быть подтверждена
* до вызова обработчиков. Прямой доступ к памяти преднамеренно использует
* volatile и требует адресов из допустимой карты памяти контроллера.
*/
/****************************************************************/
/* TMS320C32 */
/* ====== BIOS, КЛАИН, КЛВСП ====== */
/* ЦНИИ СЭТ (с) 1998-2000 г. */
/* ====== BIOS, КЛАИН, КЛВСП ====== */
/* ЦНИИ СЭТ (с) 1998-2000 г. */
/****************************************************************
Bios.c
**************************************************************
Основные комманды BIOS *
дла работы с RS232
Основные комманды BIOS *
дла работы с RS232
****************************************************************/
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
#include "RS485.h"
@@ -18,82 +28,95 @@
//#include "spartan_tools.h"
//#include "big_dsp_module.h"
int flag_DEBUG = false; /* Флаг отладочного режима */
int flag_DEBUG = false; /* Флаг отладочного режима */
//static unsigned int *RecvPtr;
//static int BS_LoadOK = false; /** Флаг успешности приема блока */
//static int BS_LoadOK = false; /** Флаг успешности приема блока */
/**********************************************************/
/* Прототипы функций, используемых и определенных в файле */
/* Прототипы функций, используемых и определенных в файле */
/**********************************************************/
//static int _getbyte(int *addr, int offs);
/** @brief Читает одно volatile-слово из физического адресного пространства DSP. */
unsigned int read_memory(unsigned long addr)
{
return (*(volatile int *)(addr));
}
/** @brief Записывает одно слово data по физическому адресу addr. */
void write_memory(unsigned long addr, unsigned int data)
{
(*(volatile int *)( addr )) = data;
}
/** Возвращает номер комманды, если есть или -1 если транзакций не было */
/** Возвращает номер комманды, если есть или -1 если транзакций не было */
/**
* @brief Проверяет и диспетчеризует полностью принятый сервисный кадр.
* @param rs_arr Порт и буфер принятой команды.
* @return Код распознанной команды либо признак ошибки кадра.
* @details Проверяет адрес, длину и CRC, затем вызывает обработчик CMD_*.
*/
int get_command(RS_DATA *rs_arr)
{
int cmd;
unsigned int crc, rcrc;
if(rs_arr->RS_DataReady) // Данные по RS пришли
if(rs_arr->RS_DataReady) // Данные по RS пришли
{
rs_arr->RS_DataReady = false;
cmd = rs_arr->RS_Header[1]; // Прочитали номер команды
cmd = rs_arr->RS_Header[1]; // Прочитали номер команды
// Провераем длину команды дла считываниа CRC
// Провераем длину команды дла считываниа CRC
if((RS_Len[cmd]<3) || (RS_Len[cmd]>MAX_RECEIVE_LENGTH))
{
RS_Line_to_receive(rs_arr); // режим приема RS485
RS_Line_to_receive(rs_arr); // режим приема RS485
RS_SetBitMode(rs_arr,9);
return -1;
}
if(cmd == CMD_LOAD) // Если команда загрузки
if(cmd == CMD_LOAD) // Если команда загрузки
{
rs_arr->RS_PrevCmd = cmd;
return cmd; // Нет проверки crc
return cmd; // Нет проверки crc
}
else // Все остальные команды
else // Все остальные команды
{
// Считываем crc из посылки
// Считываем crc из посылки
crc = (rs_arr->RS_Header[RS_Len[cmd]-1] << 8) |
(rs_arr->RS_Header[RS_Len[cmd]-2]) ;
}
// Рассчитываем crc из посылки
// Рассчитываем crc из посылки
rcrc = 0xffff;
rcrc = get_crc_16( rcrc, rs_arr->RS_Header, (RS_Len[cmd]-2) );
if(crc == rcrc) // Провераем crc
if(crc == rcrc) // Провераем crc
{
rs_arr->RS_PrevCmd = cmd;
return cmd;
}
else
{
RS_Line_to_receive(rs_arr); // режим приема RS485
RS_Line_to_receive(rs_arr); // режим приема RS485
RS_SetBitMode(rs_arr,9);
} }
return -1;
}
/** Стандартный ответ, без параметров */
/** Стандартный ответ, без параметров */
/**
* @brief Добавляет CRC и отправляет n слов ответа через исходный порт.
* @param rs_arr Порт, через который пришёл запрос.
* @param n Число информационных слов до CRC.
*/
void Answer(RS_DATA *rs_arr,int n)
{
int crc;
flag_DEBUG = true; // Флаг отладочного режима
flag_DEBUG = true; // Флаг отладочного режима
rs_arr->buffer[0] = rs_arr->addr_recive; //CNTRL_ADDR;
rs_arr->buffer[1] = n;
@@ -109,7 +132,7 @@ void Answer(RS_DATA *rs_arr,int n)
RS_Send(rs_arr,rs_arr->buffer, 6);
}
/* Внутреннаа ф-циа */
/* Внутреннаа ф-циа */
static char _getbyte(unsigned int *addr, int32 offs)
{
unsigned int *address;
@@ -122,7 +145,8 @@ static char _getbyte(unsigned int *addr, int32 offs)
}
/* начальные установки (не работает)*/
/* начальные установки (не работает)*/
/** @brief Сбрасывает параметры текущей загрузочной сессии и подтверждает команду. */
void init(RS_DATA *rs_arr)
{
/*
@@ -145,13 +169,18 @@ void init(RS_DATA *rs_arr)
*/
}
/**@name Комманды
* Комманды, вызываемые через последовательный канал
/**@name Комманды
* Комманды, вызываемые через последовательный канал
*/
//@{
/** Инициировать загрузку блока.
Настраивает прием блока данных */
/** Инициировать загрузку блока.
Настраивает прием блока данных */
/**
* @brief Принимает заголовок нового образа и подготавливает диапазон загрузки.
* @details Из кадра извлекаются начальный адрес, длина и контрольные параметры;
* запись данных разрешается только после успешной проверки заголовка.
*/
void initload(RS_DATA *rs_arr)
{
unsigned long Address;
@@ -175,8 +204,12 @@ void initload(RS_DATA *rs_arr)
Answer(rs_arr,CMD_INITLOAD);
}
/** Загрузка блока.
Вызываетса после загрузки блока через RS */
/** Загрузка блока.
Вызываетса после загрузки блока через RS */
/**
* @brief Записывает очередную порцию образа по текущему адресу загрузки.
* @post Рабочий адрес и остаток длины продвинуты на принятый блок.
*/
void load(RS_DATA *rs_arr)
{
unsigned int rcrc, crc;
@@ -201,27 +234,32 @@ void load(RS_DATA *rs_arr)
else
{
rs_arr->BS_LoadOK = false;
RS_Line_to_receive(rs_arr); // режим приема RS485
RS_Line_to_receive(rs_arr); // режим приема RS485
RS_SetBitMode(rs_arr,9);
} }
/** Выполнить программу в формате Serial Boot.
@precondition Должна быть произведена загрузка блока
Адрес программы беретса из заголовка и
сравниваетса с переменной RecvPtr, заполнаемой в ф-ции load
/** Выполнить программу в формате Serial Boot.
@precondition Должна быть произведена загрузка блока
Адрес программы беретса из заголовка и
сравниваетса с переменной RecvPtr, заполнаемой в ф-ции load
@see load */
/** @brief Подтверждает команду и передаёт управление загруженной программе. */
void run (RS_DATA *rs_arr)
{
return;
}
/** Прочитать ачейку памати */
/** Прочитать ачейку памати */
/**
* @brief Возвращает содержимое заданного диапазона памяти.
* @warning Диапазон должен быть предварительно разрешён протокольным уровнем.
*/
void peek(RS_DATA *rs_arr)
{
unsigned long Address;
unsigned int Data, crc;
flag_DEBUG = true; // Флаг отладочного режима
flag_DEBUG = true; // Флаг отладочного режима
Address = rs_arr->RS_Header[5] & 0xFF;
Address = (Address<<8) | (rs_arr->RS_Header[4] & 0xFF);
@@ -254,7 +292,8 @@ void peek(RS_DATA *rs_arr)
}
/** Записать в ачейку памати */
/** Записать в ачейку памати */
/** @brief Записывает данные команды в заданную область памяти и отвечает статусом. */
void poke(RS_DATA *rs_arr)
{
unsigned long Address;
@@ -276,12 +315,13 @@ void poke(RS_DATA *rs_arr)
Answer(rs_arr,CMD_POKE);
}
/** Передать блок памати */
/** Передать блок памати */
/** @brief Передаёт ведущему очередной блок ранее выбранной области памяти. */
void upload(RS_DATA *rs_arr)
{
int32 Address, Length, crc;
no_write = 1; // Флаг отладочного режима
no_write = 1; // Флаг отладочного режима
// stopp=1;
Address = rs_arr->RS_Header[5] & 0xFF;
@@ -301,10 +341,10 @@ void upload(RS_DATA *rs_arr)
crc = get_crc_16( crc, rs_arr->buffer, 2);
crc = get_crc_16b( crc, (unsigned int *)Address, Length);
RS_Send(rs_arr,rs_arr->buffer, 1); // <=2 байт по флагу
RS_Send(rs_arr,rs_arr->buffer, 1); // <=2 байт по флагу
rs_arr->buffer[0] = CMD_UPLOAD;
RS_Send(rs_arr,rs_arr->buffer, 1); // <=2 байт по флагу
RS_Send(rs_arr,rs_arr->buffer, 1); // <=2 байт по флагу
RS_Wait4OK(rs_arr);
RS_BSend(rs_arr,(unsigned int*)Address, Length);
@@ -318,22 +358,24 @@ void upload(RS_DATA *rs_arr)
}
/** Прошить XILINX.
@precondition Должна быть произведена загрузка блока
Адрес и длина прошивки беретса из заголовка и
сравниваетса с переменными RecvPtr и Length, заполнаемыми в ф-ции load,
так же смотрит магическое слово в начале прошивки
/** Прошить XILINX.
@precondition Должна быть произведена загрузка блока
Адрес и длина прошивки беретса из заголовка и
сравниваетса с переменными RecvPtr и Length, заполнаемыми в ф-ции load,
так же смотрит магическое слово в начале прошивки
@see load */
/** @brief Обрабатывает совместимую команду программирования внешней логики. */
void xflash(RS_DATA *rs_arr)
{
return;
}
/** Прошить TMS.
@precondition Должна быть произведена загрузка блока
Адрес и длина прошивки беретса из заголовка и
сравниваетса с переменными RecvPtr и Length, заполнаемыми в ф-ции load
/** Прошить TMS.
@precondition Должна быть произведена загрузка блока
Адрес и длина прошивки беретса из заголовка и
сравниваетса с переменными RecvPtr и Length, заполнаемыми в ф-ции load
@see load */
/** @brief Обрабатывает команду программирования памяти целевого контроллера. */
void tflash(RS_DATA *rs_arr)
{
// volatile unsigned long Address1,Address2;
@@ -341,7 +383,7 @@ void tflash(RS_DATA *rs_arr)
/*
if(!rs_arr->BS_LoadOK)
{
RS_Line_to_receive(rs_arr); // режим приема RS485
RS_Line_to_receive(rs_arr); // режим приема RS485
RS_SetBitMode(rs_arr,9);
return;
}
@@ -366,7 +408,7 @@ void tflash(RS_DATA *rs_arr)
if( (Address2 < 0x100000) || (Address2 > 0x180000) || ((Address2+LengthW) > 0x180000) )
{
RS_Line_to_receive(rs_arr); // режим приема RS485
RS_Line_to_receive(rs_arr); // режим приема RS485
RS_SetBitMode(rs_arr,9);
return;
}
@@ -378,11 +420,15 @@ void tflash(RS_DATA *rs_arr)
return;
}
/** Прошить TMS.
@precondition Должна быть произведена загрузка блока
Адрес и длина прошивки беретса из заголовка и
сравниваетса с переменными RecvPtr и Length, заполнаемыми в ф-ции load
/** Прошить TMS.
@precondition Должна быть произведена загрузка блока
Адрес и длина прошивки беретса из заголовка и
сравниваетса с переменными RecvPtr и Length, заполнаемыми в ф-ции load
@see load */
/**
* @brief Выполняет расширенную BIOS-команду, не входящую в базовый набор.
* @details Подкоманда и её аргументы извлекаются из принятого сервисного кадра.
*/
void extendbios(RS_DATA *rs_arr)
{
volatile unsigned long Address1,Address2,Length;
@@ -407,10 +453,10 @@ void extendbios(RS_DATA *rs_arr)
switch ( code )
{
// Прошиваем EPROM Из RAM
// Прошиваем EPROM Из RAM
case 4: Seeprom_write(Address1,(unsigned int*)Address2,Length);
break;
// Читаем из EPROM в RAM
// Читаем из EPROM в RAM
case 5: Seeprom_read(Address1,(unsigned int*)Address2,Length);
break;

View File

@@ -1,11 +1,19 @@
/**
* @file cntrl_adr.c
* @brief Хранение и изменение сетевых адресов контроллера.
*
* Содержит рабочий, широковещательный, ответный и терминальный адреса узла.
* set_cntrl_addr() меняет только изменяемые адреса; вызывающий код отвечает
* за проверку диапазона и за отсутствие обмена во время перенастройки.
*/
/****************************************************************/
/* TMS320C32 */
/* ====== BIOS, КЛАИН, КЛВСП ====== */
/* ЦНИИ СЭТ (с) 1998-2000 г. */
/* ====== BIOS, КЛАИН, КЛВСП ====== */
/* ЦНИИ СЭТ (с) 1998-2000 г. */
/****************************************************************
cntrl_adr.c
****************************************************************
* Адрес контроллера *
* Адрес контроллера *
****************************************************************/
#include "cntrl_adr.h"
@@ -16,27 +24,34 @@
#define ADDR_UNIVERSAL_DEF 10
/** Установка адреса контроллера дла посылки всем АИНам */
/** Установка адреса контроллера дла посылки всем АИНам */
int ADDR_FOR_ALL = ADDR_FOR_ALL_DEF;
/** Установка адреса контроллера дла посылки ответа */
/** Установка адреса контроллера дла посылки ответа */
const int ADDR_ANSWER = ADDR_ANSWER_DEF;
/** Установка адреса терминала дла посылки ответа */
/** Установка адреса терминала дла посылки ответа */
const int ADDR_TERMINAL = ADDR_TERMINAL_DEF;
/* Универсальный адрес контроллера */
/* Универсальный адрес контроллера */
const int CNTRL_ADDR_UNIVERSAL=ADDR_UNIVERSAL_DEF;
/* Адрес контроллера */
/* Адрес контроллера */
int CNTRL_ADDR=1;
int cntr_addr_c;
int cntr_addr_c_all;
/** Установка адреса контроллера дла прошивки */
/** Установка адреса контроллера дла прошивки */
/**
* @brief Одновременно обновляет индивидуальный и групповой адреса узла.
* @param cntrl_addr Новый адрес данного контроллера.
* @param cntrl_addr_for_all Адрес, по которому принимаются групповые команды.
* @note Проверка допустимого диапазона выполняется уровнем конфигурации.
*/
void set_cntrl_addr (int cntrl_addr,int cntrl_addr_for_all)
{
/* Пара адресов меняется в одном месте, исключая смешанную конфигурацию. */
CNTRL_ADDR = cntrl_addr;
ADDR_FOR_ALL = cntrl_addr_for_all;
}

View File

@@ -1,3 +1,12 @@
/**
* @file crc16.c
* @brief Расчёт контрольных сумм CRC-CCITT и CRC-16 для протокольных кадров.
*
* Поддерживает табличные и побитовые варианты алгоритмов. Текущая конфигурация
* FAST_CRC/ONLY_CRC16 оставляет в прошивке только требуемый быстрый CRC-16.
* Размер задаётся в элементах буфера целевой платформы, а не в байтах host-PC;
* начальное значение CRC определяет вызывающий протокол.
*/
#include "crc16.h"
#define MAKE_TABS 0 /* Builds tables below */
#define FAST_CRC 1 /* If fast CRC should be used */
@@ -87,6 +96,13 @@ WORD crc_16_tab[] = {
/* CRC-CCITT is based on the polynomial x^16 + x^12 + x^5 + 1. Bits */
/* are sent MSB to LSB. */
/**
* @brief Продолжает расчёт CRC-CCITT по массиву слов.
* @param crc Начальное значение либо CRC предыдущего блока.
* @param buf Буфер данных; значим младший байт каждого слова.
* @param size Число обрабатываемых элементов.
* @return Итоговое 16-битное значение CRC-CCITT.
*/
unsigned int get_crc_ccitt(unsigned int crc,unsigned int *buf,unsigned long size )
{
#if !(FAST_CRC & !MAKE_TABS)
@@ -112,6 +128,13 @@ unsigned int get_crc_ccitt(unsigned int crc,unsigned int *buf,unsigned long size
/* CRC-16 is based on the polynomial x^16 + x^15 + x^2 + 1. Bits are */
/* sent LSB to MSB. */
/**
* @brief Вычисляет быстрый табличный CRC-16 с отражённым полиномом 0xA001.
* @param crc Начальное значение или результат предыдущей порции.
* @param buf Входные слова протокольного кадра.
* @param size Количество слов.
* @return CRC после обработки всего блока.
*/
unsigned int get_crc_16(unsigned int crc,unsigned int *buf,unsigned long size )
{
#if !(FAST_CRC & !MAKE_TABS)
@@ -139,6 +162,11 @@ unsigned int get_crc_16(unsigned int crc,unsigned int *buf,unsigned long size )
/**
* @brief Побитовый эталон расчёта CRC-16 без таблицы.
* @details Используется как компактная реализация и для сверки табличного
* варианта; восемь итераций обрабатывают каждый входной байт.
*/
unsigned int get_crc_16b(unsigned int crc,unsigned int *buf,unsigned long size )
{
@@ -175,6 +203,12 @@ unsigned long i;
return (crc & 0xffff);
}
/**
* @brief Проектная обёртка CRC-16 с установленным протоколом начальным значением.
* @param buf Кадр без добавляемого поля CRC.
* @param size Число элементов кадра.
* @return Контрольная сумма, готовая для сравнения или передачи.
*/
int get_crc16(unsigned int *buf, int size )
{
int crc16,i,j;

View File

@@ -1,3 +1,12 @@
/**
* @file ecan.c
* @brief Инициализация eCAN, передача кадров и обработка CAN-прерываний.
*
* Настраивает выбранный CAN-контроллер и mailbox, нормализует номер устройства,
* отправляет данные и ведёт счётчики приёма, передачи и ошибок. Регистры eCAN
* изменяются через shadow-копии согласно требованиям C2000; обработчики должны
* подтверждать источник прерывания и не задерживать остальные группы PIE.
*/
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
#include "DSP2833x_SWPrioritizedIsrLevels.h"
@@ -30,6 +39,14 @@ int CanTimeOutErrorTR = 0;
int wait=0;
/**
* @brief Настраивает eCAN-A и его почтовые ящики для выбранного номера узла.
* @param Port Номер интерфейса; текущая аппаратная реализация использует CAN-A.
* @param DevNum Номер устройства 1..16, насыщаемый реализацией на границах.
* @details Регистры управления изменяются через ECanShadow, чтобы исключить
* опасные read-modify-write операции над регистрами с особыми битами.
* @post Разрешены приёмные mailbox и прерывания CAN, счётчики ошибок сброшены.
*/
void InitCan(int Port, int DevNum)
{
struct ECAN_REGS ECanShadow;
@@ -40,6 +57,7 @@ void InitCan(int Port, int DevNum)
long id = 0x80BA0000;
/* Протокольная нумерация начинается с 1, аппаратное поле — с 0. */
DevNum--;
if(DevNum<0)DevNum=0;
if(DevNum>15)DevNum=15;
@@ -66,16 +84,16 @@ void InitCan(int Port, int DevNum)
// Required before writing the MSGIDs
ECanRegs->CANME.all = 0;
// задаем адрес 0 ащикa на передачу
// задаем адрес 0 ащикa на передачу
ECanMboxes->MBOX0.MSGID.all = id + 0x10 + DevNum;
// задаем адрес 1 ащикa на прием
ECanMboxes->MBOX1.MSGID.all = id + DevNum; //поменать!!!! 1 и 0!!!
// задаем адрес 1 ащикa на прием
ECanMboxes->MBOX1.MSGID.all = id + DevNum; //поменать!!!! 1 и 0!!!
// задаем режимы работы ащикa 0 на передачу, остальные на прием
// задаем режимы работы ащикa 0 на передачу, остальные на прием
ECanRegs->CANMD.all = 0xFFFFFFFE;
// выбираем только 2 ащикa дла работы, остальные запрещаем
// выбираем только 2 ащикa дла работы, остальные запрещаем
ECanRegs->CANME.all = 0x00000003;
// Clear all TAn bits
@@ -100,7 +118,7 @@ void InitCan(int Port, int DevNum)
ECanRegs->CANMC.all = ECanShadow.CANMC.all;
while(!ECanRegs->CANES.bit.CCE); // Wait for CCE bit to be set..
// настриваем скорость CAN
// настриваем скорость CAN
ECanShadow.CANBTC.all = ECanRegs->CANBTC.all;
ECanShadow.CANBTC.bit.SJWREG=1;
@@ -113,7 +131,7 @@ void InitCan(int Port, int DevNum)
ECanRegs->CANMC.all = ECanShadow.CANMC.all;
while(ECanRegs->CANES.bit.CCE); // Wait for CCE bit to be cleared..
// задаем таймауты дла ожиданиа отправки получениа посылки
// задаем таймауты дла ожиданиа отправки получениа посылки
ECanMOTORegs->MOTO0 = 550000;
ECanMOTORegs->MOTO1 = 550000;
@@ -140,7 +158,7 @@ void InitCan(int Port, int DevNum)
IER |= M_INT9; // Enable CPU INT
EDIS;
// завершили настройку CAN ащиков
// завершили настройку CAN ащиков
MessageReceivedCount = 0;
ErrorCount = 0;
@@ -148,6 +166,14 @@ void InitCan(int Port, int DevNum)
MessageTransivedCount=0;
}
/**
* @brief Формирует и запускает передачу одного прикладного CAN-кадра.
* @param Port Номер CAN-интерфейса.
* @param data Слова полезной нагрузки в формате проекта.
* @param Addr Идентификатор/адрес получателя.
* @note Перед записью mailbox проверяется завершение предыдущей передачи;
* тайм-аут отражается в CanTimeOutErrorTR.
*/
void CAN_send(int Port, int data[], int Addr)
{
unsigned long hiword,loword;
@@ -177,13 +203,19 @@ void CAN_send(int Port, int data[], int Addr)
ECanRegs->CANTSC = 0; // clear time-out counter
EDIS;
ECanRegs->CANTRS.all = 1; // запустить передачу
ECanRegs->CANTRS.all = 1; // запустить передачу
wait=1;
led1_toggle();
}
/**
* @brief Разбирает данные сработавшего приёмного mailbox.
* @param ECanMboxes Набор mailbox активного CAN-контроллера.
* @details Копирует полезную нагрузку в CAN_input_data и обновляет счётчик
* принятых сообщений; подтверждение аппаратного флага выполняет вызывающий ISR.
*/
void Handlai(volatile struct ECAN_MBOXES * ECanMboxes)
{
unsigned int adr;
@@ -215,6 +247,11 @@ void Handlai(volatile struct ECAN_MBOXES * ECanMboxes)
led2_toggle();
}
/**
* @brief Основной ISR eCAN-A для приёма кадров и завершения передачи.
* @details Определяет источник по CANRMP/CANTA, вызывает Handlai() для входного
* mailbox, очищает обслуженные флаги и подтверждает группу PIE9.
*/
interrupt void CANa_handler(void)
{
// Set interrupt priority:
@@ -234,6 +271,11 @@ interrupt void CANa_handler(void)
PieCtrlRegs.PIEIER9.all = TempPIEIER;
}
/**
* @brief ISR линии ошибок eCAN-A.
* @details Сохраняет диагностические счётчики, очищает причину ошибки и
* возвращает контроллер к приёму, не меняя прикладную адресацию mailbox.
*/
interrupt void CANa_reset_err(void)
{
// Set interrupt priority:

View File

@@ -1,12 +1,32 @@
/**
* @file filter_bat2.c
* @brief Один шаг рекурсивного фильтра второго порядка.
*
* filterbat() вычисляет новый выход по текущему входу и сохранённой истории,
* затем сдвигает состояние FILTERBAT. Каждый независимый сигнал обязан иметь
* собственный экземпляр структуры; коэффициенты задаются в filter_bat2.h и
* предполагают фиксированную частоту вызова, использованную при их расчёте.
*/
#include "filter_bat2.h"
/**
* @brief Вычисляет очередной отсчёт IIR-фильтра второго порядка.
* @param b Коэффициенты и история конкретного фильтруемого канала.
* @param InpVarCurr Текущий нефильтрованный отсчёт.
* @return Отфильтрованное значение.
* @pre b указывает на инициализированный объект FILTERBAT.
* @post История двух предыдущих входов и выходов сдвинута на один отсчёт.
*/
float filterbat(FILTERBAT *b, float InpVarCurr)
{
float y;
/* Прямая ветвь использует текущий и два предыдущих входных отсчёта. */
y = (b->k_0 * (InpVarCurr + (b->i_0*2) + b->i_1)) +
/* Рекурсивная ветвь возвращает два предыдущих выхода фильтра. */
(b->k_1 * b->u_0) + (b->k_2 * b->u_1);
/* Сначала сдвигается старая история, затем сохраняются новые значения. */
b->u_1=b->u_0;
b->u_0=y;
b->i_1=b->i_0;

View File

@@ -1,3 +1,12 @@
/**
* @file kanal.c
* @brief Последовательный вывод числовых данных на канальный индикатор.
*
* Управляет линиями DCLK/DOUT, формирует импульс сброса и передаёт разряды
* индикатора с учётом адреса, десятичной точки и множителя тактовой частоты.
* Задержки и таблица сегментов привязаны к внешней схеме; изменение GPIO или
* CLKMULT требует повторной проверки временной диаграммы.
*/
#include "DSP2833x_Device.h" // DSP281x Headerfile Include File
#include "DSP2833x_SWPrioritizedIsrLevels.h"
#include "filter_bat2.h"
@@ -15,12 +24,14 @@
int digits[16] = {63,6,91,79,102,109,125,7,127,111,64,0,0,0,121,0};
int readr[5] = {0x08,0x0C,0x10,0x00,0x04};
/** @brief Устанавливает тактовую линию канального последовательного интерфейса. */
void DCLK(int x)
{
if(x) GpioDataRegs.GPASET.bit.GPIO6=1;
else GpioDataRegs.GPACLEAR.bit.GPIO6=1;
}
/** @brief Устанавливает линию данных канального последовательного интерфейса. */
void DOUT(int x)
{
if(x) GpioDataRegs.GPASET.bit.GPIO8=1;
@@ -48,6 +59,10 @@ void DOUT(int x)
#define POWS1 80L
#endif
/**
* @brief Формирует последовательность сброса внешних регистров индикатора.
* @note Число холостых тактов является частью аппаратного протокола.
*/
void RESET()
{
DCLK(0);
@@ -64,6 +79,7 @@ void RESET()
#endif
}
/** @brief Выдаёт один бит данных и сопровождающий его импульс DCLK. */
void SENDBIT(int x)
{
DOUT(x); DCLK(1);
@@ -78,12 +94,20 @@ void SENDBIT(int x)
#endif
}
/**
* @brief Кодирует адрес и число и передаёт их семисегментному индикатору.
* @param adr Адрес физического канала.
* @param dat Значение для отображения.
* @param dot Позиция десятичной точки.
* @details Таблица digits преобразует цифры в сегменты; знак и ведущие позиции
* обрабатываются до побитовой отправки каждого разряда.
*/
void kanal_Send(int adr, long dat, int dot)
{
long Word,data,aliq_part,dg[4];
int i,j,bit,byt,addr,sgn=0,punkt=0,aliq_len=0,full_len, isdot=0;
if(adr>1) // Лампочки
if(adr>1) // Лампочки
{
Word =dat;
}
@@ -134,7 +158,7 @@ void kanal_Send(int adr, long dat, int dot)
for(i=3;i>0;i--)
{
if((dg[i]==0)&&(i!=dot))
dg[i]=0xF; // Это значит пусто
dg[i]=0xF; // Это значит пусто
else break;
}
@@ -143,7 +167,7 @@ void kanal_Send(int adr, long dat, int dot)
{
if( (dg[i]==0xF)||(i==3))
{
dg[i]=0xA; // Это значит минус
dg[i]=0xA; // Это значит минус
break;
} }

View File

@@ -1,17 +1,26 @@
/**
* @file log_to_mem.c
* @brief Кольцевой журнал событий во внешней области памяти.
*
* Резервирует секцию .logg, хранит границы и текущий адрес журнала и очищает
* всю отведённую область по запросу. LOG_PAGE_START/LOG_PAGE_LEN должны
* соответствовать linker command file; clear_mem() выполняет длительную
* блокирующую запись и не предназначена для вызова из обработчика прерывания.
*/
/****************************************************************/
/* TMS320C32 */
/* ====== BIOS, КЛАИН, КЛВСП ====== */
/* ЦНИИ СЭТ (с) 1998-2001г. */
/* ====== BIOS, КЛАИН, КЛВСП ====== */
/* ЦНИИ СЭТ (с) 1998-2001г. */
/****************************************************************/
/* log_to_mem.c
****************************************************************
* Запись логов в памyть *
* Запись логов в памyть *
****************************************************************/
#include "log_to_mem.h"
int no_write = 0,
never_write = 0; // Флаги, чтобы не писать (если что)
never_write = 0; // Флаги, чтобы не писать (если что)
#pragma DATA_SECTION(logs_block,".logg");
unsigned int logs_block[0xFA00];
@@ -19,7 +28,13 @@ unsigned int logs_block[0xFA00];
LOG Log;
unsigned int flog=0;
// Очищение памати, где логи лежат
// Очищение памати, где логи лежат
/**
* @brief Сбрасывает метаданные и физически очищает всю область журнала.
* @post Log.Adres установлен в LOG_PAGE_START, Log.Circl сброшен, а каждое
* слово выделенной области равно нулю.
* @warning Функция блокируется на LOG_PAGE_LEN операций внешней памяти.
*/
void clear_mem()
{
unsigned long i;
@@ -29,6 +44,7 @@ void clear_mem()
Log.Adres = Log.Start;
Log.Circl = 0;
/* Полуинтервал [Start, Finis) исключает запись за последним словом секции. */
for (i=Log.Start; i<Log.Finis; i++)
*(volatile int *)i = 0;
}

View File

@@ -1,3 +1,12 @@
/**
* @file main.c
* @brief Точка входа и основной диспетчер периферийной прошивки Balsam 167.
*
* Выполняет инициализацию тактирования, GPIO, интерфейсов и измерительных
* модулей, выбирает обработчик Timer1 по типу платы и затем координирует CAN,
* RS-485, команды и контроль состояния. Порядок инициализации важен: глобальные
* прерывания разрешаются только после настройки таблицы PIE и зависимых блоков.
*/
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
#include "cntrl_adr.h"
@@ -23,6 +32,13 @@
#include "measure.h"
extern void DSP28x_usDelay(Uint32 Count);
/**
* @brief Настраивает CPU Timer1 и выбирает его ISR по роли платы.
* @details Для пульта используется cpu_timer1_isr_PULT(), для остальных ролей
* — измерительный cpu_timer1_isr_SENS(). Период равен одному тику READY_FREQ.
* @pre Desk уже считан из конфигурационных GPIO.
* @post Вектор XINT13 установлен, Timer1 запущен, CPU INT13 разрешён.
*/
void timer_Init()
{
EALLOW; // This is needed to write to EALLOW protected registers
@@ -35,6 +51,17 @@ void timer_Init()
IER |= M_INT13;
}
/**
* @brief Инициализирует устройство и выполняет бесконечный диспетчерский цикл.
*
* @details Последовательно поднимает системное тактирование, внешнюю память,
* PIE, GPIO, UART, EEPROM, CAN, АЦП и таймеры. Затем обслуживает завершённые
* RS-485 кадры, сервисные команды, CAN-периодику, кнопки и контроль связи.
* Точные ветви исполнения определяются Desk и опциями package.h.
*
* @warning Порядок запуска существенен: ISR не должны сработать до создания
* буферов, установки векторов и инициализации разделяемого состояния.
*/
void main()
{
int i,j,mask;
@@ -46,8 +73,10 @@ void main()
InitSysCtrl();
// Disable CPU interrupts and clear all CPU interrupt flags:
/* Конфигурация PIE и периферии выполняется при запрещённых прерываниях CPU. */
DINT;
/* После сброса PIE источники разрешаются отдельно каждым драйвером. */
InitPieCtrl();
IER = 0x0000;
@@ -56,6 +85,7 @@ void main()
InitPieVectTable();
init_zone7();
/* Роль платы определяет дальнейшую разводку ADC, GPIO и Timer1 ISR. */
get_Mode();
InitCan(0,Mode);
@@ -83,6 +113,7 @@ void main()
clear_mem();
set_cntrl_addr(Mode,16);
timer_Init();
/* Все базовые векторы и транспортные состояния готовы к обслуживанию ISR. */
EnableInterrupts();
if(Desk!=dsk_EPLT)
@@ -104,8 +135,10 @@ void main()
SysCtrlRegs.WDCR= 0x2F;
EDIS;
/* READY разрешает измерительному ADC ISR использовать накопленные данные. */
READY=1;
/* Foreground-цикл обрабатывает события, подготовленные ISR и таймерами. */
while(1)
{
if(CanGO)
@@ -206,16 +239,16 @@ void main()
led2_toggle();
switch(j)
{
case CMD_INIT: init(rs); break; // íŕ÷ŕëüíűĺ óńňŕíîâęč
case CMD_INITLOAD: initload(rs); break; // íŕńňđîéęŕ çŕăđóçęč
case CMD_RUN: run(rs); break; // çŕăđóçčňü áëîę
case CMD_LOAD: load(rs); break; // çŕăđóçčňü áëîę
case CMD_PEEK: peek(rs); break; // ďđî÷čňŕňü ŕ÷ĺéęó ďŕěŕňč
case CMD_POKE: poke(rs); break; // çŕďčńŕňü â ŕ÷ĺéęó ďŕěŕňč
case CMD_UPLOAD: upload(rs); break; // ďĺđĺäŕňü áëîę ďŕěŕňč
case CMD_EXTEND: extendbios(rs); break; // đŕńřčđĺííűĺ ęîěŕíäű äëŕ áčîńŕ
case CMD_INIT: init(rs); break; // начальные установки
case CMD_INITLOAD: initload(rs); break; // настройка загрузки
case CMD_RUN: run(rs); break; // загрузить блок
case CMD_LOAD: load(rs); break; // загрузить блок
case CMD_PEEK: peek(rs); break; // прочитать ачейку памати
case CMD_POKE: poke(rs); break; // записать в ачейку памати
case CMD_UPLOAD: upload(rs); break; // передать блок памати
case CMD_EXTEND: extendbios(rs); break; // расширенные команды дла биоса
case CMD_TFLASH: tflash(rs); break; // ďđîřčňü TMS
case CMD_TFLASH: tflash(rs); break; // прошить TMS
case CMD_MODBUS_3: ReceiveCommandModbus3(rs); break;
case CMD_MODBUS_6: ReceiveCommandModbus6(rs); break;

View File

@@ -1,3 +1,27 @@
/**
* @file
* @brief Обработка измерительных каналов, индикации и защит устройства.
*
* @details
* Модуль выполняет настройку каналов датчиков, фильтрацию результатов АЦП,
* пересчёт токов, напряжений и температур в физические величины, формирование
* диагностических признаков, а также подготовку масок передаваемых данных.
* Обработчик CPU Timer1 обслуживает сторожевой таймер, период запуска CAN-обмена
* и световую индикацию.
*
* Модуль использует глобальные параметры конфигурации, таблицы измерений и
* аппаратные функции, объявленные в подключаемых заголовочных файлах проекта.
*
* @note Исходная исполняемая логика сохранена; добавлены комментарии
* в формате Doxygen и поясняющие комментарии непосредственно внутри кода.
*/
/**
* @defgroup sensor_processing Обработка датчиков и диагностик
* @brief Инициализация, измерение, фильтрация и контроль измерительных каналов.
* @{
*/
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
#include "DSP2833x_SWPrioritizedIsrLevels.h"
#include "filter_bat2.h"
@@ -14,85 +38,187 @@
#include "message.h"
#include "log_to_mem.h"
#include <math.h> // Это чтобы мерить амплитуду! sqrt без этого будет крив!!!
#include <math.h> // Это чтобы мерить амплитуду! sqrt без этого будет крив!!!
/**
* @brief Состояние периодического запуска CAN-обмена.
*
* `CanPowse` увеличивается обработчиком таймера до значения `CANPOWSE`.
* При достижении порога счётчик сбрасывается, а `CanGO` устанавливается в 1.
*/
unsigned int CanPowse=CANPOWSE,CanGO=0;
/**
* @brief Битовые маски состава быстрых и медленных пакетов.
*
* Первый индекс выбирает режим передачи (`m_FAST` или `m_SLOW`), второй —
* 16-битное слово общей таблицы данных.
*/
unsigned int Maska[2][8];
int READY=0; // Все загрузилос
/** @brief Флаг завершения начальной загрузки и готовности модуля. */
int READY=0; // Все загрузилос
int TPL_CANS=0; // Количество температурных каналов
int tpl_cans=0; // Количество температурных датчиков
int cal_addr=0; // Начало калибровочных датчиков
int pow_addr=0; // Начало датчиков напражениа\тока
/** @brief Настроенное количество температурных групп TPL для текущего режима. */
int TPL_CANS=0; // Количество температурных каналов
/** @brief Фактическое количество логических температурных каналов. */
int tpl_cans=0; // Количество температурных датчиков
/** @brief Индекс начала калибровочных каналов в общей таблице датчиков. */
int cal_addr=0; // Начало калибровочных датчиков
/** @brief Индекс начала каналов измерения напряжения и тока. */
int pow_addr=0; // Начало датчиков напражениа\\тока
/**
* @brief Периоды, используемые логикой готовности и мигания индикации.
*/
int period_ready, period_blink;
/**
* @brief Накопленные и зафиксированные сводные признаки состояния.
*
* `chk` накапливает признаки в текущем цикле обработки каналов, а `sig`
* получает снимок накопленного состояния при начале следующего цикла.
*/
FLAG chk,sig;
/**
* @brief Временные интервалы, выраженные в периодах обработки АЦП.
*
* Значения рассчитываются функцией Init_sensors() из частоты `ADC_FREQ`.
*/
unsigned int time_1_5sec, time_5msec, time_3sec, time_5sec;
/** @brief Счётчики выдержки пониженного уровня для двух силовых каналов. */
unsigned int low_count[2] = {0,0};
/** @brief Интегрирующие счётчики ошибок по фазным значениям. */
unsigned int err_count[6];
/** @brief Действующие значения измеряемых фаз, полученные из lev_quadr[]. */
int lev_count[6];
/** @brief Состояния фильтра квадратов мгновенных фазных значений. */
float lev_quadr[6];
/** @brief Оценки среднего, то есть нулевого, уровня четырёх каналов АЦП. */
float zer_count[4];
/** @brief Тип каждого логического измерительного канала. */
int sens_type[24];
/** @brief Индекс парного канала для совместной диагностики и вычислений. */
int sens_pair[24];
/** @brief Счётчики фильтрации диагностик дискретных входов. */
unsigned int din_count[32];
/** @brief Температуры, полученные от внешних измерительных устройств. */
int ext_temp[8];
/** @brief Диагностические состояния внешних температурных каналов. */
int ext_diag[8];
/** @brief Рабочий счётчик времени процедуры калибровки. */
unsigned int Caliber_time = 0;
/** @brief Минимальный уровень тока, при котором контролируется перекос фаз. */
int Curr_Edge;
/**
* @brief Маска состояний, исключающих канал из выбора опорных данных имитации.
*/
ERROR okay;
/**
* @brief Коэффициенты пересчёта кодов АЦП в физические величины.
*
* Начальные значения соответствуют типам питающих каналов. Первые четыре
* коэффициента могут быть переопределены функцией calc_sensor_koef() по
* калибровочным значениям `Caliber[]`.
*/
float powK[] =
{
0.127, // питание 380В
0.127, // питание 380В
0.127, // питание 380В
0.127, // питание 380В
1.000, // ток
1.000, // напражение
1.000, // ток
1.000, // напражение
0.0076, // питание 31В
0.0076, // питание 24В
0.0076, // питание 24В
0.0076, // питание 15В
1.0000, // термодатчик мелкосхема
0.0076, // питание 31В
0.0076, // питание 24В
0.0076, // питание 24В
0.0076, // питание 15В
1.0000, // термодатчик мелкосхема
};
/** @brief Шаблон начального состояния цифрового фильтра. */
FILTERBAT def_FILTERBAT = DEF_FILTERBAT;
FILTERBAT adc_filter[20+4]; // макс.TPL*2 + 4 калибр
FILTERBAT out_filter[20+4]; // макс.TPL*2 + 4 напраж
FILTERBAT zer_filter[4]; // 4 напраж
/** @brief Фильтры входных значений АЦП и калибровочных каналов. */
FILTERBAT adc_filter[20+4]; // макс.TPL*2 + 4 калибр
/** @brief Вторичные фильтры выходных и калибровочных значений. */
FILTERBAT out_filter[20+4]; // макс.TPL*2 + 4 напраж
/** @brief Фильтры оценок нулевого уровня четырёх силовых каналов. */
FILTERBAT zer_filter[4]; // 4 напраж
/** @brief Рабочие счётчики состояний измерительных каналов. */
long sens_count[24+4];
/** @brief Коэффициенты пересчёта температурных каналов групп 100 и 50. */
float K100,K_50;
/** @brief Коэффициенты пересчёта первой и второй температурных ветвей. */
float K_T1,K_T2;
/**
* @brief Обработчик прерывания CPU Timer1 для обслуживания датчиков и индикации.
*
* Обработчик обслуживает сторожевой таймер, формирует периодический флаг
* `CanGO`, управляет индикацией готовности, аварии и нагрева, а в режиме
* `dsk_LOAD` передаёт выполнение функции Load_runner().
*
* @note Функция использует статические счётчики ШИМ яркости и мигания ламп.
*/
interrupt void cpu_timer1_isr_SENS(void);
/********************************************************************/
/* Расчет модула тока из показаний двух фаз */
/********************************************************************/
/**
* @brief Рассчитывает модуль трёхфазного вектора по двум измеренным фазам.
*
* @param ia Мгновенное значение первой измеренной фазы.
* @param ib Мгновенное значение второй измеренной фазы.
*
* @return Модуль результирующего вектора в тех же единицах, что и входы.
*
* @note Расчёт предполагает, что третья фаза может быть восстановлена из
* условия `ia + ib + ic = 0`.
*/
float im_calc(float ia,float ib)
{
float isa,isb;
/*
* Из двух измеренных фаз формируются две взаимно перпендикулярные
* составляющие результирующего трёхфазного вектора. Третья фаза
* подразумевается равной -(ia + ib).
*/
isa = - 1.5 * (ia + ib);
/* COSPi6 = cos(pi/6) = sqrt(3)/2; эта составляющая зависит от разности фаз. */
isb = COSPi6 * (ia - ib);
/* Нормировка 2/3 возвращает модуль в масштабе исходных фазных величин. */
return (2*sqrt(isa*isa+isb*isb)/3);
}
/**
* @brief Пересчитывает коэффициенты преобразования измерительных каналов.
*
* Для температурных каналов коэффициенты выбираются по активному варианту
* `TermoRS` или `TermoAD`. Коэффициенты первых четырёх силовых каналов
* рассчитываются из массива `Caliber[]` с учётом типа канала.
*
* @pre Разности калибровочных точек, используемые как делители, не равны нулю.
* @post Обновлены `K100`, `K_50`, `K_T1`, `K_T2` и `powK[0..3]` в зависимости
* от выбранной конфигурации.
*/
void calc_sensor_koef()
{
int i;
float K;
/*
* Вариант резистивных температурных датчиков. Коэффициент равен
* заданному температурному диапазону, делённому на разность двух
* сохранённых калибровочных кодов АЦП.
*/
if(TermoRS)
{
K100 = 129.0/(K150_D - K100_D);
@@ -101,6 +227,10 @@ void calc_sensor_koef()
K_T2 = 129.0/(K150_2 - K100_2);
}
/*
* Вариант аналоговых термодатчиков: для каждой ветви используется
* собственная пара калибровочных точек K300/K400.
*/
if(TermoAD)
{
K_T1 = 100.0/(K400_1 - K300_1);
@@ -108,66 +238,101 @@ void calc_sensor_koef()
}
// if(Kurrent)
/* Перевод пользовательских калибровок первых четырёх каналов в float-масштаб. */
for(i=0;i<4;i++)
{
K = Caliber[i];
/* Для тока и остальных величин калибровка хранится с разным десятичным масштабом. */
if(sens_type[i]==CURRENT) K/=1000.0;
else K/=10000.0;
powK[i] = K;
} }
/** @copydoc cpu_timer1_isr_SENS */
interrupt void cpu_timer1_isr_SENS(void)
{
/*
* Статические переменные сохраняют фазу программного ШИМ яркости,
* фазу мигания и требуемые логические состояния ламп между прерываниями.
*/
static unsigned int
count_blink=0, count_bright=0, count_mode,
blink_over, blink_alarm, work_lamp, heat_lamp, power_lamp;
/* Предыдущее состояние теста нужно для обнаружения фронта команды тестирования. */
static int preTest;
/* Объединённый запрос теста ламп от двух источников управления. */
int TST;
#define err_lamp work_lamp // это дла шкапа,
#define alm_lamp heat_lamp // чтоб было понатнее
#define err_lamp work_lamp // Для исполнения шкафа первый светодиод трактуется как «Ошибка».
#define alm_lamp heat_lamp // Второй светодиод трактуется как «Авария».
/*
* Разрешается запись в защищённые регистры, обновляется статистика таймера
* и настраивается маска прерываний. EINT разрешает вложенные прерывания
* с приоритетом, оставленным маской MINT13.
*/
EALLOW;
CpuTimer1.InterruptCount++;
IER |= M_INT13; // Set "global" priority
IER &= MINT13; // Set "global" priority
EINT;
EDIS; // This is needed to disable write to EALLOW protected registers
EDIS; // Запрет дальнейшей записи в защищённые EALLOW-регистры.
/* Во время активного сброса сторожевой таймер намеренно не обслуживается. */
if(!cReset) ServiceDog();
/* Делитель частоты: один раз в CANPOWSE прерываний разрешается очередной CAN-цикл. */
if(++CanPowse >= CANPOWSE)
{
CanPowse = 0;
CanGO = 1;
}
/* Тест включён, если установлен хотя бы один из двух командных флагов. */
TST = cTestLamp|bTestLamp;
/*
* Индикатор READY мигает при отсутствии ошибки; тест ламп принудительно
* разрешает его переключение. Здесь применено побитовое OR для флагов 0/1.
*/
if(!sig.bit.Error|TST) toggle_READY();
else clear_READY();
/* В исполнении нагрузки дальнейшей индикацией занимается отдельный автомат. */
if(Desk==dsk_LOAD)
{
Load_runner();
return;
}
/*
* Начало нового периода программного ШИМ яркости. Нужные выходы сначала
* включаются согласно логическим состояниям ламп, а ниже будут выключены
* при достижении значения Brightness.
*/
if(++count_bright == maximum_bright)
{
count_bright = 0 ;
/* Назначение дискретного RES_OUT_1 зависит от исполнения устройства. */
if( ((Desk==dsk_SHKF) && power_lamp) ||
((Desk==dsk_COMM) && heat_lamp) ) set_RES_OUT_1();
else clear_RES_OUT_1();
if(work_lamp) set_LED_OUT_1(); // Она же err_lamp дла шкапа
if(work_lamp) set_LED_OUT_1(); // Она же err_lamp для шкафа.
else clear_LED_OUT_1();
if(heat_lamp) set_LED_OUT_2(); // Она же alm_lamp дла шкапа
if(heat_lamp) set_LED_OUT_2(); // Она же alm_lamp для шкафа.
else clear_LED_OUT_2();
}
/*
* Конец активной части ШИМ. В режиме теста выходы не гасятся по Brightness,
* поэтому лампы проверяются с полной заданной логической активностью.
*/
if(!TST)
if(count_bright == Brightness)
{
@@ -176,6 +341,7 @@ interrupt void cpu_timer1_isr_SENS(void)
clear_RES_OUT_1();
}
/* На фронте теста немедленно запускается новый цикл мигания с известной фазы. */
if(TST & !preTest)
{
count_blink = BLINK_TIME;
@@ -183,13 +349,19 @@ interrupt void cpu_timer1_isr_SENS(void)
}
preTest = TST;
/* Обновление медленной фазы индикации выполняется один раз за BLINK_TIME. */
if(++count_blink >= BLINK_TIME)
{
count_blink=0;
count_mode++;
/* Быстрый шаблон меняет состояние на каждом шаге. */
blink_over = (count_mode & 1)?1:0;
/* Медленный шаблон равен нулю только на каждом восьмом шаге. */
blink_alarm = (count_mode & 7)?1:0;
/* При тесте все лампы получают один и тот же мигающий шаблон. */
if(TST)
{
heat_lamp = blink_over;
@@ -198,13 +370,18 @@ interrupt void cpu_timer1_isr_SENS(void)
}
else
{
/* Отдельная схема индикации для шкафа. */
if(Mode==adr_SHKF)
{
/* Наличие питания показывается постоянно, пока нет общей ошибки. */
power_lamp= 1;
/* Общая авария мигает на выходе alm_lamp. */
if(sig.bit.Alarm){// power_lamp= blink_alarm;
alm_lamp = blink_over; }
else alm_lamp = 0;
/* Ошибка останова заставляет одновременно мигать питание и err_lamp. */
if(sig.bit.Error){ power_lamp= blink_over;
err_lamp = blink_over; }
else err_lamp = 0;
@@ -214,22 +391,45 @@ interrupt void cpu_timer1_isr_SENS(void)
// if(sig.bit.Error) work_lamp = blink_over;
// else if(sig.bit.Alarm) work_lamp = blink_alarm;
// else
/* В штатном режиме лампа работы горит непрерывно. */
work_lamp = 1;
/* Секретная команда заменяет постоянное свечение медленным шаблоном. */
if(bSecretBt|cSecretBt) work_lamp = blink_alarm;
/*
* Температурная индикация имеет приоритет: перегрев — постоянно,
* предупреждение — быстрое мигание, обрыв — короткий импульс
* на каждом восьмом шаге.
*/
if(sig.bit.OverHeat) heat_lamp = 1;
else if(sig.bit.SubHeat) heat_lamp = blink_over;
else if(sig.bit.OutHeat) heat_lamp = !blink_alarm;
else heat_lamp = 0;
} } } }
/**
* @brief Выполняет базовую инициализацию измерительных каналов.
*
* Функция определяет число и размещение каналов по `Mode`, назначает типы и
* пары датчиков, сбрасывает фильтры и счётчики, настраивает специальные каналы
* для выбранного исполнения `Desk`, а также переводит временные выдержки из
* секунд в число периодов `ADC_FREQ`.
*
* @post Инициализированы таблицы `sens_type[]`, `sens_pair[]`, фильтры,
* диагностические счётчики и маска `okay`.
*/
void Init_sensors()
{
int i;
/* Сначала формируется пустая карта каналов; далее её заполняет выбранный Mode. */
TPL_CANS = tpl_cans = cal_addr = 0;
/*
* Определение числа температурных каналов и позиции калибровочных входов.
* В режимах TRN/POW на одну группу TPL приходится два логических датчика.
*/
switch(Mode)
{
case adr_TRN1:
@@ -246,17 +446,24 @@ void Init_sensors()
case adr_SHKF: tpl_cans = TPL_SHK; cal_addr = tpl_cans-2; break;
}
/* Силовые каналы размещаются после локальных и внешних температурных каналов. */
pow_addr = tpl_cans + Owen;
/* Общие значения по умолчанию для всей статической таблицы датчиков. */
for(i=0;i<24;i++)
{
/* Тип 0 означает, что специальная обработка каналу пока не назначена. */
sens_type[i]=0;
/* До явной настройки парным считается сам канал. */
sens_pair[i]=i;
/* Каждый канал получает независимое начальное состояние обоих фильтров. */
adc_filter[i] = def_FILTERBAT;
out_filter[i] = def_FILTERBAT;
}
/* Назначение типа всем локальным температурным каналам. */
if(TermoSW)
for(i=0;i<tpl_cans;i++)
{
@@ -264,32 +471,45 @@ void Init_sensors()
if(TermoAD) sens_type[i] = TERMO_AD;
}
/* Внешние каналы OWEN следуют непосредственно за локальными температурами. */
for(i=0;i<Owen;i++)
{
sens_type[tpl_cans+i] = TRM_OBEH;
}
/* Настройка четырёхканального тракта измерения тока/напряжения. */
if(Kurrent)
{
/* Ниже этого RMS-уровня контроль перекоса фаз не выполняется. */
Curr_Edge = 200;
for(i=0;i<4;i++)
{
/* Базово все четыре силовых входа считаются каналами напряжения. */
sens_type[pow_addr+i]=VOLTAGE;
/* XOR с 1 образует пары 0<->1 и 2<->3. */
sens_pair[pow_addr+i]=pow_addr+(i^1);
/* Старт оценки нуля с последнего сохранённого значения. */
zer_count[i] = Zero_lev[i];
zer_filter[i] = def_FILTERBAT;
} }
/* Для стенда нагрузки вторая пара силовых входов измеряет ток. */
if(Desk==dsk_LOAD)
{
/* Для этого исполнения контроль перекоса начинается с более высокого уровня. */
Curr_Edge = 300;
sens_type[2]=
sens_type[3]=CURRENT;
}
/*
* В шкафном исполнении карта каналов фиксирована аппаратной схемой:
* первые шесть входов образуют три пары, остальные обрабатываются отдельно.
*/
if(Mode==adr_SHKF)
{
sens_type[0] = POWER_380; sens_pair[0]=1;
@@ -310,45 +530,72 @@ void Init_sensors()
sens_type[14] = TERMO_AD;
}
/* Сброс принятой карты дискретных неисправностей. */
DigErr.all=0;
/* Все выдержки ниже хранятся не в секундах, а в количестве циклов ADC_FREQ. */
time_1_5sec = (3 * ADC_FREQ) / 2;
time_5msec = (5 * ADC_FREQ) / 1000;
time_3sec = (3 * ADC_FREQ);
time_5sec = (5 * ADC_FREQ);
/*
* Канал с любым из этих признаков не используется как исправный источник
* при имитации температурного датчика в Temper_count().
*/
okay.all=0;
okay.bit.Tear=1;
okay.bit.Bypas=1;
okay.bit.Ignor=1;
okay.bit.Imit=1;
/* Сброс накопителей RMS и выдержек фазных неисправностей. */
for(i=0;i<6; i++)
{
err_count[i] = 0;
lev_count[i] = 0;
lev_quadr[i] = 0;
}
/* Сброс остальных счётчиков и буферов внешних температур. */
for(i=0;i<28;i++) sens_count[i] = 0;
for(i=0;i<32;i++) din_count[i] = 0;
for(i=0;i<8; i++) ext_temp[i] = ext_diag[i] = 0;
}
/**
* @brief Завершает инициализацию после загрузки рабочей конфигурации.
*
* Очищает оперативные измерения и признаки ошибок, восстанавливает нулевые
* уровни, задаёт начальные значения для режима нагрузки, включает игнорирование
* отдельных каналов в специальных исполнениях и пересчитывает коэффициенты.
*
* @pre Ранее вызвана Init_sensors(), а калибровочные параметры уже доступны.
*/
void Init_sensors_more()
{
int i;
/*
* Удаляются динамические биты состояния, но сохраняются служебные биты,
* разрешённые маской NOER. Параллельно очищаются измеренные значения.
*/
for(i=0;i<24;i++)
{
modbus[i] &= NOER;
sens_data[i]=0;
}
/* Восстановление сохранённых нулей силовых АЦП. */
for(i=0;i<4;i++)
{
zer_count[i] = Zero_lev[i];
/*
* Для LOAD до появления реального измерения задаются стартовые значения
* напряжения, различающиеся для чётного и нечётного входа пары.
*/
if(Desk==dsk_LOAD)
if(sens_type[i]==VOLTAGE)
{
@@ -356,6 +603,7 @@ void Init_sensors_more()
else sens_data[i]=390;
} }
/* В COMM все четыре силовых канала запускаются с установленным признаком Ignore. */
if(Desk==dsk_COMM)
{
Modbus[pow_addr+0].bit.bitE = 1; // Ignore
@@ -364,91 +612,186 @@ void Init_sensors_more()
Modbus[pow_addr+3].bit.bitE = 1; // Ignore
}
/* В LOAD игнорируются первые два канала общей таблицы. */
if(Desk==dsk_LOAD)
{
Modbus[0].bit.bitE = 1; // Ignore
Modbus[1].bit.bitE = 1; // Ignore
}
/* Коэффициенты рассчитываются после того, как типы каналов окончательно назначены. */
calc_sensor_koef();
}
/**
* @brief Формирует маски данных быстрых и медленных пакетов обмена.
*
* Состав масок зависит от исполнения `Desk`, количества температурных каналов,
* наличия измерения тока (`Kurrent`) и внешних каналов (`Owen`).
*
* @post Массив `Maska[m_FAST][]` и `Maska[m_SLOW][]` содержит актуальный набор
* передаваемых диагностик, показаний, уставок и команд.
*/
void Init_packMask()
{
int i,j,can=0;
/* Количество каналов, для которых надо включить диагностику и показания. */
can = pow_addr;
/* При наличии силового измерителя к базовой карте добавляются четыре канала. */
if(Kurrent) can += 4;
/* Полная очистка двух наборов по восемь 16-битных слов. */
for(i=0;i<2;i++)
for(j=0;j<8;j++) { Maska[i][j]=0; }
switch(Desk)
{
case dsk_EPLT:
Maska[m_SLOW][0]|= 0x000F; // Полученное
Maska[m_SLOW][6]|= 0x01FC; // Яркость ламп и одиночество
/* Для EPLT используется фиксированный сокращённый набор медленных данных. */
Maska[m_SLOW][0]|= 0x000F; // Полученные данные.
Maska[m_SLOW][6]|= 0x01FC; // Яркость ламп и служебные признаки.
break;
default:
/* Для каждого канала включается один бит диагностики и один бит показания. */
for(i=0;i<can;i++)
{
Maska[m_FAST][ i /16]|=(1<<( i %16)); // Диагностика
Maska[m_FAST][(i+24)/16]|=(1<<((i+24)%16)); // Показаниа
}
for(i=0;i<3; i++)
Maska[m_SLOW][i+3] = Maska[m_FAST][i]; // Уставки
/* Диагностики занимают битовые позиции, начиная с 0. */
Maska[m_FAST][ i /16]|=(1<<( i %16)); // Диагностика.
if(Desk==dsk_BKSD) Maska[m_SLOW][7]|= 0x0300; // Калибровка термодатчиков
else
Maska[m_SLOW][7]|= 0x0F00; // Калибровка термодатчиков
if(Kurrent)
{ Maska[m_SLOW][7]|= 0x000F; // Ноль датчиков напражениа
Maska[m_SLOW][7]|= 0x00F0; // Калибровка датчиков напражениа
Maska[m_FAST][6]|= 0x3F00; // Фазные значениа
/* Показания размещены в том же пространстве со смещением 24 бита. */
Maska[m_FAST][(i+24)/16]|=(1<<((i+24)%16)); // Показания.
}
if(Owen) Maska[m_FAST][6]|= 0x0008; // Период опроса OWEN
/* Первые три слова быстрой карты копируются в область масок уставок. */
for(i=0;i<3; i++)
Maska[m_SLOW][i+3] = Maska[m_FAST][i]; // Уставки.
/* BKSD передаёт две калибровки температуры, остальные исполнения — четыре. */
if(Desk==dsk_BKSD) Maska[m_SLOW][7]|= 0x0300; // Калибровка термодатчиков.
else
Maska[m_SLOW][7]|= 0x0F00; // Калибровка термодатчиков.
if(Kurrent)
{
/* Четыре нулевых уровня и четыре коэффициента силовых каналов. */
Maska[m_SLOW][7]|= 0x000F; // Ноль датчиков напряжения.
Maska[m_SLOW][7]|= 0x00F0; // Калибровка датчиков напряжения/тока.
/* Шесть RMS-значений: по три фазы для двух измерительных групп. */
Maska[m_FAST][6]|= 0x3F00; // Фазные значения.
}
/* При наличии OWEN включается поле периода его опроса. */
if(Owen) Maska[m_FAST][6]|= 0x0008; // Период опроса OWEN.
if(Desk==dsk_LOAD)
{ Maska[m_SLOW][7]|= 0x0030; // Калибровка ЦАП
Maska[m_FAST][7]|= 0xE000; // Дублировать команды
Maska[m_FAST][1]|= 0x3000; // Управлающий ток СИФУ
{
/* Дополнительные поля, используемые только стендом нагрузки. */
Maska[m_SLOW][7]|= 0x0030; // Калибровка ЦАП.
Maska[m_FAST][7]|= 0xE000; // Дублирование команд.
Maska[m_FAST][1]|= 0x3000; // Управляющий ток СИФУ.
} }
Maska[m_FAST][1]|= 0x0001; // Дискретные входы
Maska[m_SLOW][6]|= 0x0007; // Яркость ламп, и периоды посылок
Maska[m_SLOW][7]|= 0xE000; // Адрес, команды, и чтоб не вылазило
/* Эти служебные поля включаются независимо от исполнения. */
Maska[m_FAST][1]|= 0x0001; // Дискретные входы.
Maska[m_SLOW][6]|= 0x0007; // Яркость ламп и периоды посылок.
Maska[m_SLOW][7]|= 0xE000; // Адрес и командные поля.
}
/**
* @brief Фильтрует логическое условие интегрирующим счётчиком.
*
* При активном условии счётчик увеличивается до порога, после чего функция
* возвращает 1. При снятии условия счётчик уменьшается; в переходной области
* возвращается переданное предыдущее состояние `pre`.
*
* @param term Текущее логическое условие: ненулевое значение означает аварию.
* @param count Указатель на накопительный счётчик условия.
* @param edge Порог подтверждения условия в циклах вызова.
* @param pre Состояние, сохраняемое на этапе накопления или отпускания.
*
* @return 1 при подтверждённом условии; 0 при полностью снятом условии;
* иначе значение `pre`.
*
* @pre `count` указывает на доступную переменную счётчика.
*/
int er_anal(int term, unsigned int * count, long edge, int pre)
{
{
/* Активное условие постепенно заполняет счётчик подтверждения. */
if (term)
{
/* После достижения edge состояние считается устойчиво подтверждённым. */
if((*count)>=edge) return 1;
/* До порога сохраняется предыдущее логическое состояние. */
(*count)++; return pre;
}
}
/* При снятии условия подтверждение также отпускается постепенно. */
if( (*count) == 0 ) return 0;
(*count)--; return pre;
}
/**
* @brief Обновляет состояние ошибок выбранного датчика.
*
* Сохраняет служебные признаки, заданные маской `NOER`, добавляет вычисленные
* ошибки, обновляет сводный признак останова `chk.bit.Error` и формирует признак
* готовности канала как инверсию `Stop`.
*
* @param sens Индекс датчика в массиве `sens_error[]`.
* @param err Новое вычисленное состояние ошибок канала.
*/
void reset_errs(int sens, ERROR err)
{
ERROR set;
/* Служебные флаги канала сохраняются, вычисляемые ошибки заменяются новыми. */
set.all = (sens_error[sens].all & NOER) | err.all;
/* Общий Stop накапливается по всем каналам текущего цикла обработки. */
chk.bit.Error|= set.bit.Stop;
/* Канал готов только тогда, когда его ошибки не требуют останова. */
set.bit.Ready = !set.bit.Stop;
sens_error[sens] = set;
}
/**
* @brief Обрабатывает один канал измерения тока или напряжения.
*
* Функция отслеживает нулевой уровень АЦП, вычисляет мгновенное и действующее
* значение фазы, а для второго канала пары — амплитуду результирующего вектора
* и восстановленное действующее значение третьей фазы. Результаты записываются
* в `sens_data[]` и диагностическую область `modbus[]`. Дополнительно выполняется
* контроль перекоса фаз, верхнего и нижнего уровней.
*
* @param chan Относительный номер силового канала; допустимый диапазон задаётся
* массивами четырёхканального измерительного тракта.
*
* @pre Выполнены Init_sensors() и Init_sensors_more(); индекс `chan` допустим
* для `adc_table_lem[]`, `zer_count[]`, `powK[]` и связанных массивов.
*/
void Current_count(int chan)
{
/* Numb — рабочее значение; Zer — оценка нуля; Current — мгновенная фаза; Level — RMS. */
float Numb,Zer,Current,Level;
/*
* aCurrent хранит первую измеренную фазу до обработки второго канала пары.
* Поэтому каналы каждой пары должны вызываться последовательно: чётный, затем нечётный.
*/
static float aCurrent,Amplitude;
int sens, pair, ist, thrd, fazz, feed;
/* Индексы глобального датчика, пары, группы и восстановленной третьей фазы. */
int ignor, sens, pair, ist, thrd, fazz, feed;
ERROR error;
/* В начале нового четырёхканального цикла фиксируется сводка прошлого цикла. */
if(Desk == dsk_LOAD)
if(!chan)
{
@@ -456,13 +799,25 @@ void Current_count(int chan)
chk.all = 0;
}
/* Переход от относительного номера силового входа к общей таблице датчиков. */
sens = pow_addr + chan;
/* Парный канал нужен для сравнения фазных RMS-значений. */
pair = sens_pair[chan];
/* Чётный канал — первая измеренная фаза пары, нечётный — вторая. */
ist = !(chan & 1);
/* Номер двухканальной измерительной группы: 0 для каналов 0/1, 1 для 2/3. */
feed = (chan >>1);
/* Начальная позиция трёх фаз этой группы в диагностической области Modbus. */
fazz = feed*3;
/* Индекс накопителя RMS восстановленной третьей фазы: 4 либо 5. */
thrd = feed+4;
/* Bypass полностью исключает канал из измерения, сохраняя сам флаг Bypass. */
if(sens_error[sens].bit.Bypas)
{
sens_error[sens].all = 0;
@@ -471,13 +826,23 @@ void Current_count(int chan)
return;
}
/* Сырой код специализированного АЦП/датчика LEM. */
Numb = adc_table_lem[chan];
/* Сохранение сырого кода в отдельной диагностической области. */
modbus[0x64+chan] = Numb;
// Средний (он же нулевой) уровень показаний АЦП
// Средний (он же нулевой) уровень показаний АЦП.
/*
* Первый IIR-слой медленно ведёт оценку к текущему коду. При вызове
* ADC_FREQ раз в секунду коэффициент соответствует примерно 5-секундной базе.
*/
zer_count[chan] += (Numb-zer_count[chan])/(5.0 * ADC_FREQ);
/* Второй фильтр подавляет остаточную пульсацию оценки нулевого уровня. */
Zer = (int)(filterbat(&zer_filter[chan],zer_count[chan]));
/* Сохранённый рабочий ноль обновляется только в разрешённой части WAKE-цикла. */
if(WAKE < (WAKE_TIME /2)) Zero_lev[chan] = Zer;
/*
Test_mem_limit(12);
@@ -485,40 +850,53 @@ Log_to_mem((int)adc_table_lem[chan]);
Log_to_mem((int)zer_count[chan]);
Log_to_mem((int)Zero_lev[chan]);
*/
// Мгновенное значение фазы
// Мгновенное значение фазы.
/* Удаление смещения АЦП и масштабирование в физическую величину. */
Current = (Numb - Zero_lev[chan]) * powK[chan];
// Действующее (среднеквадратичное) значение фазы
// Действующее (среднеквадратичное) значение фазы.
/* Низкочастотная фильтрация квадрата мгновенного значения. */
lev_quadr[chan] += ((Current*Current)-lev_quadr[chan])/(1.0 * ADC_FREQ);
/* Корень из среднего квадрата даёт RMS текущей измеренной фазы. */
lev_count[chan] = sqrt(lev_quadr[chan]);
// Запомним
// Первая фаза пары запоминается до прихода второй.
if(ist)
{
/* В COMM полярность первой фазы аппаратно/логически развёрнута. */
if(Desk==dsk_COMM) aCurrent =-Current;
else aCurrent = Current; // Запомнили мгновенное значение - дла амплитуды
else aCurrent = Current; // Мгновенное значение первой фазы для расчёта вектора.
}
else
{
// Вычисление амплитуды
// Вычисление амплитуды по двум измеренным фазам.
/* im_calc() строит модуль трёхфазного вектора, затем результат фильтруется. */
Amplitude = filterbat(&out_filter[sens], im_calc(Current,aCurrent));
/* Небольшие значения подавляются как шум; порог зависит от типа канала. */
if(sens_type[sens]==VOLTAGE)
if(Amplitude<20) Amplitude = 0;
if(sens_type[sens]==CURRENT)
if(Amplitude<75) Amplitude = 0;
/* Для синусоиды действующее значение равно амплитуде, делённой на sqrt(2). */
Level = Amplitude / RADIX2;
/* Во время WAKE вычисления ведутся, но рабочие выходные данные не обновляются. */
if(!WAKE)
{
/* Нечётный элемент пары хранит амплитуду, предыдущий — RMS. */
sens_data[sens ] = Amplitude;
sens_data[sens-1] = Level;
}
// Действующее (среднеквадратичное) значение третьей фазы
// Действующее (среднеквадратичное) значение третьей фазы.
/* Для симметричной трёхпроводной системы сумма мгновенных фаз равна нулю. */
Numb =-Current-aCurrent;
/* Та же RMS-фильтрация применяется к восстановленной фазе. */
lev_quadr[thrd] += (Numb*Numb-lev_quadr[thrd])/(1.0 * ADC_FREQ);
lev_count[thrd] = sqrt(lev_quadr[thrd]);
/*
@@ -538,43 +916,60 @@ Log_to_mem(Amplitude);
Log_to_mem(Level);
}
*/
/* Публикация трёх RMS-фаз группы в последовательных Modbus-регистрах. */
modbus[0x68+fazz ] = lev_count[pair];
modbus[0x68+fazz+1] = lev_count[chan];
modbus[0x68+fazz+2] = lev_count[thrd];
}
// Это максимальное
// Выбор максимального RMS из трёх фаз — опорного уровня для оценки перекоса.
Numb = lev_count[chan];
if(Numb<lev_count[pair]) Numb = lev_count[pair];
if(Numb<lev_count[thrd]) Numb = lev_count[thrd];
// Зашиты!
// Формирование защит.
error.all = 0;
if(WAKE || sens_error[chan].bit.Ignor) goto fin;
/* В переходном состоянии WAKE новые защиты не формируются. */
if(WAKE) goto fin;
/* Ignore оставляет диагностику видимой, но ниже может запретить Stop. */
ignor = sens_error[sens].bit.Ignor;
/*
* Если измеренная фаза отстаёт от максимальной более чем на 30 % и общий
* уровень выше Curr_Edge, условие должно сохраняться time_3sec циклов.
*/
if(er_anal( ((Numb-lev_count[chan])/Numb > 0.3) && (Numb>Curr_Edge),
&err_count[chan],time_3sec,0))
{
error.bit.Wry = 1;
error.bit.Stop = 1;
if(!ignor) error.bit.Stop = 1;
}
/* Аналогичный контроль для вычисленной третьей фазы. */
if(er_anal( ((Numb-lev_count[thrd])/Numb > 0.3) && (Numb>Curr_Edge),
&err_count[thrd],time_3sec,0))
{
error.bit.Wry = 1;
error.bit.Stop = 1;
if(!ignor) error.bit.Stop = 1;
}
/* Полное игнорирование канала прекращает дальнейший контроль уровней. */
if(ignor) goto fin;
/* Общий уровень пары проверяется один раз — при обработке её второго канала. */
if(Desk == dsk_LOAD)
if(!ist)
{
/* Верхняя уставка всегда считается аварийной и формирует Stop. */
if(Level > sens_hi_edge[sens])
{
error.bit.Hyper = 1;
error.bit.Stop = 1;
}
/* Для напряжения пониженный уровень подтверждается трёхсекундной выдержкой. */
if(sens_type[sens]==VOLTAGE)
if(er_anal( (Level < sens_lo_edge[sens]),
@@ -584,6 +979,7 @@ modbus[0x68+fazz+2] = lev_count[thrd];
error.bit.Stop = 1;
}
/* Для тока та же sens_lo_edge используется как порог предупреждения Over. */
if(sens_type[sens]==CURRENT)
if(Level > sens_lo_edge[sens])
{
@@ -610,24 +1006,49 @@ Log_to_mem(WAKE);
}
*/
fin:
/* Объединение вычисленных ошибок со служебными флагами выбранного канала. */
reset_errs(sens,error);
}
/**
* @brief Обрабатывает один температурный канал.
*
* Поддерживаются локальные значения АЦП, внешние температуры типа `TRM_OBEH`,
* режим выдачи сырого кода, имитация по исправным однотипным датчикам и режим
* калибровки. После пересчёта температура ограничивается диапазоном от -20 до
* 200 градусов, записывается в `sens_data[]` с масштабом 0,1 градуса и
* проверяется на обрыв, перегрев и предупредительный уровень.
*
* @param chan Индекс температурного канала в общих массивах датчиков.
* @param own Ненулевое значение разрешает получение данных внешнего канала
* `TRM_OBEH` из массивов `ext_temp[]` и `ext_diag[]`.
*
* @pre Индекс `chan` допустим для выбранной конфигурации каналов.
*/
void Temper_count(int chan, int own)
{
/* Рабочее значение: сначала код/внешние данные, затем температура в градусах. */
float Numb;
/* Целая температура используется при сравнении с уставками диагностики. */
static int Temper;
/* j — число доноров имитации; kun — индекс калибровочного канала. */
int i,j, kun;
long s;
ERROR error;
/* Индекс внешнего температурного канала относительно начала блока OWEN. */
int ovn;
/* Первый температурный канал открывает новый цикл накопления общих флагов. */
if(!chan)
{
sig.all = chk.all;
chk.all = 0;
}
/* Bypass применяется здесь только к локальным температурным каналам. */
if(sens_error[chan].bit.Bypas)
if(chan<tpl_cans)
{
@@ -637,14 +1058,20 @@ void Temper_count(int chan, int own)
return;
}
/* По умолчанию источником служит локальная таблица результатов АЦП. */
Numb = ADC_table[chan];
/* Сервисный режим публикует сырой код без пересчёта и диагностики. */
if(cRawMeat)
{
sens_data[chan] = Numb;
return;
}
/*
* Имитация заменяет локальный датчик средним кодом исправных датчиков
* того же типа. Каналы с флагами из okay исключаются из набора доноров.
*/
if(sens_error[chan].bit.Imit)
if(chan<tpl_cans)
{
@@ -659,27 +1086,37 @@ void Temper_count(int chan, int own)
j++;
} }
/* При наличии доноров вычисляется среднее; без доноров s остаётся равным нулю. */
if(j) s/=j;
Numb = s;
}
/* Неотрицательный kun означает, что текущий канал расположен в зоне калибровки. */
kun = chan - cal_addr;
/* При own!=0 внешний TRM_OBEH подменяет локальное значение АЦП. */
if(own)
if(sens_type[chan]==TRM_OBEH)
{
ovn = chan-tpl_cans;
/* Внешняя температура приходит в десятых долях градуса. */
Numb= 0.1 * ext_temp[ovn];
/* Любая внешняя диагностика преобразуется в заведомо аварийное значение. */
if(ext_diag[ovn])
Numb = -100;
}
/* Локальный аналоговый вариант температурного тракта. */
if(!own)
if(TermoAD)
{
/* В режиме калибровки сырой код записывается в соответствующую точку. */
if(cTermoCal|bTermoCal)
if(!WAKE)
{
/* В шкафном исполнении уровень кода выбирает старшую пару калибровок. */
if(Desk==dsk_SHKF)
if(Numb>2200) kun|=2;
@@ -690,21 +1127,25 @@ void Temper_count(int chan, int own)
return;
} }
/* Калибровочные/служебные позиции за пределами tpl_cans здесь не измеряются. */
if(chan>=tpl_cans) return;
// Kun именно чтоб скомпенсировать нечетность
// Нечётность kun выбирает одну из двух независимых аналоговых ветвей.
if(kun&1) Numb = (Numb-K300_2)*K_T2+ZERO;
else Numb = (Numb-K300_1)*K_T1+ZERO;
}
/* Резистивный вариант температурного тракта. */
if(TermoRS)
{
/* Каналы начиная с cal_addr используются как калибровочные, а не измерительные. */
if(kun>=0)
{
/* Во время WAKE вторичный фильтр синхронизируется с первичным. */
if(WAKE) out_filter[chan] = adc_filter[chan];
else
{
// Калибровку отфильтруем вторым слоем
// Калибровочный код дополнительно фильтруется вторым слоем.
Numb = filterbat(&out_filter[chan],Numb);
TCaliber[kun] = Numb;
@@ -715,29 +1156,39 @@ void Temper_count(int chan, int own)
if(Desk==dsk_BKSD)
{
/* Для диагностики BKSD сырой код дополнительно публикуется со смещением +8. */
sens_data[chan+8] = ADC_table[chan];
/* Первые шесть и последующие каналы используют разные масштабы. */
if(chan<6) Numb = (Numb-K100_D)*K100;
else Numb = (Numb-K100_D)*K_50;
}
else
{
/* В остальных исполнениях чётные и нечётные каналы имеют свои калибровки. */
if(chan&1) Numb = (Numb-K100_2)*K_T2;
else Numb = (Numb-K100_1)*K_T1;
} }
/* Ограничение диапазона одновременно защищает формат данных и задаёт аварийные границы. */
if(Numb < -20) Numb = -20;
if(Numb > 200) Numb = 200;
/* Сервисная команда подменяет результат фиксированной температурой 40 °C. */
if(bSecretBt|cSecretBt) Numb = 40.0;
/* В таблице температура хранится в десятых долях градуса. */
sens_data[chan] = Numb*10;
/* Для пороговых сравнений дробная часть отбрасывается. */
Temper = Numb;
error.all = 0;
/* Во время запуска или при Ignore показание сохраняется, но диагностика не обновляется. */
if(WAKE || sens_error[chan].bit.Ignor) goto fin;
// Обрыв или неисправность
// Обрыв или неисправность: результат упёрся в одну из границ допустимого диапазона.
if(Temper<=-20 || Temper>=200)
{
error.bit.Tear = 1;
@@ -745,7 +1196,11 @@ sens_data[chan+8] = ADC_table[chan];
}
else
// Перегрев
// Перегрев.
/*
* Если Hyper уже был активен, он удерживается до снижения температуры
* ниже верхней уставки на величину Cooling — это температурный гистерезис.
*/
if(((Temper>sens_hi_edge[chan]-Cooling) && (sens_error[chan].bit.Hyper)) ||
(Temper>sens_hi_edge[chan]) )
{
@@ -755,7 +1210,7 @@ sens_data[chan+8] = ADC_table[chan];
}
else
// Предупреждение по температуре
// Предупреждение по температуре проверяется только при отсутствии обрыва и перегрева.
if(Temper>sens_lo_edge[chan])
{
error.bit.Over = 1;
@@ -763,21 +1218,39 @@ sens_data[chan+8] = ADC_table[chan];
}
fin:
/* Запись локальной диагностики и обновление общего признака Stop. */
reset_errs(chan,error);
}
/**
* @brief Обрабатывает канал контроля питающего напряжения.
*
* Код АЦП масштабируется коэффициентом, выбранным по типу датчика. Функция
* фильтрует диагностические дискретные входы, контролирует пониженное напряжение
* и формирует общий признак аварии. Останов по низкому напряжению формируется,
* когда аналогичный признак присутствует и у парного канала.
*
* @param chan Индекс канала питания в таблицах `ADC_table[]`, `sens_type[]`,
* `sens_pair[]` и `sens_error[]`.
*
* @note Проверка повышенного напряжения в исходном коде отключена блочным
* комментарием и настоящей документацией не активируется.
*/
void Power_count(int chan)
{
/* Numb — сырой код АЦП; Power — пересчитанное напряжение; bitt — позиция диагностики. */
float Numb;
int Power,bitt;
ERROR error;
/* Нулевой канал начинает новый цикл контроля питающих каналов. */
if(!chan)
{
sig.all = chk.all;
chk.all = 0;
}
/* Bypass исключает канал, обнуляя его показание и оставляя флаг Bypass. */
if(sens_error[chan].bit.Bypas)
{
sens_error[chan].all = 0;
@@ -786,56 +1259,77 @@ void Power_count(int chan)
return;
}
/* Получение последнего кода общего АЦП. */
Numb = ADC_table[chan];
/* В сервисном режиме наружу передаётся сырой код без проверок. */
if(cRawMeat)
{
sens_data[chan] = Numb;
return;
}
/* Тип канала используется как индекс таблицы коэффициентов powK[]. */
Power = Numb * powK[sens_type[chan]];
/* Рабочая таблица хранит уже пересчитанное физическое значение. */
sens_data[chan] = Power;
error.all = 0;
/* При запуске и Ignore аналоговое значение обновляется, а защиты пропускаются. */
if(WAKE || sens_error[chan].bit.Ignor) goto fin;
// Дискретные каналы
// Дискретные каналы.
/* Обычно каждому аналоговому каналу соответствуют два последовательных бита DigErr. */
bitt = chan*2;
if(chan!=7)
{
/* Каждый вход подтверждается интегрирующей выдержкой 1000 вызовов. */
error.bit.Discr1 = er_anal(((DigErr.all>>bitt)&1), &din_count[bitt], 1000, 0); bitt++;
error.bit.Discr2 = er_anal(((DigErr.all>>bitt)&1), &din_count[bitt], 1000, 0); bitt++;
}
/* У шестого канала предусмотрены ещё два дополнительных дискретных признака. */
if(chan==6)
{
error.bit.Discr3 = er_anal(((DigErr.all>>bitt)&1), &din_count[bitt], 1000, 0); bitt++;
error.bit.Discr4 = er_anal(((DigErr.all>>bitt)&1), &din_count[bitt], 1000, 0); bitt++;
}
/* Contr — сводный признак наличия хотя бы одной дискретной неисправности. */
error.bit.Contr = error.bit.Discr1 | error.bit.Discr2 | error.bit.Discr3 | error.bit.Discr4;
// Пониженное напражение
// Пониженное напряжение.
if(Power <sens_lo_edge[chan])
{
error.bit.Out = 1;
/* Останов формируется только при одновременном снижении в парном канале. */
if(sens_error[sens_pair[chan]].bit.Out)
{
error.bit.Stop = 1;
} }
// Повышенное напражение
// Повышенное напряжение.
/*
* Эта защита присутствует в исходнике, но намеренно отключена блочным
* комментарием; исполняемый код её не учитывает.
*/
/* if(Power > sens_hi_edge[chan])
{
error.bit.Hyper = 1;
error.bit.Stop = 1;
}
*/
/* Любая локальная неисправность питания поднимает общую ламповую аварию. */
if(error.all)
chk.bit.Alarm = 1;
fin:
/* Сохранение вычисленных ошибок и обновление общего Stop. */
reset_errs(chan,error);
}
/** @} */

View File

@@ -1,3 +1,12 @@
/**
* @file message.c
* @brief Параметры устройства и прикладные сообщения протокола Modbus.
*
* Инициализирует значения по умолчанию, загружает и сохраняет параметры в
* EEPROM, принимает команды функций 3/6 и формирует ответы функции 4.
* Индексы param/Modbus согласованы с внешней картой регистров; менять порядок,
* ANSWER_LEN или представление слов можно только вместе с ведущей системой.
*/
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
#include "package.h"
#include "RS485.h"
@@ -18,9 +27,17 @@
#include "caliber.h"
/** @brief Рабочий образ регистров Modbus, передаваемый внешнему мастеру. */
int modbus[ANSWER_LEN+1];
/** @brief Сохраняемые параметры, индексно согласованные с картой регистров. */
unsigned int param[ANSWER_LEN+1];
/**
* @brief Создаёт полный набор безопасных параметров для текущего варианта платы.
* @post Modbus/param, режимы, пороги, яркость и калибровки имеют штатные значения.
* @details Начальная очистка предотвращает сохранение мусора в зарезервированных
* регистрах; далее значения выбираются по Desk и compile-time конфигурации.
*/
void Default_params()
{
unsigned int i,def;
@@ -46,19 +63,19 @@ void Default_params()
if(Desk == dsk_EPLT)
{
Cancount[m_FAST] = 2; // пауза между посылками CAN
Cancount[m_FAST] = 2; // пауза между посылками CAN
for(i=0;i<6;i++) Bright[i] = bright[i];
}
else
{
Cancount[m_FAST] = 10; // пауза между посылками CAN
Cancount[m_FAST] = 10; // пауза между посылками CAN
Brightness = 8;
}
Cancount[m_SLOW] = 101; // пауза между посылками CAN
Cancount[m_SLOW] = 101; // пауза между посылками CAN
if(Owen) Owncount = 20; // пауза между опросами OWEN
if(Owen) Owncount = 20; // пауза между опросами OWEN
if(Kalibro)
{
@@ -122,9 +139,9 @@ void Default_params()
DAC_20 = Caliber[6];
DAC_04 = Caliber[7];
/* Не врема!
DAC_go = 6; // начало зарада
DAC_stop= 15; // конец зарада
/* Не врема!
DAC_go = 6; // начало зарада
DAC_stop= 15; // конец зарада
*/
}
@@ -135,6 +152,11 @@ void Default_params()
Init_sensors_more();
}
/**
* @brief Загружает параметрический блок из EEPROM и проверяет его целостность.
* @details При несовпадении сигнатуры/CRC вызывается Default_params(); после
* успешной загрузки значения переносятся в рабочие глобальные настройки.
*/
void Load_params()
{
unsigned int i,crc;
@@ -159,6 +181,11 @@ void Load_params()
Caliber_time = 0xFFFF;
} } }
/**
* @brief Формирует параметрический блок с CRC и записывает его в EEPROM.
* @warning Запись следует запускать только при свободном драйвере EEPROM;
* питание должно сохраняться до физического завершения транзакции.
*/
void Save_params()
{
unsigned int i,dif=0;
@@ -177,24 +204,30 @@ void Save_params()
/***************************************************************/
/***************************************************************/
/* Передача данных по протоколу ModBus - команда 3
Чтение ачеек данных */
/* Передача данных по протоколу ModBus - команда 3
Чтение ачеек данных */
/***************************************************************/
/***************************************************************/
/**
* @brief Обрабатывает запрос Modbus чтения диапазона регистров.
* @param rs_arr Порт с проверенным входным кадром и буфером ответа.
* @details Проверяет начальный адрес и длину, копирует слова из modbus и
* формирует CRC непосредственно перед отправкой.
*/
void ReceiveCommandModbus3(RS_DATA *rs_arr)
{
unsigned int crc, Address_MB, Length_MB, i;
// получили начальный адрес чтениа
// получили начальный адрес чтениа
Address_MB =/*(rs_arr->RS_Header[2] << 8) |*/ rs_arr->RS_Header[3];
// получили количество слов данных
// получили количество слов данных
Length_MB = (rs_arr->RS_Header[4] << 8) | rs_arr->RS_Header[5];
/////////////////////////////////////////////////
// Отсылка
/* Посчитали контрольную сумму перед самой посылкой */
// Отсылка
/* Посчитали контрольную сумму перед самой посылкой */
rs_arr->buffer[0] = CNTRL_ADDR;
rs_arr->buffer[1] = CMD_MODBUS_3;
@@ -223,22 +256,27 @@ void ReceiveCommandModbus3(RS_DATA *rs_arr)
return;
}
/**
* @brief Применяет команду записи одного параметра Modbus.
* @details После проверки адреса обновляет разрешённое значение, выставляет
* события перенастройки/сохранения и возвращает эхо принятой команды.
*/
void ReceiveCommandModbus6(RS_DATA *rs_arr)
{
unsigned int Address_MB, Data_MB, i;
/////////////////////////////////////////////////
// Отсылка
/* Отправлаем назад то же самое */
// Отсылка
/* Отправлаем назад то же самое */
for (i=0;i<8;i++)
rs_arr->buffer[i] = rs_arr->RS_Header[i];
// получили начальный адрес записи
// получили начальный адрес записи
Address_MB = (/*(rs_arr->RS_Header[2] << 8) | */rs_arr->RS_Header[3]);
// получили слово данных
// получили слово данных
Data_MB = (rs_arr->RS_Header[4] << 8) | rs_arr->RS_Header[5];
Modbus[Address_MB].all = Data_MB;
@@ -247,12 +285,17 @@ void ReceiveCommandModbus6(RS_DATA *rs_arr)
RS_Send(rs_arr, rs_arr->buffer, 10);
}
/**
* @brief Формирует периодический запрос/ответ функции 4 для внешнего устройства.
* @details Адрес, полезная нагрузка и CRC помещаются в буфер выбранного RS-порта,
* после чего передача запускается неблокирующим RS_Send().
*/
void SendCommandModbus4(RS_DATA *rs_arr)
{
unsigned int crc;
/////////////////////////////////////////////////
// Отсылка
// Отсылка
rs_arr->buffer[0] = 16;
rs_arr->buffer[1] = 4;
@@ -275,12 +318,17 @@ void SendCommandModbus4(RS_DATA *rs_arr)
RS_Send(rs_arr, rs_arr->buffer, 8);
}
/**
* @brief Разбирает ответ внешнего измерителя на функцию Modbus 4.
* @details Проверяет формат и переносит температуры/диагностики в ext_temp и
* ext_diag; некорректный ответ не должен заменять последний пригодный снимок.
*/
void ReceiveAnswerModbus4(RS_DATA *rs_arr)
{
unsigned int i;
/////////////////////////////////////////////////
// Отсылка
// Отсылка
for (i=0;i<8;i++)
{

View File

@@ -1,3 +1,12 @@
/**
* @file peripher.c
* @brief Определение типа платы и управление дискретной периферией.
*
* Читает конфигурационные входы, выбирает режим изделия, коммутирует каналы
* термопар и опрашивает кнопки. Временное переназначение LED-линий сохраняет
* исходные MUX/DIR и должно всегда завершаться unsetup_leds_line(). Таблицы GPIO
* зависят от варианта платы и описаны в GPIO_table.h.
*/
#include "DSP2833x_Device.h" // DSP281x Headerfile Include File
#include "filter_bat2.h"
#include "measure.h"
@@ -10,12 +19,19 @@
#include "peripher.h"
#include "GPIO_table.h"
/** @brief Распознанный адрес/режим и признаки установленной периферии платы. */
int Mode,Desk,TermoAD=0,TermoRS=0,TermoSW=0,Kurrent=0,Kalibro=0,Owen=0;
/** @brief Интегрирующие счётчики фильтрации дискретных входов. */
unsigned int INcount0=0,INcount1=0;
/** @brief Снимок диагностических битов дискретной периферии. */
LONGE DigErr;
int MUX_GPIO32,MUX_GPIO48,DIR_GPIO32,DIR_GPIO48;
/**
* @brief Временно переводит GPIO32/GPIO48 в режим обычных цифровых выходов.
* @post Предыдущие MUX/DIR сохранены для unsetup_leds_line().
*/
void setup_leds_line()
{
MUX_GPIO32 = GpioCtrlRegs.GPBMUX1.bit.GPIO32;
@@ -31,6 +47,7 @@ void setup_leds_line()
EDIS;
}
/** @brief Восстанавливает MUX/DIR LED-линий, сохранённые setup_leds_line(). */
void unsetup_leds_line()
{
EALLOW;
@@ -41,14 +58,21 @@ void unsetup_leds_line()
EDIS;
}
/**
* @brief Считывает код исполнения платы и формирует её программную конфигурацию.
* @details По комбинации GPIO задаёт Mode/Desk, наличие температурных трактов,
* токовых входов, калибровки и внешнего OWEN. Затем применяет соответствующие
* таблицы направления, MUX и начальных уровней GPIO_table.h.
* @post Все inline-примитивы peripher.h можно безопасно использовать.
*/
void get_Mode()
{
EALLOW;
GpioCtrlRegs.GPAMUX1.all &= 0xFF000000; // 00—11
GpioCtrlRegs.GPAMUX2.all &= 0xFF00003F; // 19—27
GpioCtrlRegs.GPBMUX1.all &= 0xFFFFFFC0; // 32—34
GpioCtrlRegs.GPBMUX2.all &= 0x000FF000; // 48—53, 58—63
GpioCtrlRegs.GPAMUX1.all &= 0xFF000000; // 00—11
GpioCtrlRegs.GPAMUX2.all &= 0xFF00003F; // 19—27
GpioCtrlRegs.GPBMUX1.all &= 0xFFFFFFC0; // 32—34
GpioCtrlRegs.GPBMUX2.all &= 0x000FF000; // 48—53, 58—63
GpioCtrlRegs.GPADIR.bit.GPIO20 = 0;
GpioCtrlRegs.GPADIR.bit.GPIO21 = 0;
@@ -117,6 +141,10 @@ void get_Mode()
EDIS;
}
/**
* @brief Переводит адресные линии мультиплексора термопар в невыбранное состояние.
* @note Используется между каналами, чтобы снизить влияние переходных процессов.
*/
void select_tpl_255(void)
{
if(Desk==dsk_BKSD)
@@ -144,6 +172,11 @@ void select_tpl_255(void)
GpioDataRegs.GPBSET.all = BKST_B_tpl_zero;
} }
/**
* @brief Выбирает один физический канал мультиплексора термопар.
* @param n_tpl Номер канала в логической нумерации измерительного модуля.
* @details Номер раскладывается на адресные GPIO с учётом разводки текущего Desk.
*/
void select_tpl_canal(int n_tpl)
{
unsigned long GPIO_A_tpl_set = 0;
@@ -187,6 +220,11 @@ void select_tpl_canal(int n_tpl)
GpioDataRegs.GPBCLEAR.all = GPIO_B_tpl_set;
} }
/**
* @brief Опрашивает кнопки и дискретные входы с программным антидребезгом.
* @details Счётчики INcount* требуют устойчивого уровня несколько циклов;
* подтверждённые состояния переносятся в команды и диагностические биты.
*/
void get_Buttons()
{
unsigned long butt =0;
@@ -208,7 +246,7 @@ void get_Buttons()
Inputs.all = butt;
}
/* Ничего такого в действительности нет
/* Ничего такого в действительности нет
if(Desk==dsk_BKST)
{ bSecretBt = !GpioDataRegs.GPBDAT.bit.GPIO63;
InputRep1 = bSecretBt;
@@ -244,7 +282,7 @@ void get_Buttons()
if(GpioDataRegs.GPADAT.bit.GPIO5 ) butt +=0x0800;
if(GpioDataRegs.GPBDAT.bit.GPIO48) butt +=0x1000;
if(GpioDataRegs.GPADAT.bit.GPIO10) butt +=0x2000; // Секретный вход
if(GpioDataRegs.GPADAT.bit.GPIO10) butt +=0x2000; // Секретный вход
Inputs.all = butt;
}

View File

@@ -1,3 +1,12 @@
/**
* @file pulto.c
* @brief Периодическое обслуживание пульта индикации.
*
* Обработчик Timer1 обновляет яркость, маску ламп, числовые каналы и признак
* связи с ведущим устройством. Работа выполняется в прерывании, поэтому обмен
* с общими переменными должен оставаться коротким; частоты мигания вычисляются
* из READY_FREQ и изменятся при смене базовой частоты таймера.
*/
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
#include "DSP2833x_SWPrioritizedIsrLevels.h"
@@ -10,6 +19,7 @@
#include "kanal.h"
#include "peripher.h"
/** @brief Маска мигающих сегментов и тестовое число режима самопроверки. */
unsigned long isMask;
int isNumb;
@@ -17,6 +27,11 @@ int quaLamp = 6;
unsigned long Lonely=0;
/**
* @brief Выполняет шаг визуального теста всех цифр и сегментов.
* @details Через BLINK_TIME изменяет цифру 0..9 и попеременно включает либо
* гасит полную маску, позволяя заметить неисправный сегмент индикатора.
*/
void what_is()
{
static unsigned int count_blink;
@@ -33,6 +48,12 @@ void what_is()
else isMask = 0xFFFFFFFF;
} }
/**
* @brief Периодический ISR управления панелью EPLT.
* @details Поддерживает программный PWM яркости, отображает актуальные каналы,
* управляет лампами и отслеживает тайм-аут связи через Lonely. Перед выходом
* восстанавливает PIEIER и подтверждает источник Timer1.
*/
interrupt void cpu_timer1_isr_PULT(void)
{
static int cownt=0,count_bright;
@@ -53,10 +74,11 @@ interrupt void cpu_timer1_isr_PULT(void)
CanGO = 1;
}
toggle_ONLINE(); // ñòðîá äëà ÏËÈÑ
toggle_RES_OUT_1(); // òèïà ýòî ãîòîâíîñòü
toggle_RES_OUT_2(); // íà âñàêèé
toggle_ONLINE(); // строб дла ПЛИС
toggle_RES_OUT_1(); // типа это готовность
toggle_RES_OUT_2(); // на всакий
/* Насыщение предотвращает переполнение счётчика после длительной потери связи. */
if(++Lonely > Alone) Lonely = Alone;
if(++count_bright >= maximum_bright) count_bright = 0 ;
@@ -66,6 +88,7 @@ interrupt void cpu_timer1_isr_PULT(void)
if(count_bright < Bright[i]) light+=(1<<i);
}
/* За один ISR обновляется одна из трёх групп индикаторов. */
if(++cownt>2) cownt=0;
if(cTestLamp|bTestLamp)

View File

@@ -1,3 +1,12 @@
/**
* @file spise2p.c
* @brief Неблокирующий драйвер последовательной EEPROM поверх SPI.
*
* Высокоуровневые Seeprom_read/Seeprom_write запускают транзакцию, а конечный
* автомат SPISE2P_DRV_tick продвигается от Timer2. Состояние драйвера единично:
* новую операцию разрешено начинать только после spiSe2pFree(). Адрес и длина
* должны укладываться в SEEPROM_LEN и не пересекать ограничения микросхемы.
*/
/*=================================================================
File name : SPISE2PD.C
@@ -32,6 +41,14 @@ int ccc=0;
/******* SPI bus Serial EEPROM driver Initialization routine ********/
/********************************************************************/
/**
* @brief Синхронно записывает пользовательский блок через асинхронный драйвер.
* @param adres Начальный адрес EEPROM.
* @param buf Источник данных в памяти DSP.
* @param size Размер блока в байтах.
* @details Ожидает освобождения драйвера, запускает транзакцию и блокируется до
* её завершения, которое продвигается прерываниями Timer2.
*/
void Seeprom_write( unsigned int adres,
unsigned int buf[],
unsigned int size)
@@ -63,6 +80,12 @@ void Seeprom_write( unsigned int adres,
CpuTimer2Regs.TCR.all = 0x4010; // Use write-only instruction to set TSS bit = 1
}
/**
* @brief Синхронно читает блок EEPROM в пользовательский буфер.
* @param adres Начальный адрес EEPROM.
* @param buf Приёмный буфер DSP.
* @param size Размер в байтах.
*/
void Seeprom_read( unsigned int adres,
unsigned int buf[],
unsigned int size)
@@ -94,6 +117,10 @@ void Seeprom_read( unsigned int adres,
CpuTimer2Regs.TCR.all = 0x4010; // Use write-only instruction to set TSS bit = 1
}
/**
* @brief Инициализирует SPI, объект драйвера и периодический Timer2.
* @post se2p находится в свободном состоянии, Timer2 вызывает cpu_timer2_isr().
*/
void InitSeeprom()
{
se2p.init(&se2p);
@@ -108,6 +135,7 @@ void InitSeeprom()
IER |= M_INT14;
}
/** @brief Сбрасывает конечный автомат и программирует регистры SPI-A. */
void SPISE2P_DRV_init(SPISE2P_DRV *eeprom)
{
/* Configure SPI-A pins using GPIO regs*/
@@ -141,28 +169,33 @@ void SPISE2P_DRV_init(SPISE2P_DRV *eeprom)
SpiaRegs.SPICCR.bit.SPISWRESET=1; // Enable SCI
}
/** @brief Деактивирует chip-select EEPROM. */
void SPISE2P_DRV_csset()
{
GpioDataRegs.GPADAT.bit.GPIO19 = 1;
}
/** @brief Активирует chip-select EEPROM перед транзакцией. */
void SPISE2P_DRV_csclr()
{
GpioDataRegs.GPADAT.bit.GPIO19 = 0;
}
/** @brief Возвращает ненулевое значение, если конечный автомат не занят. */
unsigned int spiSe2pFree(SPISE2P_DRV *se2p)
{
if(se2p->csr&0x3) return(0);
else return(1);
}
/** @brief Привязывает описание блока и переводит автомат в начало записи. */
void spiSe2pWrite(SPISE2P_DRV *se2p, SE2P_DATA *msgPtr)
{
se2p->msgPtr=msgPtr;
se2p->csr|=0x1;
}
/** @brief Привязывает описание блока и переводит автомат в начало чтения. */
void spiSe2pRead(SPISE2P_DRV *se2p, SE2P_DATA *msgPtr)
{
se2p->msgPtr=msgPtr;
@@ -173,6 +206,10 @@ void spiSe2pRead(SPISE2P_DRV *se2p, SE2P_DATA *msgPtr)
/******* SPI bus Serial EEPROM driver Tick function *****************/
/********************************************************************/
/**
* @brief ISR Timer2, задающий такт конечному автомату EEPROM.
* @note Один вызов выполняет только ограниченный шаг SPI-транзакции.
*/
interrupt void cpu_timer2_isr(void)
{ EALLOW;
CpuTimer2.InterruptCount++;
@@ -183,6 +220,12 @@ interrupt void cpu_timer2_isr(void)
EDIS;
}
/**
* @brief Выполняет один переход конечного автомата SPI EEPROM.
* @details Состояния последовательно выдают opcode, адрес и данные, ожидают
* флаги SPI, управляют CS и проверяют готовность микросхемы после записи.
* @param eeprom Активный экземпляр драйвера с описанием текущего блока.
*/
void SPISE2P_DRV_tick(SPISE2P_DRV *eeprom)
{
static unsigned int step=0;

Some files were not shown because too many files have changed in this diff Show More