Произошла в моей жизни такая необходимость как подключение камеры (точнее тепловизора) к конечному устройству по 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.
x89377
Спасибо за публикацию. Не могли бы Вы добавить файл .ioc (если он есть) на github ?
lyrush Автор