Произошла в моей жизни такая необходимость как подключение камеры (точнее тепловизора) к конечному устройству по 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 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 это регистр 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.

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

Комментарии (2)


  1. x89377
    25.01.2025 09:24

    Спасибо за публикацию. Не могли бы Вы добавить файл .ioc (если он есть) на github ?


    1. lyrush Автор
      25.01.2025 09:24

      Здравствуйте. Собирала без генератора кода, поэтому, нет.