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

27 KiB
Raw Permalink Blame History

отследим формирование ответа с отправки

Общий принцип такой:

  • 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 и

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)^[!]
название на железе (по .ioc) на схеме из report окончание
ADC_ThrLD1_CS_Pin PE11 SPI4_NSS AD7686CRMZ (ADC) меряет цепь ThrLD1 -- подтянутую к 2V5_3
ADC_MPD1_CS_Pin PE10 SPI4_IO1 AD7686CRMZ (ADC) меряет цепь 100 mV -- 1/50 от 5VA2
Даташит на AD7686
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

  1. TIM10 -- установлен на генерацию прерывания и обновление TO10. Предделитель дает один отсчет таймера в 1 мкс.
  2. Установили период TIM10 в dt микросекунд. (обновляем TO10) (main.c:357:363)
  3. Каждое обновление TO10 через записываем Set_LTEC в DAC, увеличиваем параметр (task.current_param) на task.delta_param, пока не достигнем максимального значения (task.max_param) (main.c:373:379)
  4. Останавливаем TIM10, обнуляем счетчики (TO10, TO10_before), task.current_param = min_param;

почему при установке min, max тока одного лазера ток другого устанавливается == min первого?

В For_stm32_2023_12_08 — в main.c:339 — при варьировании тока диода 1 — ток диода 2 устанавливается в мин. значение тока диода 1. В main.c:378 — наоборот. Зачем так сделано — непонятно. В For_stm32_cubeide -- всё хорошо, токи варьируются независимо.