STM32F4xx + DCMI + USB Custom (CDC + UVC)

Произошла в моей жизни такая необходимость как подключение камеры (точнее тепловизора) к конечному устройству по 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 — 0×63 регистр, описание в Astir2 User Guide на стр. 31.

Битовое поле регистра External synchronization mode:

8c0bb72230dfd2e7b29505a445ddcb1e.jpg

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

Режим continuous synchronization mode

Режим continuous synchronization mode

Режим single frame synchronization mode

Режим single frame synchronization mode

Выбран режим continuous.

Выбор команды — CAM_DIG_VIDEO_OUT (цифровой выход). Сама команда — video output 18\r. В Astir2 User Guide это регистр 0×7D стр. 33.

BT656 протокол

Протокол BT656 — параллельный протокол с количеством данных от 8 до 10 и линия клока. В данном проекте используется 8-ми параллельная передача данных.

В качестве синхронизации используется внутренняя синхронизация, т.е заданный по протоколу сигнал горизонтальной и вертикальной развертки.

bfbed42ddcb0910dc815f2f6068be45e.jpg

EAV — End Active Video

SAV — Start Active Video

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

a73b90e954e95c8ceb4d2292f2a696fe.jpg

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

66281c8910880c4bbf299462377f6c27.jpg

Настройка кодов синхронизации в программе:

  hdcmi.Init.SyncroCode.FrameEndCode = 0x9d;
  hdcmi.Init.SyncroCode.FrameStartCode = 0x80;
  hdcmi.Init.SyncroCode.LineStartCode = 0xab;
  hdcmi.Init.SyncroCode.LineEndCode = 0xb6;

В продолжении статьи будет освещена настройка USB Custom.

Ссылка на программу

© Habrahabr.ru