dirty

  • 2024-07-16
  • 回复了主题帖: 免费申请|基于树莓派MCU RP2040的Wi-Fi/BLE开发板

    申请通过名单出来没

  • 回复了主题帖: 【全能小网关|CH32V208】--1.开箱与点灯

    tagetage 发表于 2024-6-9 12:10 点LED的目的就是 : 搭建了环境,点亮了LED,整个开发环节搭通,为后续开发奠定良好基础。 哈哈,循序渐进,先准备好,试一哈,至少验证后软、硬件上没有大问题

  • 回复了主题帖: 【全能小网关|CH32V208】--4.LCD显示

    lugl4313820 发表于 2024-6-30 06:59 看起来刷新的速度非常快呀。 硬件spi,速度还是可以的

  • 回复了主题帖: 【全能小网关|CH32V208】--4.LCD显示

    wangerxian 发表于 2024-6-30 11:44 内部Flash是不是存不了多少图片? 有些小技巧,在Link.ld文件做下配置修改

  • 回复了主题帖: 【全能小网关|CH32V208】--6.蓝牙串口服务

    lugl4313820 发表于 2024-7-14 07:04 更多关于TMOS及协议栈使用可参考阅读官方文档。 他有中文文档吗? 有的,SDK路径下,目前是\CH32V20xEVT\EVT\EXAM\BLE\沁恒低功耗蓝牙软件开发参考手册.PDF

  • 回复了主题帖: 【全能小网关|CH32V208】--5.LVGL移植

    lugl4313820 发表于 2024-7-14 07:05 感谢大佬的资料分享,我就在想,移植LVGL,文件添回那步,难不难?' 主要是MRS IDE环境熟悉使用,文件添加可以在附件工程配置里已经添加好,有需要可作参考

  • 回复了主题帖: 共读入围名单:《人工智能实践教程——从Python入门到机器学习》

    个人信息无误,确认可以完成评测计划。

  • 发表了主题帖: 《机器学习算法与实现 》Pytorch

    本帖最后由 dirty 于 2024-7-16 23:34 编辑       PyTorch是一个深受研究人员喜爱的深度学习框架,其功能丰富,使用灵活。在 pyTorch 中,可以调整操作流程,对整个过程进行控制。PyTorch还支持高性能计算,可将PyTorch视为有GPU支持的Numpy。现在有多个高级接口的基于 PyTorch 的深度学习框架,如 FastAI、Lightning、Ignite,使用这些框架可更快地开发深度学习应用。       张量(Tensor)是一种特殊的数据结构,它类似于数组和矩阵。在PyTorch 中,使用张量来编码模型的输入、输出和参数。张量类似于NumPy的ndarray,不同之处是张量可在 GPU 或其他硬件加速器上运行。       Tensor生成,代码如下 import torch import numpy as np #创建一个 numpy ndarraynumpy_tensor =np.random.randn(10,20) #使用下面两种方式将 numpy 的ndarray 转换为 tensor: pytorch_tensor1 = torch.tensor(numpy_tensor) pytorch_tensor2 = torch.from_numpy(numpy_tensor) Tensor转换 #如果PyTorch tensor在CPU上 numpy_array = pytorch_tensor1.numpy() #如果PyTorch tensor 在GPU 上 numpy_array = pytorch_tensor1.cpu().numpy() 将Tensor 转换到 GPU 上 #第一种方式是定义 cuda 数据类型 # 定义默认 GPU 的数据类型 dtype = torch.cuda.FloatTensor gpu_tensor= torch.randn(10,20).type(dtype) #第二种方式更简单,推荐使用 gpu_tensor1 = torch.randn(10,20).cuda(0) #将tensor 放到第一个GPU上使用 gpu_tensor2= torch.randn(10,20).cuda(1) #将tensor 放到第二个 GPU上 将Tensor 转换到 CPU上 cpu_tensor =gpu_tensor.cpu() Tensor 的属性 #通过如下两种方式得到 tensor 的大小 print(pytorch_tensor1.shape) print(pytorch_tensor1.size()) #得到 tensor 的数据类型 print(pytorch_tensor1.type()) print(gpu_tensor.type()) #得到 tensor 的维度 print(pytorch_tensor1.dim()) #得到 tensor 的所有元素个数 print(pytorch_tensor1.numel())         这里引入PyTorch 来构建神经网络模型。神经网络是由多个神经元堆叠在一起形成的网络,主要包括整入层、隐意层和轮出层。输入层由特征数量决定,输出层则由所要解决的问题决定,隐藏层的网络层数及每层的神经元数是可以调节的超参数,不同的层数和每层的网络参数都对模型有一定的影响。                                                 使用PyTorch实现神经网络,首先生成一些测试数据,代码如下,这里对源代码稍作修改,以便X、Y轴显示负数. import torch import numpy as np from torch import nn from torch.autograd import Variable import torch.nn.functional as F import matplotlib.pyplot as plt '''''''''''''''生成数据''''''''''''''' np.random.seed(1) m = 400 #样本数量 N = int(m/2) #每一类的点的数量 D=2 #维度 x= np.zeros((m, D)) y=np.zeros((m,1),dtype='uint8') #label向量,0-红色,1-蓝色 a = 4 #生成两类数据 for j in range(2): ix = range(N*j,N*(j+1)) t= np.linspace(j*3.12,(j+1)*3.12,N)+ np.random.randn(N)*0.2 r= a*np.sin(4*t)+ np.random.randn(N)*0.2 #radius x[ix]= np.c_[r*np.sin(t),r*np.cos(t)] y[ix] = j # 绘制生成的数据 plt.scatter(x[:,0],x[:,1],c=y.reshape(-1),s=40,cmap=plt.cm.Spectral) plt.rcParams['font.sans-serif']=['SimHei'] plt.rcParams['axes.unicode_minus']=False plt.title("PyTorch生成数据") plt.show()         逻辑斯蒂回归并不能很好地区分复杂的数据集,因为逻辑斯蒂回归是一个线性分类器,这里略过具体讲使用PyTorch实现多层神经网络。程序架构与前面的程序的架构类,只是要将网络模型定义部分改成多层神经网络,其他代码几乎不变。网络正向计算的函数是ip_network(),在这个函数中依次将输入数据和网络连接权重相乘,然后使用激活函数进行变换,完整代码如下: import torch import numpy as np from torch import nn from torch.autograd import Variable import torch.nn.functional as F import matplotlib.pyplot as plt #生成数据 np.random.seed(1) m = 400 #样本数量 N = int(m/2) #每一类的点的数量 D=2 #维度 x= np.zeros((m, D)) y=np.zeros((m,1),dtype='uint8') #label向量,0-红色,1-蓝色 a = 4 #生成两类数据 for j in range(2): ix = range(N*j,N*(j+1)) t= np.linspace(j*3.12,(j+1)*3.12,N)+ np.random.randn(N)*0.2 r= a*np.sin(4*t)+ np.random.randn(N)*0.2 #radius x[ix]= np.c_[r*np.sin(t),r*np.cos(t)] y[ix] = j ############################################################### '''''''''使用PyTorch实现多层神经网络''''''''' x= torch.from_numpy(x).float() y= torch.from_numpy(y).float() #定义两层神经网络的参数 w1= nn.Parameter(torch.randn(2,4)*0.01) #输入维度为2,隐藏层神经元 b1 = nn.Parameter(torch.zeros(4)) w2= nn.Parameter(torch.randn(4,1)*0.01) #隐藏层神经元数量为4,输出 b2 =nn.Parameter(torch.zeros(1)) # 定义模型 def mlp_network(x): x1 = torch.mm(x,w1)+b1 x1 = F.tanh(x1) #使用 PyTorch 自带的 tanh 激活函数 x2= torch.mm(x1,w2)+ b2 return x2 #定义优化器和损失函数 optimizer =torch.optim.SGD([w1,w2,b1,b2],1.) criterion =nn.BCEWithLogitsLoss() # 网络训练 10000 次 for e in range(10000): #正向计算 out = mlp_network(Variable(x)) #计算误差 loss = criterion(out,Variable(y)) #计算梯度并更新权重 optimizer.zero_grad() loss.backward() optimizer.step() # if(e +1)% 1000 == 0: # print('epoch:{},loss:{}'.format(e+1, loss.data[0])) def plot_decision_boundary(model,x,y): # Set min and max values and give it some padding x_min,x_max=x[:,0].min()-1,x[:,0].max()+ 1 y_min,y_max=x[:,1].min()-1,x[:,1].max()+1 h=0.01 # Generate a grid of points with distance h between them xx,yy= np.meshgrid(np.arange(x_min,x_max,h),np.arange(y_min, y_max, h)) # Predict the function value for the whole grid Z = model(np.c_[xx.ravel(),yy.ravel()]) Z=Z.reshape(xx.shape) #Plot the contour and training examples plt.contourf(xx,yy,Z,cmap=plt.cm.Spectral) plt.ylabel('x2') plt.xlabel('x1') plt.scatter(x[:,0],x[:,1],c=y.reshape(-1),s=40, cmap=plt.cm.Spectral) #多层神经网络结果可视化 def plot_network(x): x= Variable(torch.from_numpy(x).float()) x1= torch.mm(x,w1)+ b1 x1 = F.tanh(x1) x2=torch.mm(x1,w2)+ b2 out = F.sigmoid(x2) out =(out> 0.5)*1 return out .data.numpy() plot_decision_boundary(lambda x: plot_network(x),x.numpy(), y.numpy ()) plt.rcParams['font.sans-serif']=['SimHei'] plt.rcParams['axes.unicode_minus']=False plt.title('2层神经网络') plt.show()       可以看到,神经网络能够非常好地分类这个复杂的数据集。与前面的逻辑斯蒂回归相比,神经网络有多个处理层,能够构成复杂的非线性分类器,导致神经网络分类的边界更复杂,处理数据的能力更强。使用 PyTorch 实现逻辑斯蒂回归和多层神经网络的程序非常相似,不同之处仅为正向计算的不同。在程实现中,不需要关心梯度的计算和参数的更新,因此降低了编程的难度。       前面的线性回归模型、逻辑斯蒂回归模型和神经网络而言,构建它们时就定义了所需的参数。这样搭建较小的模型是可行的,但对大模型而言,手动定义参数就显得非常麻烦,所以 PyTorch 提供了两个模块来帮助构建模型:一个是 Sequential,另一个是 Module。Sequential允许构建序列化模块,Module是一种更灵活的模型定义方式。       PyTorch 拥有强大的神经网络定义、实现、参数更新等工具,能够极大地降低神经网络实现的难度。在前面给出的例子中,数据比较简单,无法充分体现神经网络的能力。通过引入NNIST手写数字、CIFAR-10 数据集及对应的神经网络训练技巧,使用 PyTorh 实现多层神经网络.       MNIST 数据集是一个比较常用的数据集,主要用来测试机器学习算法的性能。深度神经网络以对手写数字图像进行分类,任务是给出一幅手写数字图像,让神经网络识别其是0到9十个数字中的哪个数字。下面以 MNIST 数据集为例,介绍深度神经网络的定义和训练。PyTorch 中内置了 MNIST 数据集的加载类。       CFAR-10是一个更接近普适物体的彩色图像数据集,共包含如下 10类 RGB 彩色图片;飞(aimiane)、汽车(automobile)、鸟(bird)、猫(cat)、鹿(dcer)、狗(dog)、蛙(fog)、马(hor船(ship)和卡车(tck)。       后面部分讲了网络训练,分析了迭代次数与训练数据的损失与准确率曲线。对于模型优化求解,PyTorch支持多种优化方式,包括随机梯度下降法、Adam 优化器等,可以根据问题选择最优的求解器。       本章的学习,对PyTorch有了更深入的认知与理解,讲解内容由浅入深、循序渐进,结合案例,更能体现学有所用,梳理学习回顾,也会有常读常新的收获。  

  • 2024-07-14
  • 发表了主题帖: 《机器学习算法与实现 —— Python编程与应用实例》神经网络

          本篇讲述该书关于神经网络方面的知识点。       人工神经网络(Artificial Neural Networks,ANNS),简称神经网络(NeuralNetworks,NNS)或类神经网络,是一种模仿生物神经网络结构和功能的数学模型或计算模型,用于对函数进行估计或近似。神经网络由大量神经元及各层之间的连接关系构成,每个神经元都包含一种特定的输出函数,称为激励函数或激活函数(Activation Function):每两个节点之间的连接都用权重来代表信号连接的强度,这相当于人工神经网络的记忆。网络的输出则因网络连接方式、权重和激励函数的不同而不同。神经网络通常是对自然界中的某种事物的逼近,也可能是对一种逻辑策略的表达。       感知机(Percepton),一种单层网络特性的神经网络结构,它是解释大脑神经元如何工作的简化数学模型:取一组二进制输入值(附近的神经元),将每个输入值乘以一个连续权重(每个附近的神经元的突触强度),并设置一个阈值,如果这些加权输入值的和超过该阈值,就输出1,否则输出0,这样的假设类似于神经元是否放电。       感知机 学习策略:感知机学习的目标就是求得一个能够将训练数据的正负实例点完全分开的分离超平面,即最终求得参数参数w和b,其中w是超平面S的法向量,b是超平面的截距,超平面S称为分离超平面。       感知机学习算法:感知机学习是误分类驱动的,具体采用随机梯度下降法。首先,随机设置模型参数,b的初值,然后使用梯度下降法不断极小化目标函数,极小化过程不是一次性地选取 M中的所有误分类点使其梯度下降,而是一次随机选取一个误分类点使其梯度下降。感知机的学习策略虽然比较简单,但是具备基本的学习能力,能够通过训练数据学习得到区分数据类别的模型参数,因此为后续的多层神经网络奠定了理论和编程实现的基础。       组合多个神经元,形成一个网状结构,以实现复杂的数据分析功能。当多个神经元被组织在一起时,有些神经元的输出是另一些神经元的输入,但前面的神经元输出应该是什么值、如何计算误差等都成了问题,因此神经网络的训练是一个有待解决的难题。反向传播算法的引入解决了多层神经网络的训练问题。       神经元:神经元和感知器本质上是一样的,只不过提到感知器时,其激活函数是阶跃函数;而神经元的激活函数往往选为 sigmoid 函数或 tanh 函数。神经元的结构和示意如下图所示 图1:神经元的结构与示意       计算一个神经元的输出的方法与计算一个感知机的输出的方法是一样的。假设神经元的输入是向量x,权重向量是",激活函数是 sigmoid函数,则神经元的输出y定义为       式中,sigmoid 函数的定义为         sigmoid函数曲线如下,sigmoid函数是非线性函数,其值域是(0,1),其导数比较特殊,可用sigmoid函数自身表示。这样一旦计算出sigmod函数的值,计算其导数就非常方便。         神经网络其实就是按照一定的规则连接起来的多个神经元。其规则如下: ●神经元按照层来布局。 ●最左边的层称为输入层,负责接收输入数据。 ●最右边的层称为输出层,可以从该层获取神经网络的输出数据。 ●输入层和输出层之间的层称为隐藏层,因为它们对外部来说是不可见的。 ●同一层的神经元之间没有连接。 ●第 N层的每个神经元和第N-1层的所有神经元输入层相连(这就是全连接的含义),第N-1层神经元的输出就是第N层神经元的输入;每个连接都有一个权重。       还存在其他结构的神经网络,如卷积神经网络(Conwvohmional NeuralNewoks,CNs)、循环神经网络(Recurent Neural Networks,RNNs)等它们有着不同的连接规则。 神经网络正向算法和反向传播算法涉及到较多公式推演,用到高等数学方面知识,可自行对其要点梳理学习了解。这里简单讲下激活函数。             在逻辑斯蒂回归算法中,输入的特征经过加权、求和后,还要通过一个sigmoid逻辑函数,将线性回归值归一化到区间[0,1]上,进而体现分类概率值。这个逻函数在神经网络中被称为激活务数图为它源自生物神经系统中神经元被激活的过程。在神经网络中,不仅最后的分类输出层需要激活商数,而且每一层都需要被激活,然后向下一层输入被激活后的值。由于神经元需要激活后才能向后传播,因此激活是必要的。       使用激活函数后,神经网络可以通过改变权重来实现任意分离超平面的变化。神经网络越复杂能够拟合的形状就越复杂,这就是著名的神经网络万有逼近定理。神经网络使用的激活函数都是线性的,每个激活函数都输入一个值,然后做一种特定的数学运算得到一个结果。       书中讲到了sigmoid激活函数;tanh激活函数;ReLU激活函数。         下面通过程序演示如何将数学公式转换成程序的循环、判计算等基本操作,以及如何使用 NumPy等第三方库。为了测试、演示程序的执行,首先生成一些例数据。 import numpy as np from sklearn import datasets, linear_model import matplotlib.pyplot as plt #生成样本数据 np.random.seed(0) X,y=datasets.make_moons(200,noise =0.20) y_true =np.array(y).astype(float) #生成神经网络输出目标 t=np.zeros((X.shape[0],2)) t[np.where(y==0),0]=1 t[np.where(y==1),1]=1 #数据可视化 plt.scatter(X[:,0],X[:,1],c=y,cmap = plt.cm.Spectral) plt.rcParams['font.sans-serif']=['SimHei'] plt.title("样本数据") plt.show() 样本数据可视化图       多层神经网络训练程序 '''''''''''''''''''''多层神经网络模型''''''''''''''''''''' #生成神经网络模型 class NN_Model: epsilon=0.01 #学习率 n_epoch=1000 #迭代数 nn=NN_Model() nn.n_input_dim = X.shape[1]#输入尺寸 nn.n_output_dim=2 #输出节点大小 nn.n_hide_dim=8 #隐藏节点大小 nn.X=X nn.y=y #初始化权重数组 nn.W1 =np.random.randn(nn.n_input_dim, nn.n_hide_dim)/np.sqrt(nn.n_input_dim) nn.b1 =np.zeros((1,nn.n_hide_dim)) nn.W2 =np.random.randn(nn.n_hide_dim, nn.n_output_dim)/np.sqrt(nn.n_hide_dim) nn.b2 =np.zeros((1,nn.n_output_dim)) #定义 sigmoid 及其导数函数 def sigmoid(X): return 1.0/(1+np.exp(-X)) #网络正向运算 def forward(n,X): n.z1=sigmoid(X.dot(n.W1)+ n.b1) n.z2=sigmoid(n.z1.dot(n.W2)+ n.b2) return n #使用随机权重进行预测 forward(nn,X) y_pred = np.argmax(nn.z2, axis = 1) #数据可视化 plt.scatter(X[:,0],X[:,1],c= y_pred, cmap = plt.cm.Spectral) plt.rcParams['font.sans-serif']=['SimHei'] plt.title("随机初始化模型参数对示例数据预测的结果") plt.show() 随机初始化参数数据预测结果       反向传播算法,对网络进行训练,通过可视化,反应真实值与预测值。代码如下: '''''''''''''''''''''多层神经网络的反向传播''''''''''''''''''''' from sklearn.metrics import accuracy_score #反向传播 def backpropagation(n,X,y): for i in range(n.n_epoch): #正向计算每个节点的输出 forward(n,X) # 打印loss,accuracy L=np.sum((n.z2-y)**2) y_pred =np.argmax(nn.z2,axis=1) acc = accuracy_score(y_true, y_pred) print("epoch[%4d]L=%f,acc=%f"%(i,L, acc)) #计算误差 d2 =n.z2*(1-n.z2)*(y-n.z2) d1 =n.z1*(1-n.z1)*(np.dot(d2,n.W2.T)) #更新权重 n.W2 +=n.epsilon*np.dot(n.z1.T, d2) n.b2 +=n.epsilon *np.sum(d2,axis=0) n.W1 +=n.epsilon *np.dot(X.T,d1) n.b1 +=n.epsilon *np.sum(d1,axis=0) nn.n_epoch = 2000 backpropagation(nn,X,t) #数据可视化 y_pred =np.argmax(nn.z2,axis=1) plt.scatter(X[:,0],X[:,1],c=nn.y, cmap = plt.cm.Spectral) plt.rcParams['font.sans-serif']=['SimHei'] plt.title("真实值") plt.show() plt.scatter(X[:,0],X[:,1],c=y_pred, cmap = plt.cm.Spectral) plt.rcParams['font.sans-serif']=['SimHei'] plt.title("预测值") plt.show() 神经网络训练后对数据进行预测得到的结果       上面使用神经网络的正向计算、反向传播。此外,可以用类的方法封装多层神经网络,可以改进程序的封装方式,对理解和调用神经网络较友好。可参阅书籍做进一步了解,这里不赘述。   softmax函数与交叉熵代价函数       在机器学习中,尤其是在深度学习中,sofmax函数是一个重要函数,在多分类的场景中使用广sotmax函数将一些输入映射为区间[0,1]内的实数并归一化,以保证和为1,进而满足多分类的之和为1的要求。       sotmax函数也称归一化指数函数,一般在神经网络中作为分类任务的输出层。其实,我们可以认为sofmax函数输出的是样本属于每个类别的概率。例如,一个分类任务要分为三个类别,sofmax函数可以根据它们的相对大小,输出属于三个类别的概率,且概率之和为1。       神经网络的权重更新需要一个代价函数来表示真实值与网络估计值的偏差,而对代价函数取偏g数可得到权重的更新量。人在学习和分析新事物时,发现自己犯的错误越大,改近的力度也就越大。人们设计了交又焰(价医数,其主要优点是求导结果比较简单,工计算,并且能解决某些代价函数学习缓慢问题。         本章介绍了感知器模型及其学习、多层神经网络的构建和训练、常用激活函数,以及sofm与交叉熵代价函数等。此外,还通过程序演示了机器学习中的神经网络正向计算和误差的反向传播。总的来说受益匪浅,收获良多。

  • 2024-07-13
  • 发表了主题帖: 《机器学习算法与实现 —— Python编程与应用实例》环境搭建与k最近邻算法

          本书第二、三章讲Python语言基础及常用库,这里将本书环境搭建部分做下讲解,在涉及到k最近邻算法部分涉及库部分自行安装,并将该算法进行演示。   一.环境搭建       Anaconda集成大部分Python软件包,使用方便。这里使用国内镜像, https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/  ,下载完成后windows下安装,安装过程中选择自动加入环境变量选项。       打开控制台,输入conda --version查询版本,是否安装上。生成一个新环境 conda create -n <your_env> ,激活虚拟运行环境conda activate <your_env>,激活成功后前面带括号工程名,如下 图1:conda工程激活       此外,也可在Pycharm里建立conda环境,如下所示,绿色框内一个为在windows控制台下创建,另一个为PyCharm下创建。 图2:PyCharm下创建Conda工程       此外,安装包可在pycharm Teiminal控制台命令安装或者设置里安装,如下 图3:资源包安装选择       到这里,环境基本搭建完成,根据需要安装资源包。   二.k最近邻算法       k最近邻算法是机器学习中的基础算法之一,它既能用于分类,又能用于回归。k最近邻算法的中心思想是,通过度量给定样本到训练数据集中所有样本的特征距离,将与给定样本特征距离最近的k个样本中出现次数最多的类别指定为该样本最优估计的类别。       k最近邻算法的策略如下:有一堆已知类别的样本,当一个新样本进入时,依次求它与训练样本中的每个样本点的距离,然后挑选训练样本中最近的 k个点,通过被选样本所属的类别来决定待分配样本所属的类别。 图4:k最近邻算法的基本原理       生成训练数据和测试数据,代码如下: import numpy as np import matplotlib.pyplot as plt # 生成模拟数据 np.random.seed(314) data_size1 = 100 x1 = np.random.randn(data_size1, 2) + np.array([4,4]) y1 =[0 for _ in range(data_size1)] data_size2 = 100 x2 = np.random.randn(data_size2, 2) * 2 + np.array([10, 10]) y2 = [1 for _ in range(data_size2)] # 合并生成全部数据 x = np.concatenate((x1, x2), axis=0) y = np.concatenate((y1, y2), axis=0) data_size_all = data_size1 + data_size2 shuffled_index = np.random.permutation(data_size_all) x = x[shuffled_index] y = y[shuffled_index] # 分割训练集和测试集 split_index = int(data_size_all * 0.7) x_train = x[:split_index] y_train = y[:split_index] x_test = x[split_index:] y_test = y[split_index:] # 绘制结果 plt.scatter(x_train[:,0], x_train[:,1], c= y_train, marker='.') plt.rcParams['font.sans-serif']=['SimHei'] plt.title("训练数据") plt.show() plt.scatter(x_test[:,0], x_test[:,1], c= y_test, marker='.') plt.rcParams['font.sans-serif']=['KaiTi'] plt.title("测试数据") plt.show()       运行后可视化图如下: 图5:训练数据与测试数据       k最近邻算法的实现主要分为三步:计算距离、按距离取样本点和投票决定类别。 ●kNN_distance()函数:计算测试数据与各个训练数据之间的距离。在kNN 算法中,通过计算出对象之间的距离作为各个对象之间的非相似性指标,可以避免对象之间的匹配问题。 ●kNN_vote()函数:确定k个点中不同类别的出现频率,返回其中出现频率最高的类別作为测试数据的预测类别。 ●kNN_predict()函数:调用kNN_distance()函数计算距离,对距离排序后,选取距离最小的k个点,再调用kNN_vote()函数得到预测类别。 KNN算法代码实现如下: def kNN_distance(v1, v2): '''计算两个多维向量的距离''' return np.sum(np.square(v1 - v2)) def kNN_vote(ys): '''根据ys的类别, 挑选类别最多的一类作为输出''' vote_dict = {} for y in ys: if y not in vote_dict.keys(): vote_dict[y] = 1 else: vote_dict[y] += 1 # 找到类别最多的一类,也可以用循环遍历的方式来找,这里使用了max函数来直接返回值最大的键 max_key = max(vote_dict, key=lambda k: vote_dict[k]) return max_key def kNN_predict(x, train_x, train_y, k=3): ''' 针对指定的数据进行分类,参数: x - 输入的待分类样本 train_x - 训练数据的样本 train_y - 训练数据的标签 k - 最近邻的样本数 ''' dist_arr = [kNN_distance(x, train_x[j]) for j in range(len(train_x))] sorted_index = np.argsort(dist_arr) top_k_index = sorted_index[:k] ys = train_y[top_k_index] return kNN_vote(ys) '''对训练集的样本进行分类''' y_train_test = [kNN_predict(x_train[i],x_train, y_train) for i in range(len(x_train))] print(y_train_test) # 统计训练精度 n_correct = 0 for i in range(len(x_train)): if y_train_test[i] == y_train[i]: n_correct += 1 accuracy = n_correct / len(x_train) * 100.0 print("Train Accuracy: %f%%" % accuracy) #对测试集的样本进行分类 y_train_test = [kNN_predict(x_test[i], x_train, y_train) for i in range(len(x_test))] # 统计训练精度 n_correct = 0 for i in range(len(x_test)): if y_train_test[i] == y_test[i]: n_correct += 1 accuracy = n_correct / len(x_test) * 100.0 print("Test Accuracy: %f%%" % accuracy)       算法对训练集和测试集进行预测,并计算精度:代码运行结果如下: 图6:KNN算法运行       拓展认识学习:动手编程实现kNN 分类器能够提高自己的算法思维与编程能力,但是考虑的细节不多。①未优化算法;②通用性较差。传统机器学习领域中著名的scikit-leamn"(也称skleamn),这个库包括了大都分机器学习方法,涵盖了几乎所有的主流机器学习算法,是基于Python语言的开源机器学习工具包,可通过 NumPy、SciPy 和 Matplotlib等 Python 数值计算库高效地实现算法应用。         至此,对k最近邻算法有了一个较深入的了解与学习。  

  • 发表了主题帖: 【全能小网关|CH32V208】--7.DHCP

          本篇讲述用沁恒CH32V208开发板实现DHCP. 一.了解与准备       DHCP(Dynamic Host Configuration Protocol),动态主机配置协议,是一个应用层协议。当我们将客户主机ip地址设置为动态获取方式时,DHCP服务器就会根据DHCP协议给客户端分配IP,使得客户机能够利用这个IP上网。可以用Wireshark抓取DHCP包。       CH32V208芯片自带以太网MAC,内置10MPHY,支持10M以太网,我们可以看到芯片引脚直接与网口座连接,这款芯片具备蓝牙与以太网功能,比较适合做小型IOT网关。      沁恒自有一套WCHNET协议栈库,该协议栈库提供了TCP/IP 子程序库,集成了 TCP、UDP、ARP、RARP、ICMP、IGMP等以太网协议栈,可以同时支持 TCP、UDP 和 IPRAW 三种模式。在SDK里有一个专门文档介绍,可以根据需要参考详细说明使用。   二.代码准备 1.获取芯片MAC地址 #define ROM_CFG_USERADR_ID 0x1FFFF7E8 /********************************************************************* * @fn WCHNET_GetMacAddr * * [url=home.php?mod=space&uid=159083]@brief[/url] Get MAC address * * [url=home.php?mod=space&uid=784970]@return[/url] none. */ void WCHNET_GetMacAddr( uint8_t *p ) { uint8_t i; uint8_t *macaddr=(uint8_t *)(ROM_CFG_USERADR_ID+5); for(i=0;i<6;i++) { *p = *macaddr; p++; macaddr--; } } 2.配置DHCP主机名,这个由库函数完成 WCHNET_DHCPSetHostname("WCHNET"); //Configure DHCP host name 3.以太网库初始化 /********************************************************************* * @fn ETH_LibInit * * @brief Ethernet library initialization program * * @return command status */ uint8_t ETH_LibInit( uint8_t *ip, uint8_t *gwip, uint8_t *mask, uint8_t *macaddr ) { uint8_t s; struct _WCH_CFG cfg; memset(&cfg,0,sizeof(cfg)); cfg.TxBufSize = ETH_TX_BUF_SZE; cfg.TCPMss = WCHNET_TCP_MSS; cfg.HeapSize = WCHNET_MEM_HEAP_SIZE; cfg.ARPTableNum = WCHNET_NUM_ARP_TABLE; cfg.MiscConfig0 = WCHNET_MISC_CONFIG0; cfg.MiscConfig1 = WCHNET_MISC_CONFIG1; cfg.led_link = ETH_LedLinkSet; cfg.led_data = ETH_LedDataSet; cfg.net_send = ETH_TxPktChainMode; cfg.CheckValid = WCHNET_CFG_VALID; s = WCHNET_ConfigLIB(&cfg); if(s){ return (s); } s = WCHNET_Init(ip,gwip,mask,macaddr); ETH_Init(macaddr); return (s); } 4.启动DHCP,使用的库函数,回调函数里获取ip分配 /********************************************************************* * @fn WCHNET_DHCPCallBack * * @brief DHCPCallBack * * @param status - status returned by DHCP * 0x00 - Success * 0x01 - Failure * arg - Data returned by DHCP * * @return DHCP status */ u8 WCHNET_DHCPCallBack(u8 status, void *arg) { u8 *p; u8 tmp[4] = {0, 0, 0, 0}; if(!status) { p = arg; printf("DHCP Success\r\n"); /*If the obtained IP is the same as the last IP, exit this function.*/ if(!memcmp(IPAddr, p ,sizeof(IPAddr))) return READY; /*Determine whether it is the first successful IP acquisition*/ if(memcmp(IPAddr, tmp ,sizeof(IPAddr))){ /*The obtained IP is different from the last value, * then disconnect the last connection.*/ WCHNET_SocketClose(SocketId, TCP_CLOSE_NORMAL); } memcpy(IPAddr, p, 4); memcpy(GWIPAddr, &p[4], 4); memcpy(IPMask, &p[8], 4); printf("IPAddr = %d.%d.%d.%d \r\n", (u16)IPAddr[0], (u16)IPAddr[1], (u16)IPAddr[2], (u16)IPAddr[3]); printf("GWIPAddr = %d.%d.%d.%d \r\n", (u16)GWIPAddr[0], (u16)GWIPAddr[1], (u16)GWIPAddr[2], (u16)GWIPAddr[3]); printf("IPMask = %d.%d.%d.%d \r\n", (u16)IPMask[0], (u16)IPMask[1], (u16)IPMask[2], (u16)IPMask[3]); printf("DNS1: %d.%d.%d.%d \r\n", p[12], p[13], p[14], p[15]); printf("DNS2: %d.%d.%d.%d \r\n", p[16], p[17], p[18], p[19]); WCHNET_CreateTcpSocket(); //Create a TCP connection return READY; } else { printf("DHCP Fail %02x \r\n", status); /*Determine whether it is the first successful IP acquisition*/ if(memcmp(IPAddr, tmp ,sizeof(IPAddr))){ /*The obtained IP is different from the last value*/ WCHNET_SocketClose(SocketId, TCP_CLOSE_NORMAL); } return NoREADY; } } 5.主函数循环里调用wchnet主任务函数,其内由库组成 /********************************************************************* * @fn WCHNET_MainTask * * @brief library main task function * * @return none. */ void WCHNET_MainTask(void) { WCHNET_NetInput( ); /* Ethernet data input */ WCHNET_PeriodicHandle( ); /* Protocol stack time-related task processing */ WCHNET_HandlePhyNegotiation( ); } 6.检测中断事件,并做处理 /*检测以太网全局中断*/ WCHNET_QueryGlobalInt() /*获取以太网状态及处理*/ /********************************************************************* * @fn WCHNET_HandleGlobalInt * * @brief Global Interrupt Handle * * @return none */ void WCHNET_HandleGlobalInt(void) { u8 intstat; u16 i; u8 socketint; intstat = WCHNET_GetGlobalInt(); //get global interrupt flag if (intstat & GINT_STAT_UNREACH) //Unreachable interrupt { printf("GINT_STAT_UNREACH\r\n"); } if (intstat & GINT_STAT_IP_CONFLI) //IP conflict { printf("GINT_STAT_IP_CONFLI\r\n"); } if (intstat & GINT_STAT_PHY_CHANGE) //PHY status change { i = WCHNET_GetPHYStatus(); if (i & PHY_Linked_Status) printf("PHY Link Success\r\n"); } if (intstat & GINT_STAT_SOCKET) { //socket related interrupt for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) { socketint = WCHNET_GetSocketInt(i); if (socketint) WCHNET_HandleSockInt(i, socketint); } } } 7.main函数如下 /********************************************************************* * @fn main * * @brief Main program * * @return none */ int main(void) { u8 i; Delay_Init(); USART_Printf_Init(115200); //USART initialize printf("DHCP Test\r\n"); if((SystemCoreClock == 60000000) || (SystemCoreClock == 120000000)) printf("SystemClk:%d\r\n", SystemCoreClock); else printf("Error: Please choose 60MHz and 120MHz clock when using Ethernet!\r\n"); printf("net version:%x\n", WCHNET_GetVer()); if( WCHNET_LIB_VER != WCHNET_GetVer()){ printf("version error.\n"); } WCHNET_GetMacAddr(MACAddr); //get the chip MAC address printf("mac addr:"); for(i = 0; i < 6; i++) printf("%x ", MACAddr); printf("\n"); TIM2_Init(); WCHNET_DHCPSetHostname("WCHNET"); //Configure DHCP host name i = ETH_LibInit(IPAddr,GWIPAddr,IPMask,MACAddr); //Ethernet library initialize mStopIfError(i); if(i == WCHNET_ERR_SUCCESS) printf("WCHNET_LibInit Success\r\n"); WCHNET_DHCPStart(WCHNET_DHCPCallBack); //Start DHCP while(1) { /*Ethernet library main task function, * which needs to be called cyclically*/ WCHNET_MainTask(); /*Query the Ethernet global interrupt, * if there is an interrupt, call the global interrupt handler*/ if(WCHNET_QueryGlobalInt()) { WCHNET_HandleGlobalInt(); } } }   三.测验             编译烧录后,插上网线,上电,可看到日志如上,DHCP获取IP信息成功。  

  • 2024-07-07
  • 发表了主题帖: 【全能小网关|CH32V208】--6.蓝牙串口服务

    本帖最后由 dirty 于 2024-7-7 11:10 编辑       本篇讲述CH32V208蓝牙串口透传。 一.了解准备       沁恒低功耗蓝牙协议栈以及应用均基于 TMOS(Task Management Operating System),TMOS是一个控制循环,通过 TMOS 可设置事件的执行方式。TMOS 作为调度核心,BLE 协议栈、profile 定义、所有的应用都围绕它来实现。TMOS 不是传统意义上的操作系统,而是一种以实现多任务为核心的系统资源管理机制。TMOS 通过轮询的方式进行调度,系统时钟一般来源于 RTC。       对于一个任务,独一无二的任务 ID,任务的初始化以及任务下可执行的事件都是不可或缺的。每个Task 用户最多可以自定义 15 个事件,0x8000 为系统预留的SYS_EVENT_MSG 事件,即系统消息传递事件,不可被定义。       更多关于TMOS及协议栈使用可参考阅读官方文档。 二.代码准备 1.蓝牙协议栈初始化。包括协议栈起始地址、大小、时钟、发送功率、MAC、协议栈库初始化等。 /******************************************************************************* * @fn WCHBLE_Init * * [url=home.php?mod=space&uid=159083]@brief[/url] BLE library initialization * * @param None. * * [url=home.php?mod=space&uid=784970]@return[/url] None. */ void WCHBLE_Init(void) { uint8_t i; bleConfig_t cfg; g_LLE_IRQLibHandlerLocation = (uint32_t)LLE_IRQLibHandler; if(!tmos_memcmp(VER_LIB, VER_FILE, strlen(VER_FILE))) { PRINT("head file error...\n"); while(1); } // 32M crystal capacitance and current OSC->HSE_CAL_CTRL &= ~(0x07<<28); OSC->HSE_CAL_CTRL |= 0x03<<28; OSC->HSE_CAL_CTRL |= 3<<24; tmos_memset(&cfg, 0, sizeof(bleConfig_t)); cfg.MEMAddr = (uint32_t)MEM_BUF; cfg.MEMLen = (uint32_t)BLE_MEMHEAP_SIZE; cfg.BufMaxLen = (uint32_t)BLE_BUFF_MAX_LEN; cfg.BufNumber = (uint32_t)BLE_BUFF_NUM; cfg.TxNumEvent = (uint32_t)BLE_TX_NUM_EVENT; cfg.TxPower = (uint32_t)BLE_TX_POWER; #if(defined(BLE_SNV)) && (BLE_SNV == TRUE) cfg.SNVAddr = (uint32_t)BLE_SNV_ADDR; cfg.SNVNum = (uint32_t)BLE_SNV_NUM; cfg.readFlashCB = Lib_Read_Flash; cfg.writeFlashCB = Lib_Write_Flash; #endif cfg.ClockFrequency = CAB_LSIFQ/2; #if(CLK_OSC32K==0) cfg.ClockAccuracy = 50; #else cfg.ClockAccuracy = 1000; #endif cfg.ConnectNumber = (PERIPHERAL_MAX_CONNECTION & 3) | (CENTRAL_MAX_CONNECTION << 2); #if(defined TEM_SAMPLE) && (TEM_SAMPLE == TRUE) // Calibrate RF and internal RC according to temperature changes (greater than 7 degrees Celsius) cfg.tsCB = HAL_GetInterTempValue; #if(CLK_OSC32K) cfg.rcCB = Lib_Calibration_LSI; // Internal 32K clock calibration #endif #endif #if(defined(HAL_SLEEP)) && (HAL_SLEEP == TRUE) cfg.idleCB = BLE_LowPower; // Enable sleep #endif #if(defined(BLE_MAC)) && (BLE_MAC == TRUE) for(i = 0; i < 6; i++) { cfg.MacAddr[i] = MacAddr[5 - i]; } #else { uint8_t MacAddr[6]; FLASH_GetMACAddress(MacAddr); for(i = 0; i < 6; i++) { cfg.MacAddr[i] = MacAddr[i]; // Use chip mac address } } #endif if(!cfg.MEMAddr || cfg.MEMLen < 4 * 1024) { while(1); } i = BLE_LibInit(&cfg); if(i) { PRINT("LIB init error code: %x ...\n", i); while(1); } RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE ); NVIC_EnableIRQ( BB_IRQn ); NVIC_EnableIRQ( LLE_IRQn ); } 2.外设初始化。这里面用到了事件注册函数TMOS_ProcessEventRegister,校准任务函数tmos_start_task void HAL_Init() { halTaskID = TMOS_ProcessEventRegister(HAL_ProcessEvent); HAL_TimeInit(); #if(defined HAL_SLEEP) && (HAL_SLEEP == TRUE) HAL_SleepInit(); #endif #if(defined HAL_LED) && (HAL_LED == TRUE) HAL_LedInit(); #endif #if(defined HAL_KEY) && (HAL_KEY == TRUE) HAL_KeyInit(); #endif #if(defined BLE_CALIBRATION_ENABLE) && (BLE_CALIBRATION_ENABLE == TRUE) // Add a calibration task, and a single calibration takes less than 10ms tmos_start_task(halTaskID, HAL_REG_INIT_EVENT, MS1_TO_SYSTEM_TIME(BLE_CALIBRATION_PERIOD)); #endif // tmos_start_task(halTaskID, HAL_TEST_EVENT, MS1_TO_SYSTEM_TIME(1000)); // Add a test task } 3.低功耗蓝牙初始化。包含GAP参数设置(广播参数、连接参数、绑定参数等),GATT层服务的注册以及回调函数的注册 //扫描回包数据 static uint8_t scanRspData[] = { // complete name 13, // length of this data GAP_ADTYPE_LOCAL_NAME_COMPLETE, 'w', 'c', 'h', '_', 'b', 'l', 'e', '_', 'u', 'a', 'r', 't', // connection interval range 0x05, // length of this data GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE, LO_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL), // 100ms HI_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL), LO_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL), // 1s HI_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL), // Tx power level 0x02, // length of this data GAP_ADTYPE_POWER_LEVEL, 0 // 0dBm }; //蓝牙串口服务回调 /********************************************************************* * @fn on_bleuartServiceEvt * * @brief ble uart service callback handler * * @return NULL */ void on_bleuartServiceEvt(uint16_t connection_handle, ble_uart_evt_t *p_evt) { switch(p_evt->type) { case BLE_UART_EVT_TX_NOTI_DISABLED: PRINT("%02x:bleuart_EVT_TX_NOTI_DISABLED\r\n", connection_handle); break; case BLE_UART_EVT_TX_NOTI_ENABLED: PRINT("%02x:bleuart_EVT_TX_NOTI_ENABLED\r\n", connection_handle); break; case BLE_UART_EVT_BLE_DATA_RECIEVED: PRINT("BLE RX DATA len:%d\r\n", p_evt->data.length); //for notify back test //to ble uint16_t to_write_length = p_evt->data.length; app_drv_fifo_write(&app_uart_rx_fifo, (uint8_t *)p_evt->data.p_data, &to_write_length); tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2); //end of nofify back test PRINT("\r\n"); for(uint16_t i=0;i<to_write_length;i++) { PRINT("%02x ", app_uart_rx_fifo.data[i]); } PRINT("\r\nble to uart\r\n"); //ble to uart app_uart_tx_data((uint8_t *)p_evt->data.p_data, p_evt->data.length); break; default: break; } } void Peripheral_Init() { Peripheral_TaskID = TMOS_ProcessEventRegister(Peripheral_ProcessEvent); // Setup the GAP Peripheral Role Profile { uint8_t initial_advertising_enable = TRUE; uint16_t desired_min_interval = 6; uint16_t desired_max_interval = 1000; // Set the GAP Role Parameters GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable); GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData); GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData); GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16_t), &desired_min_interval); GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16_t), &desired_max_interval); } // Set advertising interval { uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL; GAP_SetParamValue(TGAP_DISC_ADV_INT_MIN, advInt); GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, advInt); } // Setup the GAP Bond Manager { uint32_t passkey = 0; // passkey "000000" uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ; uint8_t mitm = TRUE; uint8_t bonding = TRUE; uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY; GAPBondMgr_SetParameter(GAPBOND_PERI_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey); GAPBondMgr_SetParameter(GAPBOND_PERI_PAIRING_MODE, sizeof(uint8_t), &pairMode); GAPBondMgr_SetParameter(GAPBOND_PERI_MITM_PROTECTION, sizeof(uint8_t), &mitm); GAPBondMgr_SetParameter(GAPBOND_PERI_IO_CAPABILITIES, sizeof(uint8_t), &ioCap); GAPBondMgr_SetParameter(GAPBOND_PERI_BONDING_ENABLED, sizeof(uint8_t), &bonding); } // Initialize GATT attributes GGS_AddService(GATT_ALL_SERVICES); // GAP GATTServApp_AddService(GATT_ALL_SERVICES); // GATT attributes DevInfo_AddService(); // Device Information Service ble_uart_add_service(on_bleuartServiceEvt); // Set the GAP Characteristics GGS_SetParameter(GGS_DEVICE_NAME_ATT, sizeof(attDeviceName), attDeviceName); // Init Connection Item peripheralInitConnItem(&peripheralConnList); // Register receive scan request callback GAPRole_BroadcasterSetCB(&Broadcaster_BroadcasterCBs); // Setup a delayed profile startup tmos_set_event(Peripheral_TaskID, SBP_START_DEVICE_EVT); } 4.事件处理回调函数。这里面有实现串口接收数据蓝牙notify发送 /********************************************************************* * @fn Peripheral_ProcessEvent * * @brief Peripheral Application Task event processor. This function * is called to process all events for the task. Events * include timers, messages and any other user defined events. * * @param task_id - The TMOS assigned task ID. * @param events - events to process. This is a bit map and can * contain more than one event. * * @return events not processed */ uint16_t Peripheral_ProcessEvent(uint8_t task_id, uint16_t events) { static attHandleValueNoti_t noti; // VOID task_id; // TMOS required parameter that isn't used in this function if(events & SYS_EVENT_MSG) { uint8_t *pMsg; if((pMsg = tmos_msg_receive(Peripheral_TaskID)) != NULL) { Peripheral_ProcessTMOSMsg((tmos_event_hdr_t *)pMsg); // Release the TMOS message tmos_msg_deallocate(pMsg); } // return unprocessed events return (events ^ SYS_EVENT_MSG); } if(events & SBP_START_DEVICE_EVT) { // Start the Device GAPRole_PeripheralStartDevice(Peripheral_TaskID, &Peripheral_BondMgrCBs, &Peripheral_PeripheralCBs); return (events ^ SBP_START_DEVICE_EVT); } if(events & SBP_PARAM_UPDATE_EVT) { // Send connect param update request GAPRole_PeripheralConnParamUpdateReq(peripheralConnList.connHandle, DEFAULT_DESIRED_MIN_CONN_INTERVAL, DEFAULT_DESIRED_MAX_CONN_INTERVAL, DEFAULT_DESIRED_SLAVE_LATENCY, DEFAULT_DESIRED_CONN_TIMEOUT, Peripheral_TaskID); // GAPRole_PeripheralConnParamUpdateReq( peripheralConnList.connHandle, // 10, // 20, // 0, // 400, // Peripheral_TaskID); return (events ^ SBP_PARAM_UPDATE_EVT); } if(events & UART_TO_BLE_SEND_EVT) { static uint16_t read_length = 0; ; uint8_t result = 0xff; switch(send_to_ble_state) { case SEND_TO_BLE_TO_SEND: //notify is not enabled if(!ble_uart_notify_is_ready(peripheralConnList.connHandle)) { if(peripheralConnList.connHandle == GAP_CONNHANDLE_INIT) { //connection lost, flush rx fifo here app_drv_fifo_flush(&app_uart_rx_fifo); } break; } read_length = ATT_GetMTU(peripheralConnList.connHandle) - 3; if(app_drv_fifo_length(&app_uart_rx_fifo) >= read_length) { PRINT("FIFO_LEN:%d\r\n", app_drv_fifo_length(&app_uart_rx_fifo)); result = app_drv_fifo_read(&app_uart_rx_fifo, to_test_buffer, &read_length); uart_to_ble_send_evt_cnt = 0; } else { if(uart_to_ble_send_evt_cnt > 10) { result = app_drv_fifo_read(&app_uart_rx_fifo, to_test_buffer, &read_length); uart_to_ble_send_evt_cnt = 0; } else { tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 4); uart_to_ble_send_evt_cnt++; //PRINT("NO TIME OUT\r\n"); } } if(APP_DRV_FIFO_RESULT_SUCCESS == result) { noti.len = read_length; noti.pValue = GATT_bm_alloc(peripheralConnList.connHandle, ATT_HANDLE_VALUE_NOTI, noti.len, NULL, 0); if(noti.pValue != NULL) { tmos_memcpy(noti.pValue, to_test_buffer, noti.len); PRINT("uart to ble notify\r\n"); for(uint16_t i=0;i<read_length;i++) { PRINT("%02x ",to_test_buffer[i]); } PRINT("\r\n"); result = ble_uart_notify(peripheralConnList.connHandle, &noti, 0); if(result != SUCCESS) { PRINT("R1:%02x\r\n", result); send_to_ble_state = SEND_TO_BLE_SEND_FAILED; GATT_bm_free((gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI); tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2); } else { send_to_ble_state = SEND_TO_BLE_TO_SEND; //app_fifo_write(&app_uart_tx_fifo,to_test_buffer,&read_length); //app_drv_fifo_write(&app_uart_tx_fifo,to_test_buffer,&read_length); read_length = 0; tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2); } } else { send_to_ble_state = SEND_TO_BLE_ALLOC_FAILED; tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2); } } else { //send_to_ble_state = SEND_TO_BLE_FIFO_EMPTY; } break; case SEND_TO_BLE_ALLOC_FAILED: case SEND_TO_BLE_SEND_FAILED: noti.len = read_length; noti.pValue = GATT_bm_alloc(peripheralConnList.connHandle, ATT_HANDLE_VALUE_NOTI, noti.len, NULL, 0); if(noti.pValue != NULL) { tmos_memcpy(noti.pValue, to_test_buffer, noti.len); result = ble_uart_notify(peripheralConnList.connHandle, &noti, 0); if(result != SUCCESS) { PRINT("R2:%02x\r\n", result); send_to_ble_state = SEND_TO_BLE_SEND_FAILED; GATT_bm_free((gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI); tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2); } else { send_to_ble_state = SEND_TO_BLE_TO_SEND; //app_drv_fifo_write(&app_uart_tx_fifo,to_test_buffer,&read_length); read_length = 0; tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2); } } else { send_to_ble_state = SEND_TO_BLE_ALLOC_FAILED; tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2); } break; default: break; } return (events ^ UART_TO_BLE_SEND_EVT); } // Discard unknown events return 0; } 5.数据收发应用串口初始化。这里使用到UART3. /********************************************************************* * @fn app_uart_init * * @brief init uart * * @return NULL */ void app_uart_init() { //tx fifo and tx fifo //The buffer length should be a power of 2 app_drv_fifo_init(&app_uart_tx_fifo, app_uart_tx_buffer, APP_UART_TX_BUFFER_LENGTH); app_drv_fifo_init(&app_uart_rx_fifo, app_uart_rx_buffer, APP_UART_RX_BUFFER_LENGTH); GPIO_InitTypeDef GPIO_InitStructure = {0}; USART_InitTypeDef USART_InitStructure = {0}; NVIC_InitTypeDef NVIC_InitStructure = {0}; //uart3 init RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); /* USART3 TX-->B.10 RX-->B.11 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOB, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_Init(USART3, &USART_InitStructure); USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); USART_Cmd(USART3, ENABLE); } 6.主函数。这里main()的最后循环执行 TMOS_SystemProcess(),保证 TMOS 持续运行。app_uart_process处理串口接收及发送,并将接收到数据蓝牙发送。 void app_uart_process(void) { uint8_t data; __disable_irq(); if(uart_rx_flag) { tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2); uart_rx_flag = false; } __enable_irq(); while(app_drv_fifo_length(&app_uart_tx_fifo)) { app_drv_fifo_read_to_same_addr(&app_uart_tx_fifo, (uint8_t *)&data, 1); USART_SendData(USART3, data); while(USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET) /* waiting for sending finish */ { } } } /******************************************************************************* * Function Name : main * Description : Main function * Input : None * Output : None * Return : None *******************************************************************************/ int main(void) { SystemCoreClockUpdate(); Delay_Init(); #ifdef DEBUG USART_Printf_Init( 115200 ); #endif PRINT("%s\n", VER_LIB); WCHBLE_Init(); HAL_Init(); GAPRole_PeripheralInit(); Peripheral_Init(); app_uart_init(); while(1) { TMOS_SystemProcess(); app_uart_process(); } }   三.测验       编译烧录后运行。蓝牙广播如下: 图1:蓝牙广播       连接后,蓝牙串口服务如下: 图2:蓝牙串口服务       在nrf connect工具端打开消息通知使能,发送数据,串口端(UART3,图3右侧)将收到数据打印出来。在串口发送数据(UART3),可以看到左侧打印出调试日志并收到蓝牙数据通知如图4. 图3:串口日志。左侧调试串口,右侧数据通讯接收发送串口 图4:蓝牙串口服务调试         至此,实现WCH蓝牙串口服务功能,熟悉了对沁恒蓝牙功能的使用。

  • 2024-07-06
  • 发表了主题帖: 【全能小网关|CH32V208】--5.LVGL移植

          本篇讲述CH32V208移植LVGL,将在前面讲过ST7735 SPILCD驱动基础上展开。 一.准备工作     下载LVGL源码:https://github.com/lvgl/lvgl/tree/release/v8.3 二.移植LVGL 1.配置1ms定时器,提供LVGL心跳。配置使用SYSTICK_Init_Config(SystemCoreClock/1000-1); void SYSTICK_Init_Config(u64 ticks) { SysTick->SR = 0; SysTick->CNT = 0; SysTick->CMP = ticks; SysTick->CTLR =0xF; NVIC_SetPriority(SysTicK_IRQn, 1); NVIC_EnableIRQ(SysTicK_IRQn); } void SysTick_Handler(void) { SysTick->SR = 0; lv_tick_inc(1); counter++; if(counter>=1000) { counter=0; printf("welcome to WCH\r\n"); // printf("Counter:%d\r\n",counter); } } 2.显示初始化。 #define MY_DISP_HOR_RES 160 #define MY_DISP_VER_RES 128 void lv_port_disp_init(void) { /*------------------------- * Initialize your display * -----------------------*/ disp_init(); /*----------------------------- * Create a buffer for drawing *----------------------------*/ /** * LVGL requires a buffer where it internally draws the widgets. * Later this buffer will passed to your display driver's `flush_cb` to copy its content to your display. * The buffer has to be greater than 1 display row * * There are 3 buffering configurations: * 1. Create ONE buffer: * LVGL will draw the display's content here and writes it to your display * * 2. Create TWO buffer: * LVGL will draw the display's content to a buffer and writes it your display. * You should use DMA to write the buffer's content to the display. * It will enable LVGL to draw the next part of the screen to the other buffer while * the data is being sent form the first buffer. It makes rendering and flushing parallel. * * 3. Double buffering * Set 2 screens sized buffers and set disp_drv.full_refresh = 1. * This way LVGL will always provide the whole rendered screen in `flush_cb` * and you only need to change the frame buffer's address. */ /* Example for 1) */ static lv_disp_draw_buf_t draw_buf_dsc_1; static lv_color_t buf_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/ lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/ #if 0 /* Example for 2) */ static lv_disp_draw_buf_t draw_buf_dsc_2; static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/ static lv_color_t buf_2_2[MY_DISP_HOR_RES * 10]; /*An other buffer for 10 rows*/ lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/ /* Example for 3) also set disp_drv.full_refresh = 1 below*/ static lv_disp_draw_buf_t draw_buf_dsc_3; static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*A screen sized buffer*/ static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*Another screen sized buffer*/ lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2, MY_DISP_VER_RES * LV_VER_RES_MAX); /*Initialize the display buffer*/ #endif /*----------------------------------- * Register the display in LVGL *----------------------------------*/ static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/ lv_disp_drv_init(&disp_drv); /*Basic initialization*/ /*Set up the functions to access to your display*/ /*Set the resolution of the display*/ disp_drv.hor_res = MY_DISP_HOR_RES; disp_drv.ver_res = MY_DISP_VER_RES; /*Used to copy the buffer's content to the display*/ disp_drv.flush_cb = disp_flush; /*Set a display buffer*/ disp_drv.draw_buf = &draw_buf_dsc_1; /*Required for Example 3)*/ //disp_drv.full_refresh = 1; /* Fill a memory array with a color if you have GPU. * Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL. * But if you have a different GPU you can use with this callback.*/ //disp_drv.gpu_fill_cb = gpu_fill; /*Finally register the driver*/ lv_disp_drv_register(&disp_drv); } 3.disp_flush实现 void LCD_DrawPoint(u16 x,u16 y,u16 Data) { LCD_SetWindows(x,y,x+1,y+1); Lcd_WriteData_16Bit(Data); } /*Flush the content of the internal buffer the specific area on the display *You can use DMA or any hardware acceleration to do this operation in the background but *'lv_disp_flush_ready()' has to be called when finished.*/ static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { if(disp_flush_enabled) { /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/ int32_t x; int32_t y; for(y = area->y1; y <= area->y2; y++) { for(x = area->x1; x <= area->x2; x++) { /*Put a pixel to the display. For example:*/ /*put_px(x, y, *color_p)*/ LCD_DrawPoint(x,y,lv_color_to16(*color_p)); color_p++; } } } /*IMPORTANT!!! *Inform the graphics library that you are ready with the flushing*/ lv_disp_flush_ready(disp_drv); } 4.lv_config.h配置.这里依需配置修改 5.设计界面 void lv_ex_label_1(void) { lv_obj_t* btn = lv_btn_create(lv_scr_act()); lv_obj_set_pos(btn, 40, 44); lv_obj_set_size(btn, 80, 40); lv_obj_t* label = lv_label_create(btn); lv_label_set_text(label, "Button"); lv_obj_center(label); } 6.main函数实现 int main(void) { SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init(115200); printf("SystemClk:%d\r\n", SystemCoreClock); // SPI_FLASH_Init(); LCD_Init(); lv_init(); lv_port_disp_init(); SYSTICK_Init_Config(SystemCoreClock/1000-1); lv_ex_label_1(); while (1) { Delay_Ms(5); lv_task_handler(); } }        此外,注意在MRS 添加源文件及路径以及配置文件Link.ld的调整修改等。   三.测验       编译烧录后,LCD屏显示如下:         至此实现CH32V208开发板LVGL移植。  

  • 2024-06-29
  • 发表了主题帖: 《机器学习算法与实现 —— Python编程与应用实例》开卷有益

    本帖最后由 dirty 于 2024-6-29 23:02 编辑       收到《机器学习算法与实现 —— Python编程与应用实例》这本书,翻阅了目录和全书,确实是一本很不错学习书籍,对平台及提供方、编著者表示感谢。全书内容详实,排版工整有序, 学习曲线循序渐进,条理明晰,理论与实践相结合,是非常适合工程人员学习参阅的读本。下面一睹为快。       本篇由绪论切入,以对机器学习有一个较全面的理解。       机器学习是-类算法的总称,这些算法能够从大量历史数据中挖掘出隐含的规律,并用于分类、回归和聚类。机器学习更简单和形象的定义是寻找从输入样本数据到期望输出结果之间的映射函数。       机器学习的基本思路如下:①收集并整理数据;②将现实问题抽象成数学模型;③利用机器学习方法求解数问题;④评估求解得到的数学模型。       机器学习是人工智能(Anihciallnteigence,AI)的一个方向,伴随着人工智能的发展,机器学习被人们提出和发展。人工智能是研究、开发用子模拟、延伸和扩展人类智能的理论、方法、技术及应用系统的技术科学,其目的就是让机器能像人一样思考,让机器拥有智能。       机器学习的基本术语: 在机器学习中,特征(Feature)是被观测目标的一个独立可观测的属性或特点,其主要特点是存在信息量、区别性和独立性。 样本是数据的特定实例。样本分为两类:有标签样本和无标签样本。 模型定义特征与标签之间的关系,一般可以视为一个由参数定义的函数或逻辑操作的集合。       机器学习的基本应用是回归(Regesion)、分类(Classifcation)和聚类(Clustering)。       通过数据学习得到模型的过程就是学习(Leaming),也称训练(Training)。在学习过程中,首先根据训练数据集构建一个模型,然后将该模型用于此前从未见过的新数据的过程,被称为模型的泛化(Generalization)。       机器学习的基本分类: 监督学习;无监督学习;半监督学习。       根据人工种经网络"的层数或者机器学习方法的非线性处理深度,可以将机器学习算法分为浅学习算法和深度学习算法。浅层学习算法的典型代表是感知机,深度神经网络主要是指处理层数转季的神经网络。深度学习是机器学习领域的一个新研究方向,将深度学习引入机器学习的目的是使后者更接近最初的目标-一人工智能。深度学习的灵感源于人类大脑的工作方式,是利用深度神经网鳍来解决特征表达的一种学习过程。       强化学习(Reinforccment Leaming,RL)又称再励学习、评价学习或增强学习,用于描述和决智能体与环境交互的学习策略,以达成回报最大化或者实现特定目标的问题。       机器学习与人工智能关系       机器学习的应用:图像识别与处理;语音识别与自然语言处理;环境感知与智能决策;融合物理信息的工程设计。       机器学习应用的步骤:选择合适的模型。判断函数的好坏。找出“最好”的函数的模型参数。学习得到“最好”的函数的参数后,需要在新样本上进行测试       数据集一般需要分割为训练集、测试集和验证集。      性能度量(Perfomance Measure)是衡量模型泛化能力的数值评价标准,反映了所求解模型性能。      如何学习机器学习:由浅入深;行成于思。        绪论部分是对全书的引入与初步概要,通过阅读理解,对机器学习有一个初步全局的了解,为后面章节的阅读学习奠定了一个良好的基础与准备。  

  • 发表了主题帖: 【全能小网关|CH32V208】--4.LCD显示

    本帖最后由 dirty 于 2024-6-29 21:59 编辑       本篇讲述使用沁恒CH32V208驱动LCD屏显示。 一.硬件准备       准备一块SPI接口LCD屏,分辨率128*160,驱动芯片ST7735。使用主控SPI2接口,硬件连接如下: 图1:LCD连接     LCD_BLK    -- PB9     LCD_DC     -- PB10     LCD_RST    -- PB11     LCD_CS     -- PB12     LCD_SCL    -- PB13(SPI2_SCK)        LCD_SDA   -- PB15(SPI2_MOSI)    二.代码准备 1.引脚宏定义与初始化 #define LCD_LED_RCC RCC_APB2Periph_GPIOB #define LCD_LED_PORT GPIOB #define LCD_LED_PIN (GPIO_Pin_9) #define LCD_DC_RCC RCC_APB2Periph_GPIOB #define LCD_DC_PORT GPIOB #define LCD_DC_PIN (GPIO_Pin_10) #define LCD_RST_RCC RCC_APB2Periph_GPIOB #define LCD_RST_PORT GPIOB #define LCD_RST_PIN (GPIO_Pin_11) #define LCD_CS_RCC RCC_APB2Periph_GPIOB #define LCD_CS_PORT GPIOB #define LCD_CS_PIN (GPIO_Pin_12) /********************************************************************* * @fn SPI2_Init * * @brief Initialize SPI2 * * @return none */ void SPI2_Init(void) { GPIO_InitTypeDef GPIO_InitStructure = {0}; SPI_InitTypeDef SPI_InitStructure = {0}; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(SPI2, &SPI_InitStructure); SPI_Cmd(SPI2, ENABLE); } /********************************************************************* * @fn SPI2_DMA_Init * * @brief Initialize DMA for SPI2 * * @return none */ void SPI2_DMA_Init() { DMA_InitTypeDef DMA_InitStructure = {0}; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(SPI2_DMA_TX_CH); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DATAR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)NULL; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = 0; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(SPI2_DMA_TX_CH, &DMA_InitStructure); SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE); } /********************************************************************* * @fn LCD_GPIOInit * * @brief Configuring the control gpio for the lcd screen * * @return none */ void LCD_GPIOInit(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(LCD_LED_RCC, ENABLE); RCC_APB2PeriphClockCmd(LCD_DC_RCC, ENABLE); RCC_APB2PeriphClockCmd(LCD_RST_RCC, ENABLE); RCC_APB2PeriphClockCmd(LCD_CS_RCC, ENABLE); GPIO_InitStructure.GPIO_Pin = LCD_LED_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(LCD_LED_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = LCD_DC_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(LCD_DC_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = LCD_RST_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(LCD_RST_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = LCD_CS_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(LCD_CS_PORT, &GPIO_InitStructure); } 2.LCD写命令函数 /********************************************************************* * @fn SPI2_Write * * @brief send one byte * * @param data - the data to send * * @return none */ void SPI2_Write(uint8_t data) { while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) ; SPI_I2S_SendData(SPI2, data); while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) == SET) ; } /********************************************************************* * @fn LCD_WR_REG * * @brief Write an 8-bit command to the LCD screen * * @param data - Command value to be written * * @return none */ static void LCD_WR_REG(uint8_t data) { LCD_CS_CLR; LCD_DC_CLR; SPI2_Write(data); LCD_CS_SET; } 3.LCD写数据函数 /********************************************************************* * @fn LCD_WR_DATA * * @brief Write an 8-bit data to the LCD screen * * @param data - the data to write * */ static void LCD_WR_DATA(uint8_t data) { LCD_CS_CLR; LCD_DC_SET; SPI2_Write(data); LCD_CS_SET; } 4.LCD设置显示方向及像素宏定义。 /* LCD Screen Definition */ #define USE_HORIZONTAL 0 #define LCD_W (128u)//(240u) #define LCD_H (160u)//(320u) /********************************************************************* * @fn LCD_Init * * @brief Initialization LCD screen * * @return none */ void LCD_Init(void) { SPI2_Init(); SPI2_DMA_Init(); LCD_GPIOInit(); LCD_RESET(); LCD_LED_SET; //LCD Init For 1.44Inch LCD Panel with ST7735R. LCD_WR_REG(0x11);//Sleep exit Delay_Ms(120); #if 1 LCD_WR_REG(0xB1); LCD_WR_DATA(0x00);//0x05 LCD_WR_DATA(0x08);//0x3A LCD_WR_DATA(0x05);//0x3A LCD_WR_REG(0xB2); LCD_WR_DATA(0x05); LCD_WR_DATA(0x3A); LCD_WR_DATA(0x3A); LCD_WR_REG(0xB3); LCD_WR_DATA(0x05); LCD_WR_DATA(0x3A); LCD_WR_DATA(0x3A); LCD_WR_DATA(0x05); LCD_WR_DATA(0x3A); LCD_WR_DATA(0x3A); //------------------------------------End ST7735S Frame Rate-----------------------------------------// LCD_WR_REG(0xB4); //Dot inversion LCD_WR_DATA(0x03); //------------------------------------ST7735S Power Sequence-----------------------------------------// LCD_WR_REG(0xC0); LCD_WR_DATA(0x62); LCD_WR_DATA(0x02); LCD_WR_DATA(0x04); LCD_WR_REG(0xC1); LCD_WR_DATA(0xC0); LCD_WR_REG(0xC2); LCD_WR_DATA(0x0D); LCD_WR_DATA(0x00); LCD_WR_REG(0xC3); LCD_WR_DATA(0x8D); LCD_WR_DATA(0x6A); LCD_WR_REG(0xC4); LCD_WR_DATA(0x8D); LCD_WR_DATA(0xEE); //---------------------------------End ST7735S Power Sequence-------------------------------------// LCD_WR_REG(0xC5); //VCOM LCD_WR_DATA(0x08);//0x12 //------------------------------------ST7735S Gamma Sequence-----------------------------------------// LCD_WR_REG(0xE0); LCD_WR_DATA(0x03); LCD_WR_DATA(0x1B); LCD_WR_DATA(0x12); LCD_WR_DATA(0x11); LCD_WR_DATA(0x3F); LCD_WR_DATA(0x3A); LCD_WR_DATA(0x32); LCD_WR_DATA(0x34); LCD_WR_DATA(0x2F); LCD_WR_DATA(0x2B); LCD_WR_DATA(0x30); LCD_WR_DATA(0x3A); LCD_WR_DATA(0x00); LCD_WR_DATA(0x01); LCD_WR_DATA(0x02); LCD_WR_DATA(0x05); LCD_WR_REG(0xE1); LCD_WR_DATA(0x03); LCD_WR_DATA(0x1B); LCD_WR_DATA(0x12); LCD_WR_DATA(0x11); LCD_WR_DATA(0x32); LCD_WR_DATA(0x2F); LCD_WR_DATA(0x2A); LCD_WR_DATA(0x2F); LCD_WR_DATA(0x2E); LCD_WR_DATA(0x2C); LCD_WR_DATA(0x35); LCD_WR_DATA(0x3F); LCD_WR_DATA(0x00); LCD_WR_DATA(0x00); LCD_WR_DATA(0x01); LCD_WR_DATA(0x05); //------------------------------------End ST7735S Gamma Sequence-----------------------------------------// LCD_WR_REG(0x3A); //65k mode LCD_WR_DATA(0x05); //LCD_WR_REG(0x36); //65k mode //LCD_WR_DATA(0x40); LCD_direction(USE_HORIZONTAL);//设置图像旋转角度 LCD_WR_REG(0x29); //Display on LCD_WR_REG(0x2C); LCD_WR_REG(0x29);//Display on } 5.清屏 /********************************************************************* * @fn LCD_Clear * * @brief Full screen filled LCD screen * * @param Color - Filled color * * @return none */ void LCD_Clear(uint16_t Color) { unsigned int i, m; LCD_SetWindows(0, 0, width - 1, height - 1); LCD_CS_CLR; LCD_DC_SET; //LCD_WR_REG(0x2C);//add for (i = 0; i < height; i++) { for (m = 0; m < width; m++) { LCD_WR_DATA_16Bit(Color); } } LCD_CS_SET; } 6.设置区域 /********************************************************************* * @fn LCD_SetWindows * * @brief Setting LCD display window * * @param xStar - the bebinning x coordinate of the LCD display window * yStar - the bebinning y coordinate of the LCD display window * xEnd - the endning x coordinate of the LCD display window * yEnd - the endning y coordinate of the LCD display window * * @return none */ void LCD_SetWindows(uint16_t xStar, uint16_t yStar, uint16_t xEnd, uint16_t yEnd) { LCD_WR_REG(LCD_SET_X); LCD_WR_DATA(xStar >> 8); LCD_WR_DATA(0x00FF & xStar); LCD_WR_DATA(xEnd >> 8); LCD_WR_DATA(0x00FF & xEnd); LCD_WR_REG(LCD_SET_Y); LCD_WR_DATA(yStar >> 8); LCD_WR_DATA(0x00FF & yStar); LCD_WR_DATA(yEnd >> 8); LCD_WR_DATA(0x00FF & yEnd); LCD_WR_REG(LCD_MEM_WRITE); } 7.显示图片。使用图片取模软件Img2Lcd生成图片字模数组。 /****************************************************************************** 函数说明:显示图片 入口数据:x,y起点坐标 length 图片长度 width 图片宽度 pic[] 图片数组 返回值: 无 ******************************************************************************/ void LCD_ShowPicture(uint16_t x,uint16_t y,uint16_t length,uint16_t width,const uint8_t pic[]) { uint16_t i,j; uint32_t k=0; unsigned char picH,picL; // Lcd_SetRegion(x,y,x+length-1,y+width-1); LCD_SetWindows(x,y,x+length-1,y+width-1); for(i=0;i<length;i++) { for(j=0;j<width;j++) { picL=*(pic+k*2); //数据低位在前 picH=*(pic+k*2+1); LCD_WR_DATA_16Bit(picH<<8|picL); k++; } } } 8.设计显示界面,开机刷屏红、绿、蓝,然后显示沁恒LOGO及开发板照片。 int main(void) { // uint32_t addr = 0; // uint16_t i = 0; SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init(115200); printf("SystemClk:%d\r\n", SystemCoreClock); //SPI_FLASH_Init(); LCD_Init(); printf("LCD_Init\r\n"); LCD_Clear(LCD_RED); Delay_Ms(1000); printf("LCD_RED\r\n"); LCD_Clear(LCD_GRED); Delay_Ms(1000); printf("LCD_GRED\r\n"); LCD_Clear(LCD_BLUE); Delay_Ms(1000); printf("LCD_BLUE\r\n"); LCD_ShowPicture(0,0,128,25,gImage_img_logo); LCD_ShowPicture(0,25,128,135,gImage_img_board); while (1) { Delay_Ms(2000); // addr = 0; // i = 0; // while (i < 11) // { // i++; // LCD_drawImageWithSize(0, 0, LCD_W, LCD_H, addr); // addr += (LCD_W * LCD_H * COLOR_BYTE); // } } }   三.测验       编译烧录后,LCD竖屏显示红、绿、蓝,最后显示图片界面。   图2:LCD显示 [localvideo]38f2312badca0396cd4b29c6746164b1[/localvideo]       至此,实现LCD驱动显示。  

  • 2024-06-24
  • 回复了主题帖: 【兆易GD32H759I-EVAL】--16.边缘AI目标检测

    wangerxian 发表于 2024-6-24 15:20 那600MHz处理图像还是很吃力的。 毕竟不是带NPU的,上的也是轻量模型。这芯片能做边缘AI方面的,一般场景还是够用,相比较其他同类芯片而言,还是很不错了

  • 回复了主题帖: 【兆易GD32H759I-EVAL】--13.LCD触摸功能

    本帖最后由 dirty 于 2024-6-24 23:40 编辑 LCD触摸工程代码  

  • 回复了主题帖: 【兆易GD32H759I-EVAL】--9.摄像头屏显

    本帖最后由 dirty 于 2024-6-24 23:38 编辑 摄像头屏显工程代码  

  • 回复了主题帖: 【兆易GD32H759I-EVAL】--8.LVGL移植

    本帖最后由 dirty 于 2024-6-24 23:40 编辑 LVGL移植工程代码

  • 回复了主题帖: 【兆易GD32H759I-EVAL】--16.边缘AI目标检测

    本帖最后由 dirty 于 2024-6-24 23:32 编辑 wangerxian 发表于 2024-6-23 12:04 600MHz,图片视频频率能达到多少帧? 帧率这应用没直接一直刷帧测,因做AI,获取一帧后AI处理,用时在360ms左右,然后显示结果用时在30ms左右,依此循环。

最近访客

< 1/5 >

统计信息

已有1064人来访过

  • 芯积分:517
  • 好友:23
  • 主题:107
  • 回复:298

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言