Files
RadioPhotonic-generator-docs/анализ кода for_stm32_cubeide.md
2025-09-18 17:11:38 +03:00

277 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# отследим формирование ответа с отправки
Общий принцип такой:
- MESS_01 (default state)
- отправляем массив State_Data обычнфм блокирующим способом
- MESS_02 (Transmith packet)
- Формируем ответ в массиве Long_Data, отправляем (**что?**) через USART_TX_DMA
- MESS_03://Transmith saved packet
- обновляем данные в UART_DATA,
- запускаeм DMA отправку (**чего?**)
# отследим путь информации с ADC
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc3.Init.DMAContinuousRequests = DISABLE;
### static uint16_t Get_ADC(uint8_t num):
- num == 0 => запуск ADC1
- num == 1 => возвращает данные ADC1
- num == 2 => stop ADC1
- num == 3 => запуск ADC3
- num == 4 => возвращает данные ADC3
- num == 5 => stop ADC3
## USART_TX(State_Data,2) (main.c:414)
Срабатывает, если UART_transmission_request == Default state (MESS_1)
# при получении 0x7777
1. Decode _uart (main.c:1506)
2. Command копируется в temp2
3. Устанавливаются параметры Curr_setup
1. WORK_EN
2. U5V1_EN
3. U5V2_EN
4. LD1_EN
5. REF1_EN
6. REF2_EN
7. TEC1_EN
8. TEC2_EN
9. TS1_EN
10. TS2_EN
11. SD_EN
12. PI1_RD
13. PI2_RD
4. Парсится заголовок:
5. Eсли 0x1111:
6. Если 0x7777 -- Устанавливаются параметры task:
1. task_type
2. min_param
3. current_param
4. max_param
5. delta_param
6. dt
7. tau
8. sec_param
9. curr
10. temp
11. p_coef_1
12. i_coef_1
13. p_coef_2
14. i_coef_2
Температурные коэффициенты записываются в соотвестсвующие поля LD1_curr_setup и
7.
# hardware:
MCU настроен так, что не имеет выходов DAC и TIM. Следовательно ток лазеров задается по цифровому интерфейсу.
- SDMMC1 -- карта памяти
- **SPI2 -- TX**
- SPI4 -- RX
- SPI5 -- RX
- **SPI6 -- TX**
- USART1 -- связь с компом
main.c:1652: //LL_SPI_Enable(SPI6);//Enable SPI for Laser2 DAC -- *Догадка верна!*
Предыдущая строка (main.c:1651): `HAL_GPIO_WritePin(LD2_EN_GPIO_Port, LD2_EN_Pin, GPIO_PIN_SET);
Аналогично в(ы)ключается LD1 (main.c:1638-1647)
Что такое TEC2? `LL_SPI_Disable(SPI2);//Disable SPI for Laser1 DAC & TEC1`
main.c:1739`LL_SPI_TransmitData16(SPI6, DATA);//Transmit word to Laser1 DAC
--строка из функции void Set_LTEC(uint8_t num, uint16_t DATA).
Функция вызывается:
1. (main.c:260) `temp16=PID_Controller_Temp(&LD1_curr_setup, &LD1_param, 1);
`Set_LTEC(3, temp16);//Drive Laser TEC 1
2. (main.c:262) `Set_LTEC(4, temp16);//Drive Laser TEC 2` -- аналогично предыдущему
3. (main.c:277) `Set_LTEC(1,LD1_curr_setup.CURRENT[Work_counter]);//Drive Laser diode 1` -- установка тока лазера
4. main.c:1717 -- definition Set_LTEC -- LL_SPI_TransmitData16(SPI2, DATA);//Transmit word to Laser1 DAC, нужное устройство выбирается интерфейсом SPI (2 или 6) и сигналом EN через GPIO.
LD1_curr_setup -- структура с полями:
- P_coeff_temp
- I_coeff_temp
- CURRENT -- массив
Заполнение LD1_curr_setup: -- **происходит при парсинге команды 0x1111**
`for (uint8_t i=0; i<CURRL_16; i++)
`{
`LD1_curr_setup->CURRENT[i] = (uint16_t)(*temp2);
`temp2++;
`}` (main.c:1579-1583)
temp2 -- сдвинутый указатель на массив Command. Многократное увеличение temp2 на единицу -- видимо для расшифровки разных параметров.
# Структура команд
## 0x1111
| Номер | Название | Код | Расшифровка |
| ----- | ---------- | ------------------- | ------------------------------------ |
| 0 | Header | 0x1111 | Заголовок |
| 1 | Setup | 0xXXXX | Биты настроек |
| 2 | LD1_temp | 16bit float | Температура лазера 1 |
| 3 | LD2_temp | 16bit float | Температура лазера 1 |
| 4 | Reserved | 0x0000 | Зарезервировано |
| 5 | Reserved | 0x0000 | Зарезервировано |
| 6 | Reserved | 0x0000 | Зарезервировано |
| 7 | P_temp1 | 16bit float | Пропорциональный коэффициент для ЛМ1 |
| 8 | I_temp1 | 16bit float | Интегальный коэффициент для ЛМ1 |
| 9 | P_temp2 | 16bit float | Пропорциональный коэффициент для ЛМ2 |
| 10 | I_temp2 | 16bit float | Интегальный коэффициент для ЛМ2 |
| 11 | Message_ID | 16bit uint | Номер сообщения |
| 12 | LD1_curr | 16bit float***100** | Ток лазера 1 |
| 13 | LD2_curr | 16bit float***100** | Ток лазера 2 |
| 14 | CRC | 16bit uint | Контрольная сумма |
Итого 213 16-битовых слов.
## 0x7777
| Номер | Название | Код | Расшифровка | | | |
| ----- | ----------- | ----------- | ------------------------------------------------------------ | ----------------------------------- | ------------------------------------------------------------------------------------- | --------------------------------------- |
| 0 | Header | 0x7777 | Заголовок | поле в структуре | действие | ref |
| 1 | Setup | 0xXXXX | Биты настроек (как в 0х1111) | | | |
| 2 | TaskType | 16bit uint | 0х1 -- меняется ток лазера 1 | task.task_type | | |
| | | | 0х2 -- меняется ток лазера 2 | | | |
| | | | 0х3 -- меняется температура лазера 1 | | | |
| | | | 0х4 -- меняется температура лазера 2 | | | |
| 3 | chngd_p_min | 16bit uint | Минимальное значение изменяемого параметра (mA или C) | task.min_param = task.current_param | записывается в железный драйвер по SPI `Set_LTEC(task.task_type, task.current_param)` | main.c:373-377. И далее (main.c:384)... |
| 4 | chngd_p_max | 16bit uint | Максимальное значение изменяемого параметра (mA или C) | task.max_param | | |
| 5 | delta_p | 16bit uint | Шаг дискретизации изменяемого параметра (mA или C) | task.delta_param | | |
| 6 | delta_t | 16bit uint | Дискретный шаг по времени (мкс) | task.dt | период TIM10 | main.c:360 |
| 7 | fixed_param | 16bit uint | Значение другого параметра лазера с изменяемым параметром | task.tau | | |
| 8 | I_fixed | 16bit uint | Значение тока для лазера с фиксированными параметрами | task.sec_param | | |
| 9 | T_fixed | 16bit uint | Значение температуры для лазера с фиксированными параметрами | task.curr | | |
| 10 | Tau | 16bit uint | Значение тау (время релаксации, миллисекунды) | task.temp | | |
| 11 | P_temp1 | 16bit float | Пропорциональный коэффициент для ЛМ1 | task.p_coef_1 | | |
| 12 | I_temp1 | 16bit float | Интегальный коэффициент для ЛМ1 | task.i_coef_1 | | |
| 13 | P_temp2 | 16bit float | Пропорциональный коэффициент для ЛМ2 | task.p_coef_2 | | |
| 14 | I_temp2 | 16bit float | Интегальный коэффициент для ЛМ2 | task.i_coef_2 | | |
| 15 | CRC | 16bit uint | Контрольная сумма | | | |
# Общий принцип работы:
С частотой 1кГц:
- Установили ток current\[Work_counter]
- Померяли температуры
- увеличили Work_counter на 1
# получение температуры
Есть 2 подозрительные функции: Get_ADC и MPhD_T.
Что делает PID_Controller_Temp? -- Реализация ПИ контроллера.
### Get_ADC
В строках main.c:286:311 -- вызывается с параметрами 0-5, результат кладется в Long_Data\[CURRL_16\*2+5 -- CURRL_16\*2+10].
CURRL_16 -- макрос для 100.
Вероятно, это `//Prepare DATA of internals ADCs` (main.c:281)
определение Get_ADC(uint8_t num):
num == 0 => HAL_ADC_Start(&hadc1); // Power on
num == 1 => ADC1 poll for conversion, return value
num == 2 => HAL_ADC_Stop(&hadc1); // Power off
num == 3 => HAL_ADC_Start(&hadc3); // Power on
num == 4 => ADC3 poll for conversion, return value
num == 5 => HAL_ADC_Stop(&hadc3); // Power off
=> заполнение массива Long_Data\[CURRL_16\*2 +5:9] == результат измерения ADC1. Затем ADC1 выключается. Long_Data\[CURRL_16\*2+10] == единичное измерение ADC3.
ADC3 -- включен всего один канал.
###### Измеряется один и тот же канал или разные? -- ADC1 -- Разные; ADC3 -- один (единственный включенный)
ADC1 подключено к 5 каналам: IN2, IN8-11.
Конфигурация ADC1:
**Mode: independent**
**Scan conversion mode: enabled** -- после каждого измерения ADC переключается на следующий канал.
**Continuous conversion mode: disabled** -- In continuous conversion mode, the ADC starts a new conversion as soon as it finishes one. -- FALSE
**Discontinuous conversion mode: disabled** -- По триггеру запускается последовательность из нескольких (ADC_CR1 -> DISCNUM\[2:0]) измерений включенных каналов (ADC_SQRx).
**DMA Continuous Requests: Disabled**
**End of conversion selection: EOC flag at the end of single channel conversion.**
ADC1 и ADC3 -- сконфигурированы одинаково (кроме числа каналов.)
### MPhD_T(uint8_t num) -> uint16_t P
1. медленно дрыгаем ногами SPI(4:5)\_CNV_Pin для //Prepare conversion и //Stop acqusition & start conversion
2. Включаем SPI, ждем получения значения, возвращаем его.
3. Различия между параметрам num(1:4) -- дрыгаем разными ногами и слушаем разные SPI(4 или 5)
4. Ноги: (по 1_1_Report_generator_heterodyne_20240124 1.pdf)^[![[1_1_Report_generator_heterodyne_20240124 1.pdf#page=29]]]
| название | на железе (по .ioc) | на схеме из report | окончание | |
| ----------------- | ------------------- | ------------------ | ------------------------------------------------------------------------------- | -------------------------------------------- |
| ADC_ThrLD1_CS_Pin | PE11 | SPI4_NSS | [AD7686CRMZ](https://www.analog.com/en/products/ad7686.html#part-details) (ADC) | меряет цепь ThrLD1 -- **подтянутую к 2V5_3** |
| ADC_MPD1_CS_Pin | PE10 | SPI4_IO1 | [AD7686CRMZ](https://www.analog.com/en/products/ad7686.html#part-details) (ADC) | меряет цепь `100 mV` -- 1/50 от 5VA2 |
[Даташит на AD7686](AD7686.pdf)
6. Читаются:
1. num == 1 => MPD1
2. num == 2 => MPD2
3. num == 3 => ThrLD1
4. num == 4 => ThrLD2
7. Зачем-то MPhD_T() в коде всегда вызывается 2 раза подряд с одним и тем же аргументом, причем результат первого вызова отбрасывается.
Что такое ThrLD1? -- **Temperature** LaserDiode? ThorlabsLaserDiode?
Что такое MPD1? -- Monitor PhotoDiode1?
### Set_LTEC(uint8_t num, uint16_t DATA)
- num == 1 => запись DATA в DAC_LD1
- num == 2 => запись DATA в DAC_LD2
- num == 3 => запись DATA в DAC_TEC1
- num == 4 => запись DATA в DAC_TEC2
Куда идут значения MPhD_T(uint8_t num) -> uint16_t adc_val?
# В основном рабочем цикле WORK_ENABLE (main.c:243), каждую 1 мс:
TO7 -- счетчик TIM7_overflow -- происходят каждую 1 мс(? -- согласно комментарию в коде. Можно проверить, найдя источник тактирования TIM7 и зная настройки таймера)
- LD1_param.POWER = MPhD_T(1) (MPD1);//Get Data from monitor photodiode of LD1 **измерили мощность**
- LD1_param.LD_CURR_TEMP = MPhD_T(3); **измерили температуру**
- temp16=PID_Controller_Temp(&LD1_curr_setup, &LD1_param, 1); **рассчитали новую мощность нагревателя лазера
- Set_LTEC(3, temp16);//Drive Laser TEC 1 **отправили новую мощность в DAC нагревателя
- LD1_param.LD_CURR_TEMP -- средняя температура, реализовано через накопитель T1_temp.
- Корректировка нагревателя происходит непрерывно, каждый рабочий цикл (раз в 1 мс)
- Также непрерывно в Long_Data записывается LDx_param.POWER. LD1 -- в позиции от 0 до CURRL_16-1 (\==99), LD2 -- от 100 до 199.
- В DAC драйвера лазера записывается новая мощность из LD1_curr_setup.CURRENT\[WorkCounter]
-
- WorkCounter -- счетчик рабочих циклов. Меняется от 0 до 99, затем сбрасывается. При определенных значениях происходит:
- WorkCounter == 0 => старт нового цикла,
- WorkCounter == CURRL_16-8 (=92) -- запись значений внутренних ADC в конец Long_Data\[CURRL_16\*2 + 5:10]. (main.c:284)
- WorkCounter == CURRL_16-1 (main.c:317)
- В LDx_param.LD_CURR_TEMP записывается усредненная за весь цикл (WorkCounter от 0 до 99) температура
- В Long_Data\[CURRL_16\*2+1:2] записывается время с дискретностью 10 мс (т.е. единица времени -- 10 мс)
- В Long_Data\[CURRL_16\*2+3] записывается LD1_param.LD_CURR_TEMP; (main.c:337)
- В Long_Data\[CURRL_16\*2+4] записывается LD2_param.LD_CURR_TEMP; (main.c:340)
## Структура Long_Data
| Long_Data индекс | значение | кто пишет |
| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 0 : CURRL_16-1 (0:99) | Измеренная мониторным диодом мощность лазера 1 | LD1_param.POWER (main.c:269, 274), измеренное MPhD_T(1) (main.c:250) |
| CURR_L16 : CURRL_16\*2-1 (100:199) | Измеренная мониторным диодом мощность лазера 2 | LD2_param.POWER (main.c:270, 275), измеренное MPhD_T(2) (main.c:252) |
| CURR_L16\*2 +1:2 | Время в десятках мс | TO6_stop = TO6; и следующие 2 строки (main.c:332:334) (TO6 -- счетчик переполнения таймера TIM6, происходящего каждые 10 мс) |
| Long_Data\[CURRL_16\*2+3] | Усредненная за цикл варьирования мощности температура лазера 1 | LD1_param.LD_CURR_TEMP (main.c:337) Усреднение -- main.c:323 и main.c:264. Среднее рассчитывается прямо перед записью в массив. До этого в LD1_param.LD_CURR_TEMP хранится последняя измеренная температура |
| Long_Data\[CURRL_16\*2+4] | Усредненная за цикл варьирования мощности температура лазера 2 | Аналогично предыдущему |
| Long_Data\[CURRL_16\*2 + 5:10] | Значения внутренних ADC. Каналы и пины MCU соответственно: \[ADC1_IN2 - PA2, ADC1_IN8 - PB0, ADC1_IN9 - PB1, ADC1_IN10 - PC0, ADC1_IN11 - PC1, ADC2_IN15 - PF5] | Запись происходит когда WorkCounter == CURRL_16-8. main.c:284:311 |
| Long_Data\[CURRL_16\*2 + 11] | //Message ID | main.c:1577 |
| Long_Data\[DL_16-1] (212) | Checksum | main.c:342:348 |
Long_Data сохраняется на SD карточку (main.c:342:348)
Long_Data может быть прочитана с SDcard (main.c:216:218)
Long_Data отправляется по UART через DMA (main.c:434:442) или (main.c:420:433), взависимости от UART_transmission_request (если равна MESS_02 или MESS_03)
Long_Data (uint16_t) преобразуется в UART_DATA (uint8_t), затем вызывается функция USART_TX_DMA(data_length), запускающая отправку через DMA. DMA контроллер сконфигурирован брать данные из памяти, начиная с указателя UART_DATA (main.c:1437), отправлять в регистр LL_USART_DMA_REG_DATA_TRANSMIT, после чего инкрементировать адрес (main.c:1177), откуда берутся данные. И так data_length раз.
#### Где устанавливается флаг UART_transmission_request?
MESS_03 -- если Long_Data прочитан с SDcard
MESS_02 -- если CPU_state == TRANS_ENABLE
Установка флага CPU_state в состояние TRANS_ENABLE:
Если UART принял команду 0x4444.
# Обработка команды 0x4444
1. по UART пришло сообщение. Когда прием сообщения окончился -- вызывается обработчик UART_RxCpltCallback (stm32f7xx_it.c:376)
2. Обработчик читает заголовок сообщения. Если это 0x4444 -- CPU_state устанавливается в TRANS_ENABLE.
3. В основном цикле (main.c:154) проверяется CPU_state. Если это TRANS_ENABLE -- UART_transmission_request устанавливается в MESS_02, CPU_state устанавливается в предыдущее состояние.
4. В том же цикле проверяется состояние UART_transmission_request. Если это MESS_02:
1. Рассчитывается контрольная сумма массива Long_Data
2. Long_Data отправляется через DMA на передачу UART
# Режим TASK_ENABLE
0. TIM10 -- установлен на генерацию прерывания и обновление TO10. Предделитель дает один отсчет таймера в 1 мкс.
1. Установили период TIM10 в dt микросекунд. (обновляем TO10) (main.c:357:363)
2. Каждое обновление TO10 через записываем Set_LTEC в DAC, увеличиваем параметр (task.current_param) на task.delta_param, пока не достигнем максимального значения (task.max_param) (main.c:373:379)
3. Останавливаем TIM10, обнуляем счетчики (TO10, TO10_before), task.current_param = min_param;
4.
# почему при установке min, max тока одного лазера ток другого устанавливается == min первого?
В For_stm32_2023_12_08 — в main.c:339 — при варьировании тока диода 1 — ток диода 2 устанавливается в мин. значение тока диода 1. В main.c:378 — наоборот. Зачем так сделано — непонятно.
В For_stm32_cubeide -- всё хорошо, токи варьируются независимо.