当前位置:   article > 正文

STM32F103+OLED视频播放_iicoled屏幕能放视频吗

iicoled屏幕能放视频吗

目录

1 任务介绍

2 硬件设备

3 脚本工具

4 软件SPI实现W25Q64数据存储与读取

5 硬件IIC实现OLED显示

6 主函数调用

7 实现效果

1 任务介绍

        学完江科协的IIC通信和SPI通信后想趁热打铁实现一个小项目。该项目可以通过OLED实现视频播放(视频无声)。基本思路为通过IIC通信实现OLED显示,通过SPI通信实现数据存储与读取。单靠ARM本身的存储空间是没法存下一个视频的数据,因此需要将视频数据存储在外挂芯片W25Q64中。

        不用担心视频图像取模问题,我这里写了一个脚本可以实现视频转128*64(0.96寸OLED)的文本数据。

2 硬件设备

        STM32F103C8T6最小系统板、0.96寸OLED、W25Q64(可以直接用江科协提供的材料)

3 脚本工具

        视频本质上是播放一帧帧图片,因此在存储视频时需要将视频转换成图片进行存储,且0.96寸OLED的显示像素为128*64,所以对应的图片也必须改成这种尺寸。另外还需要将彩色视频转化为二值图片才能显示。

        视频的数据量比较多,这里我直接写了一个python脚本实现整个视频取模,最终视频其实就变成一个二维数组picture[count][pixs],其中count是视频帧数,pixs为视频像素,0.96寸的OLED其像素固定为128*64,一个数据是8bit,所以pixs=128*8. 运行下面这段代码会在工程目录中多一个array.txt文件,然后将这个文件的数据依次写入W25Q64中(依次最多写入20张图片,如果帧率较高的话要分批写入)。

  1. import os
  2. import cv2
  3. orig_video_path = r'C:\Users\Lenovo\Desktop\Myroject\getpicture\gege.avi'# 原视频路径
  4. save_pic = r'C:\Users\Lenovo\Desktop\Myroject\getpicture\picture\\'
  5. def video():
  6. videoCapture = cv2.VideoCapture(orig_video_path)
  7. f = int(videoCapture.get(cv2.CAP_PROP_FPS))
  8. print('原视频帧率为:'+str(f))
  9. fps = 40 # 保存视频的帧率,可改变
  10. size = (128, 64) # 保存视频大小,必须和OLED分辨率相对应
  11. save_dir = orig_video_path[:-4]+'_new.avi'
  12. videoWriter = cv2.VideoWriter(save_dir,
  13. cv2.VideoWriter_fourcc('D', 'I', 'V', 'X'), fps, size)
  14. while True:
  15. success, frame = videoCapture.read()
  16. if success:
  17. img = cv2.resize(frame, size)
  18. videoWriter.write(img)
  19. else:
  20. print('break')
  21. break
  22. # 释放对象,不然可能无法在外部打开
  23. videoWriter.release()
  24. # 视频转彩色图片
  25. def video_pic():
  26. video_path = orig_video_path[:-4]+'_new.avi'
  27. cap = cv2.VideoCapture(video_path)
  28. sucess = cap.isOpened()
  29. frame_count = 0
  30. i = 0
  31. while sucess:
  32. frame_count += 1
  33. sucess, frame = cap.read()
  34. if(frame is None):
  35. break
  36. if (frame_count % 5 == 0): # 每隔5帧保存一张图片(每帧都保存数据太多了)
  37. i += 1
  38. cv2.imwrite(save_pic+'\\'+str(i)+'.jpg', frame)
  39. cap.release()
  40. #彩色图片转二值图片并保存为txt文件
  41. def pic_txt():
  42. file = open('array.txt', 'w')
  43. pictures = 56 # 需要转换的图片总数量
  44. for p in range(len(os.listdir(save_pic))):
  45. nums = []
  46. file_path = save_pic+str(p)+'.jpg'
  47. img = cv2.imread(file_path, 0)
  48. img[img>140] = 255 # 阈值设置为140,即像素大于140的为0,小于140的为1
  49. img[img<=140] = 1
  50. img[img==255] = 0
  51. # 阳码
  52. # img[img<=140] = 0
  53. # img[img==255] = 1
  54. row, col = img.shape
  55. for i in range(0, row,8):
  56. for j in range(0,col,1):
  57. num = 0
  58. for k in range(8):
  59. num += img[i+k][j]*pow(2,k) # 不能直接加,要乘以2的n次方
  60. nums.append(num)
  61. line = '{'
  62. for i in range(len(nums)):
  63. if(i%32 == 0):
  64. file.write(line+'\n')
  65. print(line)
  66. line = ''
  67. line += str(hex(nums[i]))+','
  68. line = line[:-1]+'\n'
  69. file.write(line)
  70. line = '}, /***** ' + str(p) + ' *****/\n'
  71. file.write(line)
  72. file.close()
  73. if __name__ == '__main__':
  74. video() # 先转换视频尺寸
  75. video_pic() # 将视频转为图片进行保存(当然也可以不保存,这里只是方便分析)
  76. pic_txt() # 图片转文本,用于OLED水平地址模式显示

        当然要是不嫌麻烦也可以用PCtoLCD2002这个软件对图片手动取模,但是这个软件每次只能转一张图片,而且只能转二值bmp格式图片,如果是彩色图片还需要用电脑自带的画图工具修改图片格式,所以不建议使用软件转,如果你非要用软件取字模就按照下面的格式进行设置:

4 软件SPI实现W25Q64数据存储与读取

        如果完成上述步骤,你就可以获得一个array.txt文本文档,这个文本文档里面存放着所有图片数据,把数据定义成二维数组,将这个二维数据一次写入到W25Q64。我一共写了32张图片,每张图片的像素为128*64bit,即128*8byte,所以数组维度为[32][128*8]。SPI实现多张图片的写入函数如下:

W25Q64.c文件,其中

void W25Q64_PageProgram_N(uint32_t Address, uint8_t DataArray[][128*8], uint16_t Count)

函数用于多张图片的写入。Address为写入初始地址,DataArray为图片数组,Count为图片数量。

void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)

用于读取W25Q64数据,Address为读取数据块的初始地址,DataArray为读取数据缓存区,Count为数据总长度,单位为byte 

  1. #include "stm32f10x.h" // Device header
  2. #include "MySPI.h"
  3. #include "W25Q64.h"
  4. #include "W25Q64_Ins.h"
  5. void W25Q64_Init(void)
  6. {
  7. MySPI_Init();
  8. }
  9. void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
  10. {
  11. MySPI_Start(); //开始通信
  12. MySPI_SwapByte(W25Q64_JEDEC_ID);//发送获取设备ID命令
  13. *MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);//接收设备厂商号
  14. *DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);//接收设备高8位ID
  15. *DID <<= 8;
  16. *DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);//接收设备低8位ID
  17. MySPI_Stop();//停止通信
  18. }
  19. void W25Q64_WriteEnable(void)
  20. {
  21. MySPI_Start();
  22. MySPI_SwapByte(W25Q64_WRITE_ENABLE);//发送写指令(SPI固定,所有起始指令后面接的都是控制指令)
  23. MySPI_Stop();
  24. }
  25. void W25Q64_WaitBusy(void) //读状态寄存器,等待读写就绪态
  26. {
  27. uint32_t Timeout;
  28. MySPI_Start();
  29. MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);
  30. Timeout = 1000000;
  31. while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)
  32. {
  33. Timeout --;
  34. if (Timeout == 0) //等待超时退出
  35. {
  36. break;
  37. }
  38. }
  39. MySPI_Stop();
  40. }
  41. /*
  42. function 向W25Q46写入存储数据,用于写入一张图
  43. @param Address 写入地址
  44. @param DataArray 写入数据
  45. @param Count 一张图的数据长度一般为128*8
  46. */
  47. void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
  48. {
  49. uint16_t i,j=0;
  50. uint16_t L = Count / 256; //一次数据传输最多256字节,第二次要重新写入地址
  51. uint16_t M = Count % 256;
  52. uint32_t Address1 = Address;
  53. //开始传输数据
  54. while(L > j)
  55. {
  56. W25Q64_WriteEnable();
  57. Address1 = (uint32_t)(Address + (256*j));
  58. MySPI_Start();
  59. MySPI_SwapByte(W25Q64_PAGE_PROGRAM);
  60. MySPI_SwapByte(Address1 >> 16);//每次发8字节,但是地址一共24字节,所以发三次,先发送高8位
  61. MySPI_SwapByte(Address1 >> 8);
  62. MySPI_SwapByte(Address1);
  63. for (i = 0; i < 256; i ++)
  64. {
  65. MySPI_SwapByte(DataArray[i + 256*j]);
  66. }
  67. MySPI_Stop();
  68. W25Q64_WaitBusy();
  69. j++;
  70. }
  71. W25Q64_WriteEnable();
  72. Address1 = (uint32_t)(Address + (256*j));
  73. MySPI_Start();
  74. MySPI_SwapByte(W25Q64_PAGE_PROGRAM);
  75. MySPI_SwapByte(Address1 >> 16);//每次发8字节,但是地址一共24字节,所以发三次,先发送高8位
  76. MySPI_SwapByte(Address1 >> 8);
  77. MySPI_SwapByte(Address1);
  78. for (i = 0; i < M; i ++)
  79. {
  80. MySPI_SwapByte(DataArray[i + 256*j]);
  81. }
  82. MySPI_Stop();
  83. W25Q64_WaitBusy();
  84. }
  85. /*
  86. function 向W25Q46写入存储数据,用于写入一张图
  87. @param Address 写入地址
  88. @param DataArray 写入数据
  89. @param Count 图片数量,图片默认像素为128*64
  90. */
  91. void W25Q64_PageProgram_N(uint32_t Address, uint8_t DataArray[][128*8], uint16_t Count)
  92. {
  93. int i;
  94. uint32_t temp_address = Address;
  95. //一张图1KB,一个扇区最多存放4张图,每次最多擦除一个扇区
  96. uint16_t pages = Count / 4;
  97. pages += Count % 4;
  98. //先擦除扇区
  99. for(i = 0; i<pages; i++)
  100. {
  101. W25Q64_SectorErase(temp_address);
  102. uint32_t temp1_address = temp_address;
  103. for(int j=0; j<4; j++)
  104. {
  105. W25Q64_PageProgram(temp1_address, DataArray[i*4+j], 128*8);
  106. temp1_address += 0x000400; //每写入一张图片,向前移动1024byte
  107. }
  108. temp_address += 0x001000;
  109. }
  110. }
  111. /*
  112. function 擦除存储器数据
  113. @param Address 需要擦除的扇区地址,一个扇区
  114. */
  115. void W25Q64_SectorErase(uint32_t Address)
  116. {
  117. W25Q64_WriteEnable();
  118. MySPI_Start();
  119. MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB); //发送擦除指令,每次擦除一个扇区
  120. MySPI_SwapByte(Address >> 16); //擦除地址
  121. MySPI_SwapByte(Address >> 8);
  122. MySPI_SwapByte(Address);
  123. MySPI_Stop();
  124. W25Q64_WaitBusy();
  125. }
  126. /*
  127. function 从W25Q46发送读取数据
  128. @param Address 读取起始地址地址
  129. @param DataArray 数据缓冲
  130. @param Count 读取数据长度
  131. */
  132. void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
  133. {
  134. uint32_t i;
  135. MySPI_Start();
  136. MySPI_SwapByte(W25Q64_READ_DATA);
  137. MySPI_SwapByte(Address >> 16);
  138. MySPI_SwapByte(Address >> 8);
  139. MySPI_SwapByte(Address);
  140. for (i = 0; i < Count; i ++)
  141. {
  142. DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);
  143. }
  144. MySPI_Stop();
  145. }

5 硬件IIC实现OLED写入

  1. #include "OLED_IIC.h"
  2. void My_Init(void)
  3. {
  4. RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
  5. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
  6. GPIO_InitTypeDef GPIO_InitStructure;
  7. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
  8. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
  9. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  10. GPIO_Init(GPIOB, &GPIO_InitStructure);
  11. I2C_InitTypeDef I2C_InitStructure;
  12. I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  13. I2C_InitStructure.I2C_ClockSpeed = 400000;
  14. I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
  15. I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  16. I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  17. I2C_InitStructure.I2C_OwnAddress1 = 0x30;
  18. I2C_Init(I2C2, &I2C_InitStructure);
  19. I2C_Cmd(I2C2, ENABLE);
  20. }
  21. //发送一个字节数据
  22. void My_OLED_WriteData(uint16_t addr, uint16_t data)
  23. {
  24. while(I2C_GetFlagStatus(I2C2,I2C_FLAG_BUSY));
  25. I2C_GenerateSTART(I2C2,ENABLE);
  26. while(!I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT));
  27. I2C_Send7bitAddress(I2C2,0X78,I2C_Direction_Transmitter);
  28. while(!I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
  29. I2C_SendData(I2C2, addr);
  30. while(!I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING));
  31. //I2C_EVENT_MASTER_BYTE_TRANSMITTING
  32. I2C_SendData(I2C2, data);
  33. while(!I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING));
  34. I2C_GenerateSTOP(I2C2,ENABLE);
  35. }
  36. //写命令
  37. void My_WriteCmd(unsigned char cmoodcmd)
  38. {
  39. My_OLED_WriteData(0x00,cmoodcmd);
  40. }
  41. //写数据
  42. void My_WriteDATA(unsigned char ic2data)
  43. {
  44. My_OLED_WriteData(0x40,ic2data);
  45. }
  46. //设置光标
  47. void My_Starting_point(unsigned char x,unsigned char y)
  48. {
  49. My_WriteCmd(0xb0+y);
  50. My_WriteCmd((x&0xf0)>>4|0x10); //1111 0000 ->0000 1111|0X10=0001 1111
  51. My_WriteCmd((x&0x0f)|0x01);//0000 1111 | 0000 0001 =0000 1111
  52. }
  53. void oled_fill(unsigned char Flii_data)
  54. {
  55. uint8_t i, j;
  56. for (j = 0; j < 8; j++)
  57. {
  58. My_Starting_point(j, 0);
  59. for(i = 0; i < 128; i++)
  60. {
  61. My_WriteDATA(0x00);
  62. }
  63. }
  64. }
  65. //清屏
  66. void My_OLED_Clear(void)
  67. {
  68. oled_fill(0x00);
  69. }
  70. void My_OLED_Init()
  71. {
  72. uint32_t i, j;
  73. for (i = 0; i < 1000; i++) //上电延时
  74. {
  75. for (j = 0; j < 1000; j++);
  76. }
  77. My_Init(); //端口初始化
  78. My_WriteCmd(0xAE); //关闭显示
  79. My_WriteCmd(0xD5); //设置显示时钟分频比/振荡器频率
  80. My_WriteCmd(0x80);
  81. My_WriteCmd(0xA8); //设置多路复用率
  82. My_WriteCmd(0x3F);
  83. My_WriteCmd(0xD3); //设置显示偏移
  84. My_WriteCmd(0x00);
  85. My_WriteCmd(0x40); //设置显示开始行
  86. My_WriteCmd(0xA1); //设置左右方向,0xA1正常 0xA0左右反置
  87. My_WriteCmd(0xC8); //设置上下方向,0xC8正常 0xC0上下反置
  88. My_WriteCmd(0xDA); //设置COM引脚硬件配置
  89. My_WriteCmd(0x12);
  90. My_WriteCmd(0x81); //设置对比度控制
  91. My_WriteCmd(0xCF);
  92. My_WriteCmd(0xD9); //设置预充电周期
  93. My_WriteCmd(0xF1);
  94. My_WriteCmd(0xDB); //设置VCOMH取消选择级别
  95. My_WriteCmd(0x30);
  96. My_WriteCmd(0xA4); //设置整个显示打开/关闭
  97. My_WriteCmd(0xA6); //设置正常/倒转显示
  98. My_WriteCmd(0x20); //修改地址模式为水平模式
  99. My_WriteCmd(0x00);
  100. My_WriteCmd(0x8D); //设置充电泵
  101. My_WriteCmd(0x14);
  102. My_WriteCmd(0xAF); //开启显示
  103. My_WriteCmd(0x2E); //关闭滚动显示
  104. My_OLED_Clear(); //OLED清屏
  105. }

6 主函数

  1. uint8_t ArrayRead[128*8] = {0};
  2. //void begin_show(void);
  3. void twinkle_show(uint8_t pic[], uint16_t length, uint16_t width);
  4. int main(void)
  5. {
  6. // OLED_Init();
  7. My_OLED_Init();
  8. W25Q64_Init();
  9. // W25Q64_PageProgram_N(0x000000, OLED_PIC2, 20); //SPI写图片数据
  10. uint32_t temp_address = 0x000000;
  11. int i =0,j=0 ;
  12. for(i=0; i<32; i++)
  13. {
  14. My_OLED_Clear();
  15. My_Starting_point(0, 0);
  16. // OLED_Clear();
  17. // OLED_SetCursor(0, 0);
  18. W25Q64_ReadData(temp_address, ArrayRead, 128*8); //每次读一张图片
  19. // twinkle_show(ArrayRead, 128, 64);
  20. for(j=0; j<128 *8; j++)
  21. {
  22. My_WriteDATA(ArrayRead[j]); //硬件IIC写OLED
  23. // OLED_WriteData(ArrayRead[j]);//软件IIC写OLED
  24. }
  25. Delay_ms(20);
  26. temp_address += 0x000400;
  27. }
  28. while (1)
  29. {
  30. }
  31. }

7 实现效果

视频没法上传,这里实现效果直接贴出b站UP主的效果,如有侵权请及时联系。

丝滑单片鸡 STM32F103C8+0.96寸OLED_哔哩哔哩_bilibili

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/676780
推荐阅读
相关标签
  

闽ICP备14008679号