打印
[活动专区]

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

[复制链接]
1427|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 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=c2d3413b74687345dfc8205a8d735598
OLED ToolBox说明:
https://www.bilibili.com/read/cv5097904?spm_id_from=333.999.0.0
OLED 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
OLED
N32G430C8L7-STB
Function
GND
GND
GND
VCC
3.3V
VCC
SCL
PB13
SPI_SCK
SDA
PB15
SPI_MOSI
RES
PB10
GPIO
DC
PB11
GPIO
CS
PB12
SPI_NSS

USBTTL <-> N32G430C8L7-STB
USBTTL
N32G430C8L7-STB
Function
GND
GND
GND
RXD
PA2
USART_TX
TXD
PA3
USART_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_InitType  SPI2_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_InitType  DMA_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[SelectIndex];
    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_InitType  USART_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[32];

    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[Index]);
    }
}

成品效果演示,请移步哔站链接:
https://www.bilibili.com/video/BV1tT411E719/

大家可以拿去播放任意图片或者小视频,小屏观看也很快乐!

OLED_ToolBox_OK.zip (3.57 MB)


使用特权

评论回复
沙发
xld0932| | 2022-7-22 10:01 | 只看该作者
原来OLED还可以这么玩呀……

使用特权

评论回复
板凳
xld0932| | 2022-8-31 16:52 | 只看该作者

使用特权

评论回复
地板
Afanx| | 2022-8-31 18:38 | 只看该作者
大佬牛啊

使用特权

评论回复
5
月亮| | 2022-8-31 19:49 | 只看该作者
发源码的都强烈支持!!!

使用特权

评论回复
6
pklong| | 2022-9-3 16:31 | 只看该作者
这个的效果看着不错。   

使用特权

评论回复
7
cemaj| | 2022-9-3 17:30 | 只看该作者
ToolBox 包含了哪些功能呢

使用特权

评论回复
8
belindagraham| | 2022-9-3 20:12 | 只看该作者
OLED ToolBox有什么用呢   

使用特权

评论回复
9
pklong| | 2022-9-3 21:31 | 只看该作者
这个使用什么开发的上位机呢  

使用特权

评论回复
10
soodesyt| | 2022-9-4 22:20 | 只看该作者
这个使用了什么通信协议呢   

使用特权

评论回复
11
Stahan| | 2022-9-5 22:46 | 只看该作者
厉害啊,楼主给提供了个led新思路

使用特权

评论回复
12
usysm| | 2022-12-5 21:26 | 只看该作者
使用串口接收数据的吗?              

使用特权

评论回复
13
modesty3jonah| | 2022-12-5 21:46 | 只看该作者
怎么保证接收数据的完整性呢?              

使用特权

评论回复
14
hudi008| | 2022-12-5 22:27 | 只看该作者
OLED ToolBox是什么软件?

使用特权

评论回复
15
i1mcu| | 2022-12-6 15:11 | 只看该作者
如何判断一帧图片接收完成?              

使用特权

评论回复
16
abotomson| | 2022-12-6 17:00 | 只看该作者
可以实现图片的压缩吗?              

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

38

主题

235

帖子

10

粉丝