<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      STM32H743 驅動 0.96彩屏OLED (一)

       

      數據通訊方式 4-SPI
      屏幕尺寸 0.96寸
      分辨率 160*80*3 
      色彩模式 RGB888/565
      顯示IC SP5210
      模塊制造商 臺灣YEEBO
      產地 蘇州
      物理接口形式 25PIN0.3FPC
      主要引腳 VCC VSS RES A0 CS SCL MOSI VPP FRM
      顯示類型 OLED

       單片機      STM32H743

      工作中的任務,給產品增加一個狀態指示屏,由于初期SPEC要求的頁面不多,復雜度也小,就決定從驅動到應用層都自己寫了。最開始使用單個數據的硬件SPI發送,軟件觸發傳輸,之后改為DMA觸發SPI,最后增加垂直同步外部中斷。

      從IC手冊和設備手冊中查好接口定義,網購了那個0.3間距25PIN的FPC排線插座(各種各樣的,我喜歡前插口后鎖定板的,有同事覺得前插口前鎖定的更好)和轉2.54的轉接PCB板,按照推薦電路接好電阻電容,用直流電源給VPP外接16V供電,雖然容易誤碰電壓旋鈕燒屏,但是這個屏幕不是那種正負4.5V的供電方式,沒辦法。。。

      調試用的H743 最小開發板,上面的晶振12M,與產品樣機上用的25M晶振不一樣,所以在給不同板下載測試時,要把system_stm32h7xx.c里面的

      #define HSE_VALUE                  ((uint32_t)12000000)

      #define HSE_VALUE                  ((uint32_t)25000000)

      修改成實際使用的晶振頻率,不改的話,很大概率也能運行起來,但時不時看門狗定期重啟或者SPI傳輸不正確等現象。

      而且后續的PLLM、PLLN、PLLP,以及選用的SPI所在PLL分段也要對應修改一下。

      ----------此處路段掉坑很多,請小心駕駛。------------

      (怕踢電源,先commit一下)

      配置完時鐘,繼續配置引腳。為了盡量降低CPU負載,選用硬件SPI4進行通訊。

      #define IO_PE2             GPIOE, GPIO_PIN_2 /* OLED SCK (HARD) SPI4_SCL */
      #define IO_PE15_O_OLED_RESET    GPIOE, GPIO_PIN_3 /* OLED RESET (SOFT) */
      #define IO_PE11_O_OLED_CS      GPIOE, GPIO_PIN_4 /* OLED CS (SOFT) SPI4_NSS */
      #define IO_PE13_O_OLED_A0      GPIOE, GPIO_PIN_5 /* OLED DATA OR PARAM SELECT (SOFT) SPI4_MISO */
      #define IO_PE6             GPIOE,  GPIO_PIN_6 /* OLED SDA_IN (HARD) SPI4_MOSI*/

      為方便整體修改,做宏定義

      #define SPI_CH2_SPI SPI4
      #define SPI_CH2_AF GPIO_AF5_SPI4
      #define SPI_CH2_GRP_CLK LL_AHB4_GRP1_PERIPH_GPIOE
      #define SPI_CH2_PORT GPIOE
      #define SPI_CH2_PINS LL_GPIO_PIN_2 | LL_GPIO_PIN_6
      #define SPI_CH2_SCK_PORT GPIOE
      #define SPI_CH2_SCK_PIN LL_GPIO_PIN_2
      #define SPI_CH2_MOSI_PORT GPIOE
      #define SPI_CH2_MOSI_PIN LL_GPIO_PIN_6
      #define SPI_CH2_CLK_ENABLE LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI4)

      (不知道發布后是否可以追加編輯,試試)

      配置SPI外設

      /* Enable GPIO Clock */

      LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOE);

      /* Enable SPI Clock */
      LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI4);


      /* SPI SCK GPIO pin configuration*/
      {LL_GPIO_InitTypeDef io_initStructure_s;

      io_initStructure_s.Pin = SPI_CH2_PINS ;
      io_initStructure_s.Mode = LL_GPIO_MODE_ALTERNATE;
      io_initStructure_s.Speed = LL_GPIO_SPEED_FREQ_MEDIUM;
      io_initStructure_s.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
      io_initStructure_s.Pull = LL_GPIO_PULL_NO;
      io_initStructure_s.Alternate = LL_GPIO_AF_5;
      LL_GPIO_Init(SPI_CH2_PORT , &io_initStructure_s);}


      LL_SPI_Disable(SPI4);
      /* Configure the SPI parameters */

      {LL_SPI_InitTypeDef spi_ch_s;
      spi_ch_s.TransferDirection = LL_SPI_SIMPLEX_TX;
      spi_ch_s.Mode = LL_SPI_MODE_MASTER;
      spi_ch_s.DataWidth = LL_SPI_DATAWIDTH_8BIT;
      spi_ch_s.ClockPolarity = LL_SPI_POLARITY_HIGH;
      spi_ch_s.ClockPhase = LL_SPI_PHASE_2EDGE;
      spi_ch_s.NSS = LL_SPI_NSS_SOFT;

      spi_ch_s.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV4;    /* PLL SET 40MHz clock DIV2 for 20MHz /DIV4 for 10MHz*/
      spi_ch_s.BitOrder = LL_SPI_MSB_FIRST;
      spi_ch_s.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
      LL_SPI_Init(SPI_CH2_SPI , &spi_ch_s);}

      LL_SPI_SetTransferSize(SPI_CH2_SPI , 0);
      LL_SPI_Enable(SPI_CH2_SPI );
      LL_SPI_IsActiveFlag_TXC(SPI_CH2_SPI );
      LL_SPI_StartMasterTransfer(SPI_CH2_SPI );

      使能IO時鐘,SPI時鐘,IO初始化,SPI初始化。

      主要注意 DataWidth = LL_SPI_DATAWIDTH_8BIT; 

      OLED的芯片手冊上沒有限定8BIT還是16BIT,想提高傳輸速度,自然想用16BIT寬度,但是發現有些指令僅8BIT,有些是7*8BIT,所以為了不增加麻煩,退而求其次,選了8

      還有時鐘空閑極性ClockPolarity和數據沿位置ClockPhase 也要按照OLED的要求。雖然設置錯了可能也能驅動,但是在速度變化或者某些特殊數據時就會造成數據不完整,所以一定要按照芯片手冊的要求配置。

      因為4線SPI的OLED一般不提供讀功能,所以SPI方式為僅發送的主機模式,也可以選雙向,區別不大。

       

      這樣,一個由軟件觸發傳輸一次8BIT的SPI就配置好了,可以進一步調試屏幕了。

      (commit)

      屏幕初始化:(部分IO配置簡化表述)

      io_cfgOutput(IO_PE11_O_OLED_CS);

      io_cfgOutput_pull(IO_PE13_O_OLED_A0,LL_GPIO_PULL_DOWN);

      io_cfgOutput(IO_PE15_O_OLED_RESET);

      #define LCD_CS_CLR() io_clrOutput(IO_PE11_O_OLED_CS)
      #define LCD_CS_SET() io_setOutput(IO_PE11_O_OLED_CS)

      #define LCD_RST_CLR() io_clrOutput(IO_PE15_O_OLED_RESET)
      #define LCD_RST_SET() io_setOutput(IO_PE15_O_OLED_RESET)

      #define LCD_RS_CLR() io_clrOutput(IO_PE13_O_OLED_A0)
      #define LCD_RS_SET() io_setOutput(IO_PE13_O_OLED_A0)

      配置除SPI SCK、MOSI以外3個控制IO,其中A0給下拉的原因是由于它在使用中負責數據/指令選擇,瞬間的高低變化可能會引起顯示錯誤,由此給他一個下拉防止積累電荷。

       

      按照屏幕的說明,寫數據和寫指令都是使用SPI發數據,區分點就是那個A0的高低,所以把寫指令 單獨 寫個函數:

      void disp_WriteIndex(BYTE Index)
      {
        while(!LL_SPI_IsActiveFlag_TXC(SPI_CH2_SPI )){}
        LCD_RS_CLR();

        LL_SPI_TransmitData8(SPI_CH2_SPI,Index);
        LL_SPI_StartMasterTransfer(SPI_CH2_SPI);

      }

      這個函數先檢查SPI的發送完成標記,等待其發送完成。再拉低A0線,再向SPI數據寄存器寫入要發送的數據,最后啟動傳輸。因為在傳輸后沒有繼續等待發送完成,因此再次調用SPI發送任何數據、指令之前必須先檢查發送狀態,再調整A0線高低。

       

      BYTE device_display_initialize(BYTE channel_)

      {

      LCD_CS_CLR();
      LCD_RST_SET();
      OS_Delay(5);
      LCD_RST_CLR();
      OS_Delay(5);
      LCD_RST_SET();
      OS_Delay(10);
      disp_WriteIndex(0xAEu); /* DISPLAY OFF */
      disp_WriteIndex(0xACu); /* Color/Gray Mode 0-ColorMode */
      disp_WriteIndex(0x00u); /* --0--for ColorMode,1 for GrayMode */

      disp_WriteIndex(0xFDu); /*removed function MLA--FD ADPS--D6*/
      disp_WriteIndex(0x5Bu); /* ---- */
      disp_WriteIndex(0x00u); /* ---- */
      disp_WriteIndex(0xD6u); /* ---- */

      disp_WriteIndex(0x20u); /* Set refresh direction 2lines command*/
      disp_WriteIndex(0x00u); /* Set horiz refresh*/
      disp_WriteIndex(0xD4u); /* Set color format 2lines command*/
      disp_WriteIndex(0x10u); /* Set 65K RGB */

      disp_WriteIndex(0xA1u);     /* Horizontal Mirror */

      disp_WriteIndex(0xD5u); /* Set Display Clock Divide Ratio/Oscillator Frequency */
      disp_WriteIndex(0x13u); /* --12--V_sync limit from 10MHzSPI 20.8ms trans_time ,0x13 the min speed */

      disp_WriteIndex(0x80u); /* Set Contrast */
      disp_WriteIndex(0xFFu); /* --255--Contrast Max from 0-255*/

      disp_WriteIndex(0xA2u); /* Set Display Start Line */
      disp_WriteIndex(0x00u); /* --00--default*/
      disp_WriteIndex(0x00u); /* --00--default*/

      disp_WriteIndex(0xA8u); /* Set Multiplex Ratio: */
      disp_WriteIndex(0x4Fu); /* --4F--default*/

      disp_WriteIndex(0xADu); /* Set iRef resist */
      disp_WriteIndex(0x00u); /* --04--internal 300uA */

      disp_WriteIndex(0xDDu); /* VSEGH control no use for user */
      disp_WriteIndex(0x1Fu); /* ----VSEGH control no use for user */

      disp_WriteIndex(0xD9u); /* Set Discharge/Pre-charge Period */
      disp_WriteIndex(0x07u); /* ---- */
      disp_WriteIndex(0x0Au); /* ---- */
      disp_WriteIndex(0x0Du); /* ---- */
      disp_WriteIndex(0x0Du); /* ---- */
      disp_WriteIndex(0x0Du); /* ---- */
      disp_WriteIndex(0x0Du); /* ---- */
      disp_WriteIndex(0x29u); /* ---- */

      disp_WriteIndex(0xA4u); /* Set Entire Display hold A5 or normal A4 */

      Lcd_SetRegion(0u,0u,159u,79u);

      disp_WriteIndex(0xAFu); /* DISPLAY ON */
      _Delay(20);

      return 1u;

      }

      其中設置顯示區域由于 假設 要經常調用,

      所以單獨寫了一個函數:

      void Lcd_SetRegion(WORD x_start,WORD y_start,WORD x_end,WORD y_end)
      {
        while(!LL_SPI_IsActiveFlag_TXC(SPI_CH2_SPI)){}
        LCD_RS_CLR();
        disp_WriteIndex(0x22u);
        disp_WriteIndex(0x02u);
        disp_WriteIndex((BYTE)y_start);
        disp_WriteIndex((BYTE)y_end);
        disp_WriteIndex(0x21u);
        disp_WriteIndex(0x01u);
        disp_WriteIndex((BYTE)x_start);
        disp_WriteIndex((BYTE)x_end);
        while(!LL_SPI_IsActiveFlag_TXC(SPI_CH2_SPI)){}
      }

      如果一切正常,執行到這一步,屏幕就能看到點雜亂的顏色了,之后就可以刷新顯示了。

      由于不能從屏幕讀數據,即便能讀也比讀內存慢,所以先開辟一塊顯存,讀寫先到顯存,再整體刷新到屏幕,此思路來自于emWin圖形框架。

      WORD display_ram_wa[80*160]@ "SRAM1_section"={0};

      void Refresh_Gram_Async(void)
      {
        WORD i;
        BYTE mid_b;
        SCB_CleanInvalidateDCache();
        LCD_RS_SET();
        for(i=0u;i<12800u;i++)
        {
          mid_b=(BYTE)(display_ram_wa[i]>>8);
          disp_WriteData((BYTE)(display_ram_wa[i]));
          disp_WriteData(mid_b);
        }
      }

      函數中,SCB_CleanInvalidateDCache();是由于H7的芯片帶有Cache,由于顯存的數據量較大,寫入后立即使用其他方式刷新可能會讀不到正確的內容,所以強制刷新一下Cache。由于屏幕像素160*80*RGB565,所以定義了160*80大小的WORD數組;

      先解釋下RGB565,常規的Windows繪圖軟件可供用戶選擇的顏色其紅綠藍色彩分量各有256檔,能組成256*256*256=16,777,216種色彩,即RGB888格式,每一個8代表8位,可存儲0x00~0xFF共256檔。所以存儲一個RGB888數據需要24位空間,當然也可以用更多的存儲空間存儲顏色,進行更細致的劃分,但是這樣就會增加所需要的內存空間或者調色板尺寸,由于一般的顯示設備,并不需要專業級的色彩細節,所以一般彩色顯示屏提供RGB888,但是對于常規的單片機,24位并不是一個常用的數據類型,16位或者32位才是更常用的,所以用32位存儲RGB888數據當然可以,浪費8位空間,這也是計算機上常規顏色會出現ARGB的原因,富裕出來的8位用來記錄透明度。但是選用32位存儲顏色不僅僅在存儲時增大了空間,還在傳輸時增加了傳輸時間。因此簡單的顯示設備中還提供一種低位寬的顏色格式,RGB565,加和后5+6+5=16,剛好一個WORD,而這種數據僅僅是忽略了各顏色數據的最后兩、三位,還原RGB888顏色時將不夠的位數尾部補0湊夠8位。

      刷新顯存時只需要將每個像素點共12800個WORD發送出去,由于使用的8BIT寬度配置SPI,所以要將WORD拆分成兩塊發送。

      且發送時最好按照先高位后低位的MSB順序,至于為什么后面再說,MSB就導致了顯存中的顏色數據與實際的RGB565有個區別,如下:

      #define Color_RED_MSB565        (0x00F8u)        /* 0xF800-MSB>0x00F8 */

      實際的紅色RGB565格式 應該是0xF800,但是發送時會先發送內存中的地址較低的一位,而屏幕要求傳入的16位顏色數據順序還是RGB,所以要將真實數據前后位交換后再存入顯存,即存入的內容為WORD格式的0x00F8,這樣通過SPI逐byte發送,屏幕收到的順序是0xF8,0x00,才能正確表達位0xF800.

      (commit)

       

       

      void tskDisplay_tsk(void)
      {
          tskDisplay_stack_ba[TASK_DISPALY_STACKSIZE - 1u] = TASK_STACKPROTECT;
          callCount_w                                      = 0u;

          OS_EVENT_CreateEx(&vsyncDisplay_event, OS_EVENT_RESET_MODE_MANUAL); /* globle EVENT */
          initStaticParam();
          while(TRUE)
          {
              if(!display_isConfigured())
              {
                  OS_Delay(1);
                  OS_EVENT_Set(&vsyncDisplay_event);
                  device_close(&displayDevice_s);
                  tskDisplay_on();
              }
              else
              {
                  display_refresh();
                  OS_Delay(tskDisplay_skipFrame_sdw);
              }

              /* Flowcontrol */
              callCount_w++;
              /* Stack analysis */
              if(tskDisplay_stack_ba[TASK_DISPALY_STACKSIZE - 1u] != TASK_STACKPROTECT)
              {
                  tuv_systemReset(TUV_ERROR_CPU_FLOWRTOS, TUV_EMERGENCY_CPU_FLOWRTOS);
              }
          }
      }
       
      后續補充功能,比如運行中更改顯示配置,如亮度、顯示方向,刷新率等,需要重新初始化,就設置了一個 display_isConfigured() 狀態檢測,同時用tskDisplay_skipFrame_sdw來控制刷新率。
       
      由于后續啟用了DMA-SPI傳輸,所以在更改設置之前,要等待當前DMA發送完畢。
      void display_waitForRefresh(void)
      {
          DWORD DMA_TC0_b, DMA_EN_b;

      #if (SPI_DMA_VSYNC_OFF_0_ON_1 == 1)
          /* while(spi_dma_allow){} */
          OS_EVENT_WaitTimed(&vsyncDisplay_event, 100);
      #endif
          WORD timeout_w = 100u;
          do
          {
              DMA_TC0_b = LL_DMA_IsActiveFlag_TC0(SPI_CH2_DMA);
              DMA_EN_b  = LL_DMA_IsEnabledStream(SPI_CH2_DMA, SPI_CH2_DMA_TX_STREAM);
              if((!DMA_TC0_b)  && (DMA_EN_b))
              {
                  OS_Delay(1);
              }

              if(timeout_w) { timeout_w--; }
          }while((!DMA_TC0_b)  && (DMA_EN_b) && timeout_w);
      }
      并把這段放在常規的 display_refresh(); 開頭或結尾。放開頭是由于沒而有預留雙顯存,必須要等前一幀發送結束,才能再在顯存上繪圖。預留雙緩存后就可以放結尾,下一次啟動刷新前等待發送完畢。
       
      void Refresh_Gram(void)
      {
      #if (RTIG_SPI__SOFT_0_DMA_1 == 1)
          SCB_CleanInvalidateDCache();
          LCD_RS_SET();
          #if (SPI_DMA_VSYNC_OFF_0_ON_1 == 1)
          OS_EVENT_Reset(&vsyncDisplay_event);
          /* spi_dma_allow=1u; */
          #elif (SPI_DMA_VSYNC_OFF_0_ON_1 == 0)
          LL_DMA_ClearFlag_TC0(SPI_CH2_DMA);
          LL_DMA_EnableStream(SPI_CH2_DMA, SPI_CH2_DMA_TX_STREAM);
          #endif
      #else
          WORD i;

          BYTE mid_b;

          SCB_CleanInvalidateDCache();
          LCD_RS_SET();
          for(i = 0u; i < 12800u; i++)
          {
              mid_b = (BYTE)(display_ram_wa[i] >> 8);
              disp_WriteData((BYTE)(display_ram_wa[i]));
              disp_WriteData(mid_b);
          }
      #endif
      }

      void Refresh_Gram_ISRHandler(void)
      {
      #if (RTIG_SPI__SOFT_0_DMA_1 == 1)
          SCB_CleanInvalidateDCache();
          LCD_RS_SET();
          LL_DMA_ClearFlag_TC0(SPI_CH2_DMA);
          LL_DMA_EnableStream(SPI_CH2_DMA, SPI_CH2_DMA_TX_STREAM);
      #endif
      }
      單獨這個啟動DMA傳輸,就寫了三種:
      一個同步模式,就是垂直同步,只做標記,等垂直同步信號收到后啟動傳輸。
      一個非同步模式,忽略垂直同步,直接啟動,在刷新較慢的屏幕上會看到圖像撕裂。
      另一個就是垂直同步信號IO外部中斷函數里的啟動傳輸。
       
      寫入緩存的函數,包含清空(統一設置一個顏色),設置、讀取
      void clear_mem_color16(WORD color)
      {
          WORD i;

          for(i = 0u; i < 12800u; i++)
          {
              display_ram_wa[i] = color;
          }
      }

      /** @brief set color to a point in Gram
        * @param WORD color
        * @return void
        */
      void set_mem_16bit(WORD u16color, BYTE x, BYTE y)
      {
      #if    (REFRESH__HORIZ_0_VERTI_1 == 1)
          display_ram_wa[x * 80u + (79u - y)] = u16color;
      #elif  (REFRESH__HORIZ_0_VERTI_1 == 0)
          display_ram_wa[y * 160u + x]        = u16color;
      #endif
      }

      /** @brief get color from a point in Gram
        * @param BYTE x
        * @param BYTE y
        * @return WORD
        */
      WORD get_mem_16bit(BYTE x, BYTE y)
      {
      #if    (REFRESH__HORIZ_0_VERTI_1 == 1)
          return (display_ram_wa[x * 80u + (79u - y)]);
      #elif  (REFRESH__HORIZ_0_VERTI_1 == 0)
          return (display_ram_wa[y * 160u + x]);
      #endif
      }
       
      簡單應用不涉及讀取,讀取是為了后面做透明疊加時運算重疊后顏色用的。
      REFRESH__HORIZ_0_VERTI_1 用來定義屏幕的刷新方向。
      屏幕參數可以配置,是由起始點 --第一行首位 開始橫向刷新到第一行末位 再到 第二行 首位再至 第二行末位 這種橫向刷新
      或者是  第一列首位 刷新至 第一列末位,再換至 第二列首位 刷新至 第二列末位。
      兩種方式看似沒有區別,但實際,在非垂直同步模式下(垂直同步掉幀切需要多硬件配合,簡單功能或靜止圖像不用考慮),如果圖像 沿 行方向移動頻繁,刷新與移動在同一直線,
      圖像撕裂現象明顯。此時如果采用 列模式刷新,圖像移動和刷新方向垂直,非同步引起的撕裂只會將圖像壓縮變緊湊,在動態中不明顯,圖像撕裂現象很不容易被察覺。
       
      下面是常規16位彩色 RGB565顏色轉8位灰度函數,和RGB565前景色背景色按比例透明混合的計算函數。
      BYTE color565ToGray(WORD frontcolor565)
      {
          WORD colo_ = (WORD)((((DWORD)frontcolor565 & 0xC000u) >> 8 | ((DWORD)frontcolor565 & 0x0007u) << 8));

          colo_ = (WORD)(((((DWORD)frontcolor565 & 0x1F00u) >> 7u) * 30u +
                          ((DWORD)colo_ >> 5u) * 150u +

                          (((DWORD)frontcolor565 & 0x00F8u) >> 2u) * 76u
                          ) >> 8u);

          return (BYTE)colo_;
      }

      WORD color565mulitply(WORD cololr565, BYTE mult_, BYTE divi_)
      {
          WORD colo_ = ((((cololr565 & 0xC000u) >> 8 | (cololr565 & 0x0007u) << 8) * mult_ / divi_) & 0x07E0u);

          return (
              (((cololr565 & 0x1F00u) / divi_ * mult_) & 0x1F00u) |
              (colo_ >> 8) | ((colo_ & 0xE0u) << 8) |
              /* (((cololr565&0x07E0u)*mult_/divi_)&0x07E0u) |  */
              (((cololr565 & 0x00F8u) * mult_ / divi_) & 0x00F8u)
              );
      } /* 0xE007,0x1F00 */

      WORD color565mulitplyTransp(WORD frontColor565, WORD backColor565, BYTE mult_, BYTE divi_)
      {
          WORD colo_  = (WORD)(((((DWORD)frontColor565 & 0xC000u) >> 8 | ((DWORD)frontColor565 & 0x0007u) << 8) * mult_ / divi_) & 0x07E0u);
          WORD colob_ = (WORD)(((((DWORD)backColor565 & 0xC000u) >> 8 | ((DWORD)backColor565 & 0x0007u) << 8) * ((DWORD)divi_ - (DWORD)mult_) / divi_) & 0x07E0u);

          colo_ = ((WORD)(((((DWORD)frontColor565 & 0x1F00u) * (WORD)mult_ / (WORD)divi_) & 0x1F00u))
                   | (WORD)(colo_ >> 8u) |
                   ((colo_ & 0xE0u) << 8u) |
                   ((WORD)((((DWORD)frontColor565 & 0x00F8u) * (WORD)mult_ / (WORD)divi_) & 0x00F8u))
                   );

          colob_ = (
              (WORD)((((DWORD)backColor565 & 0x1F00u) * ((DWORD)divi_ - (DWORD)mult_) / divi_) & 0x1F00u) |
              (WORD)(colob_ >> 8) | ((colob_ & 0xE0u) << 8) |
              (WORD)((((DWORD)backColor565 & 0x00F8u) * ((DWORD)divi_ - (DWORD)mult_) / divi_) & 0x00F8u)
              );

          return (colo_ + colob_);
      }

      WORD newcolor565mulitplyTransp(WORD frontColor565, WORD backColor565, BYTE mult_, BYTE divi_)
      {
          DWORD R_dw, G_dw, B_dw;
          DWORD R2_dw, G2_dw, B2_dw;

          R_dw = (((DWORD)frontColor565 & 0x00F8u) * (DWORD)mult_ / (DWORD)divi_) & 0x00F8u;
          if(R_dw > 0x00F8u) { R_dw = 0x00F8u; }

          R2_dw = (((DWORD)backColor565 & 0x00F8u) * ((DWORD)divi_ - (DWORD)mult_) / (DWORD)divi_) & 0x00F8u;
          if(R2_dw > 0x00F8u) { R2_dw = 0x00F8u; }

          R2_dw += R_dw;
          if(R2_dw > 0x00F8u) { R2_dw = 0x00F8u; }

          G_dw = ((((DWORD)frontColor565 & 0xE000u) >> 8 | ((DWORD)frontColor565 & 0x0007u) << 8) * mult_ / divi_) & 0x07E0u;
          if(G_dw > 0x07E0u) { G_dw = 0x07E0u; }

          /* G_dw=(G_dw>>8u) | ((G_dw&0xE0u)<<8u); */
          G2_dw = ((((DWORD)backColor565 & 0xE000u) >> 8 | ((DWORD)backColor565 & 0x0007u) << 8) * ((DWORD)divi_ - (DWORD)mult_) / divi_) & 0x07E0u;
          if(G2_dw > 0x07E0u) { G2_dw = 0x07E0u; }

          G2_dw += G_dw;
          if(G2_dw > 0x07E0u) { G2_dw = 0x07E0u; }

          G2_dw = (G2_dw >> 8u) | ((G2_dw & 0xE0u) << 8u);

          B_dw  = ((((DWORD)frontColor565 & 0x1F00u) * (DWORD)mult_ / (DWORD)divi_) & 0x1F00u);
          if(B_dw > 0x1F00u) { B_dw = 0x1F00u; }

          B2_dw = ((((DWORD)backColor565 & 0x1F00u) * ((DWORD)divi_ - (DWORD)mult_) / divi_) & 0x1F00u);
          if(B2_dw > 0x1F00u) { B2_dw = 0x1F00u; }

          B2_dw += B_dw;
          if(B2_dw > 0x1F00u) { B2_dw = 0x1F00u; }

          return ((WORD)(R2_dw + G2_dw + B2_dw));
      }

       

       

      將顏色或圖片數據繪制到顯存,并進行鏡像翻轉旋轉90°等操作的函數示例,再加入蒙版數據還可以進行透明疊加等動作,函數種類太多,并未來得及整理,就先放一個。

      /** @brief draw PIC at GRAM
        * set color with get original color data
        * Draw a 16 bit grayscale image in the memory buffer, fill the foreground color
        * with (grayscale/16) mask transparency to the background color, and then draw the
        * entire image to the memory according to the specified transparency. (Draw watermark with background color)
        * @param
        * const WORD frontColor565,   ForeColor
        * const WORD backColor565,    --no-used--BackColor
        * const BYTE msk_16gray[],    Brightness mask pointer
        * BYTE max16Gray,             Max brightness
        * SWORD xpos,SWORD ypos,      top left position
        * SWORD width, SWORD height,  size
        * SWORD starwidth, SWORD starheight,  start width when draw,start height when draw
        * SWORD endWidth, SWORD endHeight,    finish width when draw,finish height when draw
        * enum PIC_ROUTE_ANGLE route_0_90_180_270,    rotate
        * enum PIC_MIRROR_ANGLE mirror                mirror
        * @return void
        */
      void drawcolor_mask16grayTrFoTrBaRM(const WORD frontColor565, const WORD backColor565, const BYTE msk_16gray[], BYTE max16Gray, BYTE transparent, SWORD xpos, SWORD ypos, SWORD width, SWORD height, SWORD starwidth, SWORD starheight, SWORD endWidth, SWORD endHeight, enum PIC_ROUTE_ANGLE route_0_90_180_270, enum PIC_MIRROR_ANGLE mirror)
      {
          SWORD j, k, mid_k, mid_j, mid_sw, mid_x, mid_y;
          SWORD org_width = width, org_height = height;
          SWORD mid1_sw = (width % 2);
          BYTE gap_w    = (BYTE)mid1_sw;
          BYTE gray;

          if((X_MAX_PIXEL < xpos) || (Y_MAX_PIXEL < ypos) || (xpos + width < 1) || (ypos + height < 1))
          {}
          else
          {
              if(width + xpos > X_MAX_PIXEL)
              {
                  width = X_MAX_PIXEL - xpos;
              }

              if(height + ypos > Y_MAX_PIXEL)
              {
                  height = Y_MAX_PIXEL - ypos;
              }

              for(j = starheight; j < endHeight; j++)
              {
                  mid_j = j;
                  if(mirror & 0x01u) { mid_j = org_height - 1 - mid_j; } /* 0 1 2 3 4 5 6 7 8 9 ~ drawHeight */

                  if((j >= height) || (j + ypos - 1 < 0))
                  {}
                  else
                  {
                      for(k = starwidth; k < endWidth; k++) /* 0 1 2 3 4 5 6 7 8 9 ~ 29 */
                      {
                          mid_k = k;
                          if((k >= width) || (k + xpos < 0)) {}
                          else
                          {
                              if(mirror & 0x02u) { mid_k = org_width - 1 - mid_k; }

                              /* MIRROR_HORIZ */
                              /* 79 78 77 76 75 74 73 ~ 0 */

                              mid_sw = ((j * (org_width + (SWORD)gap_w) + k));
                              gray   = (msk_16gray[mid_sw / 2] >> ((1 - mid_sw % 2) * 4)) & 0x0fu;
                              if(gray)
                              {
                                  switch(route_0_90_180_270)
                                  {
                                  case ROUTE_0:
                                      mid_x = (xpos + mid_k);
                                      mid_y = (ypos + mid_j);

                                      break;
                                  case ROUTE_90:
                                      mid_x = xpos + (org_height - 1 - (mid_j));
                                      mid_y = ypos + ((mid_k));

                                      break;
                                  case ROUTE_180:
                                      mid_x = xpos + (org_width - 1 - (mid_k));
                                      mid_y = ypos + (org_height - 1 - (mid_j));

                                      break;
                                  case ROUTE_270:
                                      mid_x = xpos + ((mid_j));
                                      mid_y = ypos + (org_width - 1 - (mid_k));

                                      break;
                                  default:
                                      break;
                                  }

                                  if(((WORD)mid_x < MAX_X_PIXELS) && ((WORD)mid_y < MAX_Y_PIXELS))
                                  {
                                      set_mem_16bit(color565mulitplyTransp(
                                                        color565mulitplyTransp(frontColor565, backColor565, gray, max16Gray),
                                                        get_mem_16bit((BYTE)mid_x, (BYTE)mid_y) /* backColor565 */,
                                                        transparent, 0xfu),
                                                    (BYTE)mid_x,
                                                    (BYTE)mid_y
                                                    );
                                  }
                              }
                          }
                      }
                  }
              }
          }
      }

      (commit -- 2024 08 26)

      posted @ 2022-07-07 10:59  華斯基  閱讀(1127)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲精品在线视频自拍| 亚洲高清aⅴ日本欧美视频| 亚洲国产在一区二区三区| 狠狠做五月深爱婷婷天天综合| 麻豆人人妻人人妻人人片av| 日本一区二区三本视频在线观看| 日韩人妻无码一区二区三区综合部 | 日韩在线一区二区每天更新| 久久99国产精品尤物| 午夜免费无码福利视频麻豆| 人妻少妇精品无码专区二区 | 国产精品国产精品无卡区| 成人网站国产在线视频内射视频| 亚洲av无一区二区三区| 精品人妻无码一区二区三区| 国产精品播放一区二区三区| 亚洲日本韩国欧美云霸高清| 亚洲综合一区国产精品| 国产精品成| 免费观看欧美猛交视频黑人| 保康县| 2022最新国产在线不卡a| 欧美激情视频一区二区三区免费| 黑河市| 亚洲精品国产av成拍色拍个| 做暖暖视频在线看片免费| 国内精品无码一区二区三区| 爆乳日韩尤物无码一区| 亚洲产国偷v产偷v自拍色戒| 92精品国产自产在线观看481页| 亚洲天堂成人一区二区三区| 日韩亚洲精品中文字幕| 亚洲香蕉免费有线视频| 色琪琪丁香婷婷综合久久| 国内偷自第一区二区三区| 国产一区二区三区av在线无码观看| 亚洲AV日韩AV激情亚洲| 国产互换人妻xxxx69| 久久婷婷大香萑太香蕉AV人 | 日韩精品亚洲专区在线播放| 亚洲一本二区偷拍精品|