/* ---------------------------------------------------------------------------- */ /* Atmel Microcontroller Software Support */ /* SAM Software Package License */ /* ---------------------------------------------------------------------------- */ /* Copyright (c) 2015, Atmel Corporation */ /* */ /* All rights reserved. */ /* */ /* Redistribution and use in source and binary forms, with or without */ /* modification, are permitted provided that the following condition is met: */ /* */ /* - Redistributions of source code must retain the above copyright notice, */ /* this list of conditions and the disclaimer below. */ /* */ /* Atmel's name may not be used to endorse or promote products derived from */ /* this software without specific prior written permission. */ /* */ /* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR */ /* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE */ /* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, */ /* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ /* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */ /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF */ /* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */ /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */ /* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* ---------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------- * Headers *----------------------------------------------------------------------------*/ #include "chip.h" /*---------------------------------------------------------------------------- * Local functions *----------------------------------------------------------------------------*/ /** * \brief Workaround for ISI CFG2 register read. * \note The ISI_CFG2[31:27] can be written correctly, because the input writing * data are assigned directly to the internal control bits as specified, * the mismatch only happens in reading operation. * [31:28] are shift right 1 bit, so [31:27] can be read from [30:27]. */ __STATIC_INLINE uint32_t _ISI_GetCFG2_Workaround(void) { uint32_t wrongfield; wrongfield = ISI->ISI_CFG2 >> (ISI_CFG2_YCC_SWAP_Pos - 1); return (ISI->ISI_CFG2 & 0x07FFFFFF) | (wrongfield << ISI_CFG2_YCC_SWAP_Pos); } /*---------------------------------------------------------------------------- * Export functions *----------------------------------------------------------------------------*/ /** * \brief Enable ISI */ void ISI_Enable(void) { ISI->ISI_CR |= ISI_CR_ISI_EN; while ((ISI->ISI_SR & ISI_CR_ISI_EN) != ISI_CR_ISI_EN); } /** * \brief Enable ISI Dma channel * \param channel to be enabled */ void ISI_DmaChannelEnable(uint32_t channel) { ISI->ISI_DMA_CHER |= channel; } /** * \brief Disable ISI Dma channel * \param channel to be disabled */ void ISI_DmaChannelDisable(uint32_t channel) { ISI->ISI_DMA_CHDR |= channel; } /** * \brief Disable ISI */ void ISI_Disable(void) { /* Write one to this field to disable the module */ ISI->ISI_CR |= ISI_CR_ISI_DIS; /* Software must poll DIS_DONE field in the ISI_STATUS register to verify that the command has successfully completed.*/ while ((ISI->ISI_SR & ISI_SR_DIS_DONE) != ISI_SR_DIS_DONE); } /** * \brief Enable ISI interrupt * \param flag of interrupt to enable */ void ISI_EnableInterrupt(uint32_t flag) { ISI->ISI_IER = flag; } /** * \brief Disable ISI interrupt * \param flag of interrupt to disable */ void ISI_DisableInterrupt(uint32_t flag) { ISI->ISI_IDR = flag; } /** * \brief Return ISI status register * \return Status of ISI register */ uint32_t ISI_StatusRegister(void) { return (ISI->ISI_SR); } /** * \brief Enable Codec path for capture next frame */ void ISI_CodecPathFull(void) { // The codec path is enabled and the next frame is captured. // Both codec and preview data-paths are working simultaneously ISI->ISI_CR |= ISI_CR_ISI_CDC; ISI->ISI_CFG1 |= ISI_CFG1_FULL; } /** * \brief Set frame rate * \param frame frame rate capture */ void ISI_SetFrameRate(uint32_t frame) { if (frame > 7) { TRACE_ERROR("rate too big\n\r"); frame = 7; } ISI->ISI_CFG1 |= ISI_CFG1_FRATE(frame); } /** * \brief Get the number of byte per pixels * \param bmpRgb BMP type can be YUV or RGB */ uint8_t ISI_BytesForOnePixel(uint8_t bmpRgb) { uint8_t nbByte_Pixel; if (bmpRgb == RGB) { if ((_ISI_GetCFG2_Workaround() & ISI_CFG2_RGB_MODE) == ISI_CFG2_RGB_MODE) { // RGB: 5:6:5 16bits/pixels nbByte_Pixel = 2; } else { // RGB: 8:8:8 24bits/pixels nbByte_Pixel = 3; } } else { // YUV: 2 pixels for 4 bytes nbByte_Pixel = 2; } return nbByte_Pixel; } /** * \brief Reset ISI */ void ISI_Reset(void) { uint32_t timeout = 0; // Resets the image sensor interface. // Finish capturing the current frame and then shut down the module. ISI->ISI_CR = ISI_CR_ISI_SRST | ISI_CR_ISI_DIS; // wait Software reset has completed successfully. while ((!(ISI->ISI_SR & ISI_SR_SRST)) && (timeout < 0x5000)) timeout++; if (timeout == 0x5000) TRACE_ERROR("ISI-Reset timeout\n\r"); } /** * \brief Set the windows blank * \param hBlank pixel clock periods to wait before the beginning of a line. * \param vBlank lines are skipped at the beginning of the frame. */ void ISI_SetBlank(uint8_t hBlank, uint8_t vBlank) { ISI->ISI_CFG1 |= ISI_CFG1_SLD(hBlank) + ISI_CFG1_SFD(vBlank); } /** * \brief Set vertical and horizontal Size of the Image Sensor * \param hSize Horizontal size of the Image sensor [0..2047]. * \param vSize Vertical size of the Image sensor [0..2047]. */ void ISI_SetSensorSize(uint32_t hSize, uint32_t vSize) { uint32_t val; val = _ISI_GetCFG2_Workaround(); val &= (~ISI_CFG2_IM_VSIZE_Msk); val &= (~ISI_CFG2_IM_HSIZE_Msk); // IM_VSIZE: Vertical size of the Image sensor [0..2047] // Vertical size = IM_VSIZE + 1 // IM_HSIZE: Horizontal size of the Image sensor [0..2047] // Horizontal size = IM_HSIZE + 1 ISI->ISI_CFG2 &= (~ISI_CFG2_IM_VSIZE_Msk); ISI->ISI_CFG2 &= (~ISI_CFG2_IM_HSIZE_Msk); ISI->ISI_CFG2 = val | ISI_CFG2_IM_VSIZE(vSize - 1) | ISI_CFG2_IM_HSIZE( hSize - 1); } /** * \brief Defines RGB pattern when RGB_MODE is set to 1. * \param wRgbPixelMapping RGB pattern */ void ISI_RgbPixelMapping(uint32_t wRgbPixelMapping) { ISI->ISI_CFG2 = _ISI_GetCFG2_Workaround() & (~ISI_CFG2_RGB_CFG_Msk); if (wRgbPixelMapping != ISI_CFG2_RGB_CFG_DEFAULT) ISI->ISI_CFG2 = _ISI_GetCFG2_Workaround() | wRgbPixelMapping | ISI_CFG2_RGB_MODE; else ISI->ISI_CFG2 = _ISI_GetCFG2_Workaround(); } /** * \brief Enables RGB swap * \param swapMode 0: D7-R7, 1: D0-R7 */ void ISI_RgbSwapMode(uint32_t swapMode) { ISI->ISI_CFG2 = _ISI_GetCFG2_Workaround() & (~ISI_CFG2_RGB_SWAP); if (swapMode) ISI->ISI_CFG2 = _ISI_GetCFG2_Workaround() | ISI_CFG2_RGB_SWAP; } /** * \brief Defines YCrCb swap format. * \param wYuvSwapMode YUV Swap format */ void ISI_YCrCbFormat(uint32_t wYuvSwapMode) { ISI->ISI_CFG2 = _ISI_GetCFG2_Workaround() & (~ISI_CFG2_YCC_SWAP_Msk); ISI->ISI_CFG2 = _ISI_GetCFG2_Workaround() | wYuvSwapMode; } /** * \brief Input image is assumed to be grayscale-coded. * \param wPixelFormat 0: 2 pixels per word, 1:1 pixel per word. */ void ISI_setGrayScaleMode(uint32_t wPixelFormat) { ISI->ISI_CFG2 = _ISI_GetCFG2_Workaround() | ISI_CFG2_GRAYSCALE; if (wPixelFormat) ISI->ISI_CFG2 = _ISI_GetCFG2_Workaround() | ISI_CFG2_GS_MODE; } /** * \brief Set data stream format. * \param wStreamMode 0: YUV input, 1: RGB 8:8:8/5:6:5 input */ void ISI_setInputStream(uint32_t wStreamMode) { ISI->ISI_CFG2 = _ISI_GetCFG2_Workaround() & (~ISI_CFG2_COL_SPACE); if (wStreamMode) ISI->ISI_CFG2 = _ISI_GetCFG2_Workaround() | ISI_CFG2_COL_SPACE; } /** * \brief Set preview size. * \param hSize Horizontal Preview size (640 max only in RGB mode). * \param vSize Vertical Preview size (480 max only in RGB mode). */ void ISI_setPreviewSize(uint32_t hSize, uint32_t vSize) { if (hSize > 640) hSize = 640; if (vSize > 480) vSize = 480; ISI->ISI_PSIZE = ISI_PSIZE_PREV_VSIZE(vSize - 1) | ISI_PSIZE_PREV_HSIZE( hSize - 1); } /** * \brief calculate scaler factor automatically. * \note The sensor size and preview size for LCD was configured before this setting. */ void ISI_calcScalerFactor(void) { uint32_t hLcdSize, hSensorSize; uint32_t hRatio; hLcdSize = ((ISI->ISI_PSIZE & ISI_PSIZE_PREV_HSIZE_Msk) >> ISI_PSIZE_PREV_HSIZE_Pos) + 1; hSensorSize = ((_ISI_GetCFG2_Workaround() & ISI_CFG2_IM_HSIZE_Msk) >> ISI_CFG2_IM_HSIZE_Pos) + 1; hRatio = 1600 * hSensorSize / hLcdSize; ISI->ISI_PDECF = (hRatio / 100); } /** * \brief Configure DMA for preview path. * \param baseFrameBufDesc Preview Descriptor Address. * \param dmaCtrl DMA Preview Control. * \param frameBufferStartAddr DMA Preview Base Address. */ void ISI_setDmaInPreviewPath(uint32_t baseFrameBufDesc, uint32_t dmaCtrl, uint32_t frameBufferStartAddr) { ISI->ISI_DMA_P_DSCR = baseFrameBufDesc; ISI->ISI_DMA_P_CTRL = dmaCtrl; ISI->ISI_DMA_P_ADDR = frameBufferStartAddr; } /** * \brief Configure DMA for Codec path. * \param baseFrameBufDesc Preview Descriptor Address. * \param dmaCtrl DMA Preview Control. * \param frameBufferStartAddr DMA Preview Base Address. */ void ISI_setDmaInCodecPath(uint32_t baseFrameBufDesc, uint32_t dmaCtrl, uint32_t frameBufferStartAddr) { ISI->ISI_DMA_C_DSCR = baseFrameBufDesc; ISI->ISI_DMA_C_CTRL = dmaCtrl; ISI->ISI_DMA_C_ADDR = frameBufferStartAddr; } /** * \brief ISI set matrix for YUV to RGB color space for preview path. * \param yuv2rgb structure of YUV to RBG parameters. */ void ISI_SetMatrix4Yuv2Rgb (ISI_Y2R *yuv2rgb) { ISI->ISI_Y2R_SET0 = ISI_Y2R_SET0_C0(yuv2rgb->C0) | ISI_Y2R_SET0_C1(yuv2rgb->C1) | ISI_Y2R_SET0_C2(yuv2rgb->C2) | ISI_Y2R_SET0_C3(yuv2rgb->C3); ISI->ISI_Y2R_SET1 = ISI_Y2R_SET1_C4(yuv2rgb->C4) | ((yuv2rgb->Yoff == 1) ? ISI_Y2R_SET1_Yoff : 0) | ((yuv2rgb->Croff == 1) ? ISI_Y2R_SET1_Croff : 0) | ((yuv2rgb->Cboff == 1) ? ISI_Y2R_SET1_Cboff : 0); } /** * \brief ISI set matrix for RGB to YUV color space for codec path. * \param rgb2yuv structure of RGB to YUV parameters. */ void ISI_SetMatrix4Rgb2Yuv (ISI_R2Y *rgb2yuv) { ISI->ISI_R2Y_SET0 = ISI_R2Y_SET0_C0(rgb2yuv->C0) | ISI_R2Y_SET0_C1(rgb2yuv->C1) | ISI_R2Y_SET0_C2(rgb2yuv->C2) | ((rgb2yuv->Roff == 1) ? ISI_R2Y_SET0_Roff : 0); ISI->ISI_R2Y_SET1 = ISI_R2Y_SET1_C3(rgb2yuv->C3) | ISI_R2Y_SET1_C4(rgb2yuv->C4) | ISI_R2Y_SET1_C5(rgb2yuv->C5) | ((rgb2yuv->Goff == 1) ? ISI_R2Y_SET1_Goff : 0); ISI->ISI_R2Y_SET2 = ISI_R2Y_SET2_C6(rgb2yuv->C6) | ISI_R2Y_SET2_C7(rgb2yuv->C7) | ISI_R2Y_SET2_C8(rgb2yuv->C8) | ((rgb2yuv->Boff == 1) ? ISI_R2Y_SET2_Boff : 0); }