本帖最后由 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/
大家可以拿去播放任意图片或者小视频,小屏观看也很快乐!
|