目录
0 引言
有幸做过一个无线传输的设计,有关BMP文件格式在STM32上的解析,更难的在于文件中图片的像素值与屏幕的一一对应关系,在这里跟大家分享出来。
1 硬件设计
这里没有什么难点,简要说明。
- STM32主控与屏幕使用IIC总线连接。
- STM32主控与ESP-01S无线透传模块通过串口进行连接。
- ULN2003驱动步进电机。(这里不赘述)
框图:
2 软件设计
直接上代码。
main.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "step_motor.h"
#include "oled.h"
#include "bmp.h"
#include "led.h"
#include "common.h"
#include "malloc.h"
#include "bitmap.h"
int main(void)
{
vu16 t = 0,cnt = 0;
u8 constate=0; //连接状态
u8 res = 0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
delay_init(); //延时函数初始化
uart_init(115200); //串口初始化为115200
LED_Init();
Step_Motor_GPIO_Init();
mem_init(); //初始化内存池
OLED_Init();
OLED_ColorTurn(0);//0正常显示,1 反色显示
OLED_DisplayTurn(1);//0正常显示 1 屏幕翻转显示
//--------------------开始WIFI配置----------------------//
while(atk_8266_send_cmd("AT","OK",20))//检查WIFI模块是否在线
{
atk_8266_quit_trans();//退出透传
atk_8266_send_cmd("AT+CIPMODE=0","OK",20); //关闭透传模式
delay_ms(800);
}
delay_ms(200);
while(atk_8266_send_cmd("ATE0","OK",20));//关闭回显
atk_8266_wifista_tcp();
//--------------------结束WIFI配置----------------------//
/* OLED_ShowChinese(0,0,0,16,1);
OLED_ShowChinese(16,0,1,16,1);
OLED_ShowChinese(32,0,2,16,1);*/
OLED_ShowString(0,0,"SSID:ESP8266",16,1);
OLED_ShowString(0,16,"KEY:1234567890",16,1);
OLED_ShowString(0,32,"IP:192.168.4.1",16,1);
OLED_ShowString(0,48,"Port:8086",16,1);
while(1)
{
delay_ms(10);
OLED_Refresh();
if(USART_RX_STA&0X8000) //接收到一次数据了
{
res = getDataFromUart();// +IPD,0,1088:BM>......
if(res)//若收到数据
{
res = 0;
getBitmapInfo(bitmap_buf);
}
// rlen=USART_RX_STA&0X7FFF; //得到本次接收到的数据长度
// USART_RX_BUF[rlen]=0; //添加结束符
// printf("%s",USART_RX_BUF); //发送到串口
//sprintf((char*)p,"收到%d字节,内容如下",rlen);//接收到的字节数
USART_RX_STA=0;
if(constate!='+')cnt=200; //状态为还未连接,立即更新连接状态
else cnt=0; //状态为已经连接了,10秒后再检查
}
cnt++;
t++;
if(cnt>=200)//连续10秒钟没有收到任何数据,检查连接是不是还存在.
{
constate=atk_8266_consta_check();//得到连接状态
cnt=0;
}
if(t==10)
{
t = 0;
LED = !LED;
}
// OLED_Clear();
}
}
bitmap.c
#include "malloc.h"
#include "bitmap.h"
#include "oled.h"
u8 bitmap_buf[1088];
/****获取bmp格式文件信息*********/
/* 返回值 0 成功 */
/* 1 失败 */
/* 入口参数 缓冲区首地址*/
/********************************/
u16 getBitmapInfo(u8 *ptr)
{
BITMAP_FILE_HEADER *fileHead;//文件头
BITMAP_INFO_HEADER *infoHead;//信息头
vu8 buf;
vu8 buff=0x00;
u8 *display_buf;
u8 *p;
volatile int i,j,k,l;
fileHead = (BITMAP_FILE_HEADER*)ptr;
infoHead = (BITMAP_INFO_HEADER*)(ptr + sizeof(BITMAP_FILE_HEADER));
if(fileHead->bfType != 0x4D42)//验证文件头
{
return 1;//返回失败
}
if(infoHead->biWidth != 128 || infoHead->biHeight != 64)//验证图片大小
{
return 1;//返回失败
}
display_buf = (u8*)mymalloc(infoHead->biSizeImage);//申请1024字节的内存用于存储
p = display_buf;
for(i=63;i>=0;i=i-8)//这里将bmp格式转换为oled可以识别的字节顺序
{
for(j=0;j<16;j++)
{
for(l = 0;l<8;l++)
{
buff = 0;
for(k=0;k<8;k++)
{
buf = ((u8(*)[16])(ptr+fileHead->bfOffBits))[i-k][j];
if(((~buf)&(0x80>>l)))
{
buff |= 1<<k;
}
else
{
buff &= ~(1<<k);
}
}
*p++ = buff;
if(p-display_buf>1023)
{
p = display_buf;
}
}
}
}
OLED_ShowPicture(0,0,128,64,display_buf,1);
myfree(display_buf);
return 0;
}
3 重点难点(BMP文件格式解析、内存与屏幕显示的对应)
光有代码没有用,主要是当时提取BMP文件的图片信息就耗费了很长时间;另外,还需要将像素矩阵的顺序跟屏幕刷新时的顺序相对应,具体要看用的什么屏幕驱动,这里是将像素矩阵转置后再把每行倒序输入进屏幕缓存就可以。
注:每个屏幕驱动都不一样,历经1年多我也忘记我的是什么顺序了,反正就是要调整顺序的意思。
首先,判断是都未BMP文件,判断文件头。
if(fileHead->bfType != 0x4D42)//验证文件头
{
return 1;//返回失败
}
然后,判断图片大小,因为是128x64的屏幕,因此我这里只显示128x64的图片。
if(infoHead->biWidth != 128 || infoHead->biHeight != 64)//验证图片大小
{
return 1;//返回失败
}
最后,重中之重,提取BMP文件的像素并转换成屏幕可以识别的顺序。(笨办法,多重for循环之术)
display_buf = (u8*)mymalloc(infoHead->biSizeImage);//申请1024字节的内存用于存储
p = display_buf;
for(i=63;i>=0;i=i-8)//这里将bmp格式转换为oled可以识别的字节顺序
{
for(j=0;j<16;j++)
{
for(l = 0;l<8;l++)
{
buff = 0;
for(k=0;k<8;k++)
{
buf = ((u8(*)[16])(ptr+fileHead->bfOffBits))[i-k][j];
if(((~buf)&(0x80>>l)))
{
buff |= 1<<k;
}
else
{
buff &= ~(1<<k);
}
}
*p++ = buff;
if(p-display_buf>1023)
{
p = display_buf;
}
}
}
}
OLED_ShowPicture(0,0,128,64,display_buf,1);
myfree(display_buf);
4 写在最后
难点解决了就很简单了,就是通过TCP将图片传输过来,单片机收到数据后进行数据解析。
演示视频稍后放出。
各位大佬,码字不易,如果需要完整代码,可以交流联系。