本帖最后由 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,具体引脚分配如下表所示:
USB转TTL <-> N32G430C8L7-STB | | | | | | | | | | | | |
功能实现方案 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/
大家可以拿去播放任意图片或者小视频,小屏观看也很快乐!
|