Произошла в моей жизни такая необходимость как подключение камеры (точнее тепловизора) к конечному устройству по USB. Камера с интерфейсом DCMI и протоколом BT656. Собственно, поэтому была разработана (написана) программа. Постараюсь изложить некоторые особенности.
Ссылка на программу прикреплена в конце публикации.
Кадр
Кадр размещается во внешнюю SDRAM (Synchronous Dynamic Random Access Memory).
Инициализация и настройка таймингов памяти (…/User/Src/sdram.c)
void MX_FMC_Init(void) { FMC_SDRAM_TimingTypeDef SdramTiming = {0}; /** Perform the SDRAM1 memory initialization sequence */ hsdram1.Instance = FMC_SDRAM_DEVICE; /* hsdram1.Init */ hsdram1.Init.SDBank = FMC_SDRAM_BANK1; hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8; hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12; hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16; hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4; hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3; hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE; hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_3; hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_DISABLE; hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_1; /* SdramTiming */ //This settings are taken from the memory manual SdramTiming.LoadToActiveDelay = 2; SdramTiming.ExitSelfRefreshDelay = 7;//11; 7 SdramTiming.SelfRefreshTime = 4;//7; 4 SdramTiming.RowCycleDelay = 6;//10; 6 SdramTiming.WriteRecoveryTime = 2; SdramTiming.RPDelay = 2; SdramTiming.RCDDelay = 2; if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK) { Error_Handler( ); } }
Настройки взяты из примеров (коих очень много, даже ссылку оставлять не буду) и даташита на микросхему (IS42S16800F-6tli).
Захват и размещение кадра в памяти (…/User/Src/camera.c):
uint8_t GrabCamFrame(void) { CameraTypeDef *ptr = &Camera; if(ptr->FlagFrameTrans)return 0; //this means the frame is sent via uvc. you can parse cmd. if(ptr->CmdFrameComplit == CAM_INIT) { HAL_DCMI_Start_DMA(&hdcmi,DCMI_MODE_CONTINUOUS,SDRAM_BANK_ADDR,FRAMESIZEWORDS); while(pcamera->FlagFrameComplit==0) { ; } GPIOC->ODR |= (1<<5); ptr->FlagFrameTrans=1; pcamera->FlagFrameComplit=0; } return ptr->FlagFrameTrans; }
DCMI_MODE_CONTINUOUS – непрерывный режим работы.
SDRAM_BANK_ADDR – адрес во внешней памяти, куда записываются байты кадра 0xC0000000.
FRAMESIZEWORDS – размер кадра в 16-битном поле.
GrabCamFrame() вызывается в основном потоке. После захвата кадра и размещения в памяти, устанавливается флаг FlagBusy, используется как признак передачи кадра по USB.
Использована блокирующая функция. Есть необходимость от нее избавиться.
void HAL_DCMI_LineEventCallback(DCMI_HandleTypeDef *hdcmi) { if(vLineCntFrame++ ==FRAMELINES) { __HAL_DCMI_DISABLE(hdcmi); //this is not safe stop DCMI over DMA, but allows the frame to be stable HAL_DMA_Abort(hdcmi->DMA_Handle); vLineCntFrame=0; pcamera->FlagFrameComplit =1; } }
FRAMELINES – количество строк в кадре (…/User/Inc/camera.h).
Инициализация камеры.
Функция инициализации камеры:
void CameraInit(void) { pcamera->pBufCmdResponse = ResponseCmdFromCam; pcamera->pBufCmdReceived = ReceiveCmdToCam; pcamera->Start = CameraStart; }
Инициализация буферов команд и функции Start.
Структура камеры (…/User/Inc/camera.h):
typedef struct{ uint8_t State; //enable or disable uint8_t CmdFrameComplit; //exec cmd uint32_t AddrBufFrame; volatile uint8_t FlagFrameBufFull; volatile uint8_t FlagFrameComplit; uint8_t FlagFrameTrans; uint32_t cntBufFrame; uint8_t NumBuffs; //number of buffers char *pBufCmdResponse; char *pBufCmdReceived; uint16_t ResponseSize; uint16_t CmdSize; volatile uint8_t FlagCmdResponse; volatile uint8_t FlagCmdReceived; void (* Start) (void); void (* Init) (void); }CameraTypeDef;
Сущность класса CameraTypeDef - Camera; //TV - thermal imager
CameraStart (…/User/Src/camera.c):
void CameraStart(void) { HAL_UART_Transmit(&huart2,(uint8_t *)Cmd[CAM_SYCH_MODE_EN],strlen(Cmd[CAM_SYCH_MODE_EN]),100); HAL_Delay(40); HAL_UART_Transmit(&huart2,(uint8_t *)Cmd[CAM_DIG_VIDEO_OUT],strlen(Cmd[CAM_DIG_VIDEO_OUT]),100); }
Камера настраивается по uart, так реализована сама камера.
Выбор команды - CAM_SYCH_MODE_EN (режим синхронизации). Сама команда - rw 99 131\r. rw – внутренняя команда камеры – чтение/запись регистра, параметры – регистр и записываемое значение. Команды и значения записываются в десятичном формате. Т.е. 99 – 0x63 регистр, описание в Astir2 User Guide на стр. 31.
Битовое поле регистра External synchronization mode:

Для понимания:


Выбран режим continuous.
Выбор команды - CAM_DIG_VIDEO_OUT (цифровой выход). Сама команда - video output 18\r. В Astir2 User Guide это регистр 0x7D стр. 33.
BT656 протокол
Протокол BT656 – параллельный протокол с количеством данных от 8 до 10 и линия клока. В данном проекте используется 8-ми параллельная передача данных.
В качестве синхронизации используется внутренняя синхронизация, т.е заданный по протоколу сигнал горизонтальной и вертикальной развертки.

EAV – End Active Video
SAV – Start Active Video
EAV и SAV есть для полезных данных и для Blanking. Определяется по определенному порядку, таблица ниже:

Preamble – 0xFF0000
F – field (четные/нечетные строки): F – 0 – нечетные строки.
V – вертикальная развертка
H = 0 - SAV, H = 1 – EAV
P3 = V + H
P2 = F + H
P1 = F + V
P0 = F + V + H

Настройка кодов синхронизации в программе:
hdcmi.Init.SyncroCode.FrameEndCode = 0x9d; hdcmi.Init.SyncroCode.FrameStartCode = 0x80; hdcmi.Init.SyncroCode.LineStartCode = 0xab; hdcmi.Init.SyncroCode.LineEndCode = 0xb6;
В продолжении статьи будет освещена настройка USB Custom.