Capturing images using the Digital Camera Interface | STM32L4 | DCMI | CMSIS

Digital Camera Interface

The DCMI (Digital Camera Interface) is a synchronous parallel interface designed to receive a high-speed data stream from an external CMOS camera module. It offers compatibility with 8-bit, 10-bit, 12-bit, or 14-bit camera modules. The DCMI peripheral supports Embedded Line and Frame synchronization methods in addition to the Hardware Synchronisation from the CMOS Camera Module. It has two operating mode, namely Continuous or Snapshot mode. Additionally, it offers a crop feature and supports various data formats, including 8/10/12/14-bit progressive video (Monochrome or Raw Bayer), YCbCr 4:2:2 Progressive Video, RGB 565 Progressive Video, and Compressed Data in JPEG format. For more information on the DCMI peripheral, read the Training material for DCMI on STM32L4 from STMicroelectronics.

DCMI Registers

The DCMI peripheral has Control and Status registers for the DCMI Core and Interrupts. It also has a few registers for handling Data synchronization, Cropping, and at last a Data Register. The ones used in this blog are:

  1. DCMI Control Register (DCMI_CR)
  2. DCMI Status Register (DCMI_SR)
  3. DCMI Interrupt Enable Register (DCMI_IER)
  4. DCMI Raw Interrupt Status Register (DCMI_RIS)
  5. DCMI Interrupt Clear Register (DCMI_ICR)
  6. DCMI Data Register (DCMI_DR)

Configuration of DCMI Peripheral 

This driver is written specifically for interfacing with an OV7670 CMOS Camera Module from Omni Vision. So, we will configure the DCMI for an 8-bit Parallel interface. The Pixel Clock Polarity is set to the Rising Edge to capture the data. Hardware Synchronisation is used instead of Embedded Synchronisation mode and JPEG Compression is opted. We will configure the driver to operate the camera in Snapshot mode and not the Continous Grab mode. The rest of the options are set to the reset state. 

Walkthrough

Definitions and Headers


We will start off with including the DCMI.h file and defining the Buffer Size to store the data received from the Camera Module. We need an array to store the received data from the camera, and hence we define a character array of size, BUFFER_SIZE. The functions to initialize GPIO Pins for the DCMI Peripheral and the DMA controller are not exposed to the user, and hence we define it at this time.

Initializing GPIO Pins


If you consult the Alternate function Pin Mapping section in the Datasheet for the STM32L4R5ZI microcontroller, you can see that different ports can be used for the DCMI Data and Control pins. But, for this driver, we are using the below-listed configuration. 

HSYNC PH8
VSYNC PI5
PIXCLK PH5
D0 PH9
D1 PH10
D2 PH11
D3 PH12
D4 PH14
D5 PI4
D6 PI6
D7 PI7

So first, we enable the clock to GPIOH and GPIOI ports. Then we set the required pins of Port H and Port I into Alternate Function mode. From there, we then set the Alternate Function for the respective pins. You can find the Alternate Function numbers for these pins from the Datasheet.

Configuring DMAMUX and DMA Controller

Once the DCMI peripheral starts capturing a frame, the data is added to an 8-word* FIFO and then fed into the DCMI Data Register (DCMI_DR). Since the DCMI peripheral acquires data at high speed, we have to use DMA to read the data from the DCMI_DR register rather than using the Software method. 

In the STM32F4, we had some fixed peripherals for a DMA Channel and the inputs were multiplexed. But in the STM32L4, this is different. We can configure which peripheral can use which DMA Channel using the DMA Request Multiplexer (DMAMUX) peripheral. 

We start off by enabling the clock to DMA1 and DMAMUX peripherals and waiting for the clock to be active. We set the DMAMUX's Channel 0 to the DCMI Peripheral, which is DMAMUX Input Request 90. So, we write 90 to the DCMI_CCR register. Then, we configure the DMA Controller's Channel 1, Source, and the Destination address to the DCMI_DR register and the character array, imageDataBuffer respectively. Then, we set the NDTR value to the BUFFER_SIZE macro, configure DMA to Memory Increment Mode, Enable Transfer Complete Interrupt, Set Transfer Direction to Read from Memory, and Set the Priority to High.

Configuring DCMI Core

From this function, we call the DCMI_InitGPIO() and DCMI_InitDMA() functions. In the DCMI Control Register, we set the DCMI peripheral to 8-bit input, with Hardware Synchronization, JPEG Compression, PXCLK Polarity to Rising Edge, and Capture Mode in Snapshot Mode. Then, we enable all the Interrupts available to the DCMI Peripheral in the DCMI_IER register. Finally, we enable the DCMI peripheral by setting the EN bit in the DCMI_CR register. But this does not start sampling the data from the camera, for that we have to set the CAPTURE bit.

De-initializing the DCMI Peripheral

When we want to De-Initialize the DCMI peripheral, we can do so by cutting off the clock signal to the DCMI peripheral in the RCC_AHB2ENR register.

Enabling and Disabling Capture

Once we need to capture a particular frame, we set the CAPTURE bit in the DCMI_CR register, Along with that, we will also enable Channel 1 of DMA1 to transfer data from DCMI_DR to the imageDataBuffer array. In the Snapshot mode, we don't need to Disable Capture, the hardware will reset the CAPTURE bit in the DCMI_CR register when a complete frame has been captured.

Polling DCMI Status 

The DCMI Status Register can poll the status of three conditions, 

  1. If FIFO is empty
  2. If in between Frames
  3. If in between Lines
When this function is called, it will check the DCMI_SR register for these three conditions and report the status. 

DCMI Interrupt Handler

The DCMI Interrupt Request Handler is used to handle the Interrupts that occur due to any of the five previously enabled Interrupts in the DCMI_IER register. The five interrupts of the DCMI peripheral are;

  1. End of Line Interrupt
  2. Frame Synchronization Interrupt
  3. Embedded Synchronization Code Error Interrupt
  4. Data Overrun Interrupt
  5. End of Frame Interrupt

For the time being, we can just print the name of the interrupt fired to the Serial Terminal. We also have to clear the corresponding interrupt in the DCMI_ICR register, or else the Interrupt will be marked as pending. 

Checkout my GitHub repository for example code and projects.

Popular posts from this blog

SignGlove: Bridging the Communication Gap for Paralyzed Patients

Cifradopro: A baremetal Hardware Security Module using the STM32L4S5 Cortex-M4 MCU

AESecureIO: Embedded Data Encryption and Decryption System