yang377156216 发表于 2022-7-22 09:38

【N32G430开发板试用】基于OLED ToolBox 同步显示图片和视频

本帖最后由 yang377156216 于 2022-7-22 09:40 编辑

@安小芯@21小跑堂
在B站上看到AnChangNice大神制作过一个OLED ToolBox上位机软件,结合下位机可以将上位机打开的图片、正在播放的视频、选中的截屏区域实时同步显示到下位机驱动的OLED显示屏上。本文通过上位机的OLED ToolBox软件结合国民的N32G430C8L7-STB开发板驱动SPI接口的OLED实现同步显示图片和视频的功能。全篇分为OLED ToolBox环境部署、硬件环境介绍、功能实现方案、底层驱动实现、以及最后的成品效果演示。OLED ToolBox环境部署OLED ToolBox视频:https://www.bilibili.com/video/BV1v7411A7sF?spm_id_from=333.999.0.0&vd_source=c2d3413b74687345dfc8205a8d735598OLED ToolBox说明:https://www.bilibili.com/read/cv5097904?spm_id_from=333.999.0.0OLED ToolBox开源代码:https://github.com/AnChangNice/oled_display_gui
我们通过GitHub下载OLED ToolBox源代码,在解压的oled_display_gui文件夹下存放了3个文件夹,分别是Doc、mcu_prj和python_gui,还有一个README说明文件。Doc文件夹存放了一些图片文件和OLED手册;mcu_prj是基于STM32最小系统板的一个演示DEMO,其源码工程是基于GCC环境开发的;最后python_gui文件夹下存放的是OLED ToolBox的源代码,它是基于Python环境开发的上位机应用软件,所以我们需要参考README文件先来部署开发环境,包含安装Python以及需要使用到的软件插件包。根据README文件中的描述,我们需要先安装Python3.x的版本,并且是32位版本(这个在README中没有描述过),如果是64位版本的会发现环境部署完成后程序运行报错,所以这边需要注意!接下来我们需要安装相应的组件,包含pyserial、numpy、opencv-python、PyQt5和pywin32,我们可以手动的通过pip install xxx命令一个一个来安装,也可以通过python_gui\tools文件夹下的environment_setup.py来自动安装。在安装组件之前,建议先执行一下python get-pip.py这个文件,这个是将pip的版本更新到最新的版本,因为在后面安装组件的时候,可能会提示pip的版本过低,导致组件安装不成功的现象。通过执行python environment_setup.py来自动安装的过程是在线下载组件的方式,如果下载速度过慢会导致组件安装失败,如果出现这种情况,可以到https://pypi.org/这个网站上先下载好编译后的组件包(WHL)文件,然后离线来安装这些组件。等Python和组件包都安装完成后,我们就可以通过执行python main.py来运行OLED ToolBox上位机软件了,软件主界面如下所示:

硬件环境介绍硬件准备了N32G430C8L7-STB开发板、0.96” SPI接口通讯的OLED显示屏、USB转TLL调试工具,如下图所示:我们通过N32G430C8L7-STB开发板的端口引脚扩展针,将OLED显示屏连接到开发板上的SPI接口,将USB转TTL调试工具连接到开发板上的USART2,具体引脚分配如下表所示:
OLED <-> N32G430C8L7-STB
OLEDN32G430C8L7-STBFunction
GNDGNDGND
VCC3.3VVCC
SCLPB13SPI_SCK
SDAPB15SPI_MOSI
RESPB10GPIO
DCPB11GPIO
CSPB12SPI_NSS


USB转TTL <-> N32G430C8L7-STB
USB转TTLN32G430C8L7-STBFunction
GNDGNDGND
RXDPA2USART_TX
TXDPA3USART_RX

功能实现方案OLED ToolBox上位机软件经过处理将OLED显示数据通过USB转TTL调试工具以串口的方式传输给N32G430C8L7-STB开发板上的USART2,为了达到串行数据高速通讯,在N32G430C8L7端使用DMA的方式接收数据,将OLED显示数据缓存在内存数组中,再结合1Mbps的串口通讯速率和双数据缓存的乒乓操作,达到接收显示数据和更新OLED显示流畅的效果。OLED使用硬件SPI接口进行驱动,通过设置SPI的分频来调整SPI的传输速率,实现迅速更新显示缓存的功能。功能框图如下所示:底层驱动实现代码如下:void OLED_WriteCommand(uint8_t Command)
{
    OLED_DC_L();
    OLED_CS_L();
    OLED_SPI_WriteByte(Command);
    OLED_CS_H();
}

void OLED_WriteData(uint8_t *Buffer, uint32_t Length)
{
    OLED_DC_H();
    OLED_CS_L();

    while(Length--)
    {
      OLED_SPI_WriteByte(*Buffer++);
    }

    OLED_CS_H();
}

void OLED_HardwareReset(void)
{
    OLED_RST_H(); SysTick_DelayMS(100);
    OLED_RST_L(); SysTick_DelayMS(100);
    OLED_RST_H(); SysTick_DelayMS(100);
}

void OLED_ConfigureREGn(void)
{
    OLED_WriteCommand(0xAE);
    OLED_WriteCommand(0x20);
    OLED_WriteCommand(0x00);
    OLED_WriteCommand(0xB0);
    OLED_WriteCommand(0xC8);
    OLED_WriteCommand(0x00);
    OLED_WriteCommand(0x10);
    OLED_WriteCommand(0x40);
    OLED_WriteCommand(0x81);
    OLED_WriteCommand(0xFF);
    OLED_WriteCommand(0xA1);
    OLED_WriteCommand(0xA6);
    OLED_WriteCommand(0xA8);
    OLED_WriteCommand(0x3F);
    OLED_WriteCommand(0xA4);
    OLED_WriteCommand(0xD3);
    OLED_WriteCommand(0x00);
    OLED_WriteCommand(0xD5);
    OLED_WriteCommand(0xF0);
    OLED_WriteCommand(0xD9);
    OLED_WriteCommand(0x22);
    OLED_WriteCommand(0xDA);
    OLED_WriteCommand(0x12);
    OLED_WriteCommand(0xDB);
    OLED_WriteCommand(0x20);
    OLED_WriteCommand(0x8D);
    OLED_WriteCommand(0x14);
    OLED_WriteCommand(0xAF);
}

void OLED_Configure(void)
{
    OLED_HardwareReset();

    OLED_ConfigureREGn();
}OLED SPI显示驱动实现代码如下:void OLED_InitGPIO(void)
{
    GPIO_InitType GPIO_InitStructure;

    RCC_AHB_Peripheral_Clock_Enable(RCC_AHB_PERIPH_GPIOB);

    GPIO_Structure_Initialize(&GPIO_InitStructure);
    GPIO_InitStructure.Pin       = GPIO_PIN_10 | GPIO_PIN_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_OUT_PP;
    GPIO_Peripheral_Initialize(GPIOB, &GPIO_InitStructure);
}

void OLED_InitSPI2(void)
{
    GPIO_InitType GPIO_InitStructure;
    SPI_InitTypeSPI2_InitStructure;

    RCC_APB2_Peripheral_Clock_Enable(RCC_APB2_PERIPH_SPI2);
    RCC_AHB_Peripheral_Clock_Enable( RCC_AHB_PERIPH_GPIOB);

    GPIO_Structure_Initialize(&GPIO_InitStructure);
    GPIO_InitStructure.Pin       = GPIO_PIN_12;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_OUT_PP;
    GPIO_Peripheral_Initialize(GPIOB, &GPIO_InitStructure);

    OLED_CS_H();

    GPIO_Structure_Initialize(&GPIO_InitStructure);
    GPIO_InitStructure.Pin            = GPIO_PIN_13 | GPIO_PIN_15;
    GPIO_InitStructure.GPIO_Mode      = GPIO_MODE_AF_PP;
    GPIO_InitStructure.GPIO_Alternate = GPIO_AF1_SPI2;
    GPIO_Peripheral_Initialize(GPIOB, &GPIO_InitStructure);

    SPI_I2S_Reset(SPI2);

    SPI_Initializes_Structure(&SPI2_InitStructure);
    SPI2_InitStructure.DataDirection = SPI_DIR_SINGLELINE_TX;
    SPI2_InitStructure.SpiMode       = SPI_MODE_MASTER;
    SPI2_InitStructure.DataLen       = SPI_DATA_SIZE_8BITS;
    SPI2_InitStructure.CLKPOL      = SPI_CLKPOL_LOW;
    SPI2_InitStructure.CLKPHA      = SPI_CLKPHA_FIRST_EDGE;
    SPI2_InitStructure.NSS         = SPI_NSS_SOFT;
    SPI2_InitStructure.BaudRatePres= SPI_BR_PRESCALER_2;
    SPI2_InitStructure.FirstBit      = SPI_FB_MSB;
    SPI2_InitStructure.CRCPoly       = 7;
    SPI_Initializes(SPI2, &SPI2_InitStructure);

    SPI_NSS_Config(SPI2, SPI_NSS_SOFT);
    SPI_Set_Nss_Level(SPI2, SPI_NSS_HIGH);

    SPI_ON(SPI2);
}


void OLED_SPI_WriteByte(uint8_t Data)
{
    SPI_I2S_Data_Transmit(SPI2, Data);
    while(SPI_I2S_Flag_Status_Get(SPI2, SPI_I2S_FLAG_TE)   == RESET);

    while(SPI_I2S_Flag_Status_Get(SPI2, SPI_I2S_FLAG_BUSY) != RESET);
}USART初始化及DMA接收实现代码如下:void OLED_USART_Receive_DMA(uint8_t SelectIndex)
{
    DMA_InitTypeDMA_InitStructure;
    NVIC_InitType NVIC_InitStructure;

    RCC_AHB_Peripheral_Clock_Enable(RCC_AHB_PERIPH_DMA);

    DMA_Reset(DMA_CH4);

    DMA_Structure_Initializes(&DMA_InitStructure);
    DMA_InitStructure.PeriphAddr   = (uint32_t)(USART2_BASE + 0x04);
    DMA_InitStructure.MemAddr      = (uint32_t)OLED_USART_RxBuffer;
    DMA_InitStructure.Direction      = DMA_DIR_PERIPH_SRC;
    DMA_InitStructure.BufSize      = 1024;
    DMA_InitStructure.PeriphInc      = DMA_PERIPH_INC_MODE_DISABLE;
    DMA_InitStructure.MemoryInc      = DMA_MEM_INC_MODE_ENABLE;
    DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_WIDTH_BYTE;
    DMA_InitStructure.MemDataSize    = DMA_MEM_DATA_WIDTH_BYTE;
    DMA_InitStructure.CircularMode   = DMA_CIRCULAR_MODE_DISABLE;
    DMA_InitStructure.Priority       = DMA_CH_PRIORITY_HIGHEST;
    DMA_InitStructure.Mem2Mem      = DMA_MEM2MEM_DISABLE;
    DMA_Initializes(DMA_CH4, &DMA_InitStructure);

    DMA_Channel_Request_Remap(DMA_CH4, DMA_REMAP_USART2_RX);

    NVIC_InitStructure.NVIC_IRQChannel = DMA_Channel4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Initializes(&NVIC_InitStructure);

    DMA_Interrupts_Enable(DMA_CH4, DMA_INT_TXC);

    DMA_Channel_Enable(DMA_CH4);
}

void DMA_Channel4_IRQHandler(void)
{
    if(DMA_Interrupt_Status_Get(DMA, DMA_CH4_INT_TXC) != RESET)
    {
      OLED_USART_RxCompleteDMA = 1;
      DMA_Interrupt_Status_Clear(DMA, DMA_CH4_INT_TXC);
      DMA_Channel_Disable(DMA_CH4);
    }
}


void OLED_InitUSART(void)
{
    GPIO_InitType   GPIO_InitStructure;
    USART_InitTypeUSART_InitStructure;

    RCC_AHB_Peripheral_Clock_Enable(RCC_AHB_PERIPH_GPIOA);
    RCC_APB1_Peripheral_Clock_Enable(RCC_APB1_PERIPH_USART2);

    GPIO_Structure_Initialize(&GPIO_InitStructure);
    GPIO_InitStructure.Pin            = GPIO_PIN_2 | GPIO_PIN_3;
    GPIO_InitStructure.GPIO_Mode      = GPIO_MODE_AF_PP;
    GPIO_InitStructure.GPIO_Alternate = GPIO_AF5_USART2;
    GPIO_Peripheral_Initialize(GPIOA, &GPIO_InitStructure);

    USART_Structure_Initializes(&USART_InitStructure);
    USART_InitStructure.BaudRate            = 1000000;
    USART_InitStructure.WordLength          = USART_WL_8B;
    USART_InitStructure.StopBits            = USART_STPB_1;
    USART_InitStructure.Parity            = USART_PE_NO;
    USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
    USART_InitStructure.Mode                = USART_MODE_RX | USART_MODE_TX;
    USART_Initializes(USART2, &USART_InitStructure);

    USART_DMA_Transfer_Enable(USART2, USART_DMAREQ_RX);

    USART_Enable(USART2);
}OLED ToolBox交互处理实现代码如下:void OLED_Handler(void)
{
    char Buffer;

    if(OLED_USART_RxCompleteDMA == 1)
    {
      sprintf(Buffer, "time : %d ms\n", SysTick_Tick);
      OLED_USART_SendString(Buffer);

      uint8_t Index = OLED_USART_RxBufferIndex;

      OLED_USART_RxBufferIndex += 1;
      OLED_USART_RxBufferIndex %= 2;

      OLED_USART_RxCompleteDMA= 0;
      OLED_USART_Receive_DMA(OLED_USART_RxBufferIndex);

      OLED_UpdateScreenWithBuffer(OLED_USART_RxBuffer);
    }
}
成品效果演示,请移步哔站链接:https://www.bilibili.com/video/BV1tT411E719/
大家可以拿去播放任意图片或者小视频,小屏观看也很快乐!{:lol:}


xld0932 发表于 2022-7-22 10:01

原来OLED还可以这么玩呀……

xld0932 发表于 2022-8-31 16:52

https://www.bilibili.com/video/BV1tT411E719/

Afanx 发表于 2022-8-31 18:38

大佬牛啊

月亮 发表于 2022-8-31 19:49

发源码的都强烈支持!!!

pklong 发表于 2022-9-3 16:31

这个的效果看着不错。   

cemaj 发表于 2022-9-3 17:30

ToolBox 包含了哪些功能呢

belindagraham 发表于 2022-9-3 20:12

OLED ToolBox有什么用呢   

pklong 发表于 2022-9-3 21:31

这个使用什么开发的上位机呢

soodesyt 发表于 2022-9-4 22:20

这个使用了什么通信协议呢   

Stahan 发表于 2022-9-5 22:46

厉害啊,楼主给提供了个led新思路

usysm 发表于 2022-12-5 21:26

使用串口接收数据的吗?            

modesty3jonah 发表于 2022-12-5 21:46

怎么保证接收数据的完整性呢?            

hudi008 发表于 2022-12-5 22:27

OLED ToolBox是什么软件?

i1mcu 发表于 2022-12-6 15:11

如何判断一帧图片接收完成?            

abotomson 发表于 2022-12-6 17:00

可以实现图片的压缩吗?            
页: [1]
查看完整版本: 【N32G430开发板试用】基于OLED ToolBox 同步显示图片和视频