一、缘起
因为之前想要DIY自己的开发板,还好死不死选择了STM32的,没办法也只能头铁的继续下去了,主要是:SDRAM,TF卡,7寸电容触摸屏,OV相机,百兆网口及COM口以便满足常规的应用需求,所有单功能测试已经完成,但是在功能集成的时候EMC严重干扰,而且也不知道自己哪来的自信,还打了个两层板的样,想降低一下开发成本,奈何想法很美好,现实很骨感!
二、调试及踩坑记录
一开始的时候发现点亮还是很开心的,但是随即各种问题也是让人无语。
1.显示不全问题,不知道是不是我买的这块屏幕独有的问题,总之工作在低频下侧边总有300个像素左右的宽度区域是黑的无法点亮,一开始以为是宽高方向设置错了了,各种配置比较,奈何网上大部分都是5寸屏,有7寸屏的也都是比较不出点什么,手上也没有其他电容屏测试,所以只能硬着头皮瞎猜调试,不过最后直觉使然下,居然尝试配置成高频模式下直接过了。当真是惊呆了,然后随之而来的问题
2.EMC,一开始的时候直接是通过硬件图层管理直接刷的原图,显示出来的效果和原图的色彩相差实在太大了,当时就懵圈了,后来仔细一想索性就做个猜想测试,将原图RGB三通道拆离分别一个通道一个通道的画到LCD 上去,一看问题就出来了。
WeChat_20230313191744
只有R通道时正常的,其他两个通道简直不忍直视了,实际如上面的视频也已经展示出了问题。
3.OV相机问题,EMC严重,已经可以成像,但是成像色彩严重偏差,几乎只有黑白两色,呈现轮廓明显存在畸变(暂时也没时间处理)
以上剩余问题需要重新设计PCB打样测试了。毕竟没有专业设备,也只能是边猜边解决了。也不知道EMC源头在哪,哎,无奈,SO 与此同时就顺便搞下软件上的进度。
毕竟是DIY自己的开发板,那同样的也要配套自己的软体系统
三、系统开发
1.RTOS:网上到处都是,有原理,有代码的,不过不怎么喜欢各种宏的条件编译,所以动手手撸一个吧,CPU Poll 切片多任务,满足基本应用要求。
2.文件系统,FAT32首选,不过据说存在删除回收的BUG,所以也是花了一个星期了解了一下FAT32长/短文件名规则及内容获取,手撸了一个半成品,从TF中读取文件及内容的功能。
写入、删除功能暂时不考虑,文件直接通过WIN系统存储进去就可以了,自己这边只是读一下处理。
3.主要应用任务,毕竟心血来潮之后总得让它跑起点什么,那么就从CNN入手,虽然只有180M主频32Mb内存,跑个CNN 是绰绰有余但是速度性能效率也不知道会怎么样。而且麻雀虽小但也得要五脏俱全吧。
四 、CNN及调试与记录
这也是目前要处理的重点了:所以为了方便开发和调试最终还是选择了WINDOWS及VS开发
CNN 卷积神经网络 网上一搜也是各种资料就不赘叙了、包括怎么卷,我们要做的只是代码实现而已。
目前初步 单步卷积,均值/最大值 池化,Relu激活函数,相当于是单层卷积已经完成,不过因为初步需要测试所以没有对图像大小做限制处理,边距操作也没有加入,以下代码,目前只是笔者初步测试,仅在以下的测试图片上完成,其他图片测试可自行调试修改
核心代码及测试代码,测试卷积核,以及测试效果如下,opencv 辅助读取一下图片,因为在window平台只是方便调试及测试,所以核心代码都采用 C 基础风格及语法处理,方便后面直接集成进自己的硬件上。
#define MAXCHANNELCOUNT 3
typedef struct _inkpps {
unsigned Width;
unsigned Height;
unsigned char Channel;
unsigned char *pDatas[MAXCHANNELCOUNT];
}inkPicParam;
inkPicParam PicDataGet(char *path) {
inkPicParam pp = { 0 };
Mat in = imread(path);
unsigned size = in.cols * in.rows;
if (!in.empty() && size > 0) {
pp.Width = in.cols;
pp.Height = in.rows;
pp.Channel = in.channels();
int xd = in.depth();
for (unsigned x = 0; x < MAXCHANNELCOUNT; x++)
pp.pDatas[x] = (unsigned char *)malloc(size);
size *= pp.Channel;
IkrvPicChannelSplit((char *)in.data, size, pp.pDatas);
}
return pp;
}
void Release(inkPicParam *pp) {
for (unsigned m = 0; m < MAXCHANNELCOUNT; m++) {
if (pp->pDatas[m] != 0)
free(pp->pDatas[m]);
}
memset(pp, 0, sizeof(inkPicParam));
}
int main()
{
inkPicParam ipp = PicDataGet("../testBMP/test.jpg");
//横线卷积核
char rkconv[3][3] = {
{1,1,1},
{0,0,0},
{-1,-1,-1}
};
//竖线卷积核
char ckconv[3][3] = {
{ 1,0,-1 },
{ 1,0,-1 },
{ 1,0,-1 }
};
char nkconv[3][3] = {
{-1,-1,-1},
{-1,8,-1},
{-1,-1,-1}
};
char xkconv[3][3] = {
{1,2,1},
{2,4,2},
{1,2,1}
};
inkScPicParam iscp = { 0 };
char *k = 0;
k = rkconv[0];
iscp.pDatas = ipp.pDatas[0];
iscp.Width = ipp.Width;
iscp.Height = ipp.Height;
IkrvCnnConvolution(&iscp, &k, 3);
//inkCnnPool(INK_AVERPOOL, &iscp, 4);
Mat tc1(iscp.Height, iscp.Width, CV_MAKETYPE(CV_8U, 1), iscp.pDatas, iscp.Width * 1);
imwrite("../testBMP/tc1.jpg", tc1);
k = ckconv[0];
iscp.pDatas = ipp.pDatas[1];
iscp.Width = ipp.Width;
iscp.Height = ipp.Height;
IkrvCnnConvolution(&iscp, &k, 3);
//inkCnnPool(INK_AVERPOOL, &iscp, 4);
Mat tc2(iscp.Height, iscp.Width, CV_MAKETYPE(CV_8U, 1), iscp.pDatas, iscp.Width * 1);
imwrite("../testBMP/tc2.jpg", tc2);
k = nkconv[0];
iscp.pDatas = ipp.pDatas[2];
iscp.Width = ipp.Width;
iscp.Height = ipp.Height;
IkrvCnnConvolution(&iscp, &k, 3);
//inkCnnPool(INK_AVERPOOL, &iscp, 4);
Mat tc3(iscp.Height, iscp.Width, CV_MAKETYPE(CV_8U, 1), iscp.pDatas, iscp.Width * 1);
imwrite("../testBMP/tc3.jpg", tc3);
Release(&ipp);
return 0;
}
void IkrvCnnConvolution(inkScPicParam *inPpOut, char **ckd, unsigned char kDimension) {
unsigned ki = 0, sCuri = 0;
unsigned sSize = 0, nd = 0;
unsigned sr = 0, sc = 0;
unsigned srcw = inPpOut->Width;
short sumval = 0;
if (kDimension % 2 != 0) {
nd = (kDimension - 1);
inPpOut->Height = (inPpOut->Height - nd);
inPpOut->Width = (inPpOut->Width - nd);
ki = 0;
sSize = inPpOut->Height * inPpOut->Width;
for (unsigned x = 0; x < sSize; x++)
{
sumval = 0;
sr = x / inPpOut->Width;
sc = x % inPpOut->Width;
for (unsigned kh = 0; kh < kDimension; kh++) {
for (unsigned kw = 0; kw < kDimension; kw++) {
ki = kh * kDimension + kw;
sCuri = (sr + kh)*srcw + (sc + kw);
sumval += (inPpOut->pDatas[sCuri] * (*ckd)[ki]);
}
}
inPpOut->pDatas[x] = Relu(sumval / (kDimension * kDimension));
}
}
}
测试一下,将原图拆成三个通道,分别采用三个不同的卷积核卷(上面代码中的卷积核)下,测试效果如下:
在横竖线的卷积核对应的结果图像下特征也是显然明显的。
五、后续
接下去,就是最终的全连接层以及分类器了,等后面有时间继续更新。