本教程视频版已经发布在CW32生态社区视频号与B站号上:
https://space.bilibili.com/3493144165354211/lists/1803525?type=season
Ⅰ、模块移植
1.CW32F030的MCU运用:此一步骤没有太多好讲的,涉及到自身个人毅力,学习时间的长短等不可控因素;需自身去学习,但其实在此项目中没用多少外设,能熟练运用GPIO,EXTI,USART,SPI就够了(IIC是模拟的);如果没有基础的话最好集中学习。
2.OLED屏幕驱动:
前面说了此屏幕想让它显示我们要显示的数据,需通过IIC协议向其发送命令设置好寄存器,再通过发送数据对OLED的显存进行操作也就是让相应的像素点点亮就行了;
下面来详解一下如何从0到显示一个’1‘出来:
我们所使用的是模拟IIC,意思就是使用GPIO,通过置1置0来模拟IIC中的高低电平,通过改变置1置0的延时时间从而改变IIC的速率;
如图1:
图1这个结构描述了IIC通讯时的基本结构,包含起始位,8位数据,应答位,结束位;此结构也是IIC发送一个8位数据时需要遵循的结构,这也是IIC的特性;我们需要的是使用我们的GPIO通过置1置0以及延时来模拟此IIC的结构,也就是起始位,8位数据,应答位,结束位。
图2:
如图2想要使用我们的GPIO通过置1置0以及延时来模拟此IIC的结构,那么就要了解起始位,8位数据,应答位,结束位的具体结构;可以看到上述协议帧从左往右:START是起始位,想使用GPIO模拟此时序只需让模拟的SDA,SCL全置1然后让SDA置0,SCL暂不用管,延时(或不用延时)一会,再将SCL置0就行了,随后这么一写,封装成一个函数那不就成了吗:
结束位也是一样只需让模拟的SDA,SCL全置0然后让SCL置1,SDA暂不用管,延时(或不用延时)一会,再将SDA置1就行了:
应答信号最为简单,因为不涉及到读取数据所以不需要向对方表明我方有没有读取到数据:
写一个字节就复杂一点点,需要循环8个SCL与SDA,每一个循环都表达一位的数据,也就是向外输出8位,发送一个字节数据:
上述函数一个一个封装起来就相当于是模拟出了起始位,8位数据,应答位,结束位,分别按顺序调用这些个函数就是完整的IIC协议了;要注意的是OLED_SDA_OUT();上述函数中此函数是相当于将IO口配置为推挽输出的,在当前环境下,此函数只需要配置一次就行,因为在需要读取数据时才需要将IO口配置为输入模式。
图3:
如图3在我们模拟出了起始位,8位数据,应答位,结束位后,分别按顺序调用这些个函数就是完整的IIC协议,上述就是两个实例,上面将起始位,8位数据,应答位,结束位按一个顺序分别排列,其中发送的数据0X78,0X00,0X40分别代表OLED的地址,即将写入命令,即将写入数据三种意思;这些数据已经不属于IIC协议的范畴了,不同但符合IIC协议的设备这几个值是不一样的;通过上面的函数写入命令,下面的函数写入数据,OLED就能正常接收到我们发送过去的数据了。
图4:
如图4:上面我们说过使用发送命令函数对一些相应的寄存器进行设置后,再使用发送数据函数就对显存进行操作就行,在我们使用GPIO模拟出了起始位,8位数据,应答位,结束位后,也封装出了发送命令函数和发送数据函数;现在使用发送命令的函数向OLED的寄存器进行一些设置,具体要设置啥得自己翻译去,网上找找肯定也有中文注释,大概就是设置一些像范围,是否反向显示等等;进行完这一步后,就来到了最终的发送数据来显示我们想要的’1‘了。
图5:
如图5这个函数是把我们想要显示的字符与我们向括号填入的字符联系起来的函数,因为假设我们填入’1‘这个字符进去,OLED是不认识的,我们需要给OLED提供一个字库,F8X16[c*16+i]就描述的是8*16像素点大小的字库,里面描述了0-9分别应该怎么显示,字库包含的数据与我们要显示的数据是一 一对应的,字库就是OLED的语言,’1‘属于我们人类的语言。
图6:
就像这个0x00, 0x00, 0x42, 0x7F, 0x40, 0x00,这是OLED认识的’1‘,我们将此6个数据发往OLED那么OLED就会在屏幕上显示为’1‘,中间涉及到一个取模的过程;仔细看看可以看出将此6个字节表达的数据按顺序写成2进制,2进制里所有的’1‘连在一起其实就可以看出里面的数据表达的就是我们人所认识的向右倒地的’1‘。
至此如何从0到显示一个’1‘出来结束。
陀螺仪的IIC驱动与OLED差不多,起始位,8位数据,应答位,结束位都是共用的,区别在于(1)多了一个读取数据的函数,(2)需要等待应答信号,(3)需要发送应答或不应答信号,(4)以及上述讲的陀螺仪的地址,写入命令与写入数据的差别。
图7:
如图7此函数描述了如何从SDA引脚读取8位数据,与IIC的发送8为数据类似,都是需要循环8次;区别在(1)需要提前MPU_SDA_IN();也就是将SDA引脚设置为输入,(2)需要定义一个8位局部变量每读一位数据向左偏移一位以存储起来,循环8次正好储存一整字节,(3)在接收完一个字节后,不论是不是接收到了正确的数据,一定得发送是否应答的信号。
图8.1,8.2,8.3
第一张图是上面所说的描述了需要等待对方是否应答,因为在此种通讯模式下需要反复确认对方是不是在线,有没有正确接收到发过去的数据;第二张与第三张描述了发送应答信号或是不应答信号,应答对方设备就会认为你是正常的会继续发数据,不应答直接结束通讯。
到此之上都是属于IIC的协议层接下来就是设备独有的了。
图9:
图9此两个函数是融入了MPU6050的特性的,从这里就可以看出MPU与OLED使用IIC的不同,OLED发送8位数据0X78,0X00,0X40分别代表OLED的地址,即将写入命令,即将写入数据三种意思;MPU则是((MPU_ADDR<<1)|0),((MPU_ADDR<<1)|1)分别代表写命令和读数据,两者使用IIC的区别一目了然且都完美符合IIC协议;在进行完上面的操作后,就是对设备的相应寄存器进行设置,设置好后就可以读数据了。
图10:
如图10此函数是最终使用IIC的最关键一步,对相应的寄存器进行设置,上面有中文注释是最好的了,看看中文注释大概也能明白每个及寄存器都是怎么设置的;在调用完此函数之后:
图11:
如图11调用此两个函数传入6个指针就能得到三轴的角速度与加速度了,使用变量保存下来这就是原始值了,就可以用这原始值再做处理得到三轴对地角度(使用一阶互补过滤+卡尔曼滤波)去用;或者是我下面要介绍的方法(使用内部DMP库):
1.首先要获取DMP库,这个库比较常见,也可以在我这个工程中复制过去:
添加里面的两个的源文件到工程中,再指定头文件路径到此文件夹;
2.分别点开两个源文件可以看到一些毫秒延时,这部分可能需要你用你定义的毫秒延时函数替换,这是DMP库需要用到的;
3.在定义好了上述所有的读一个字节和写一个字节后,还需要自己去实现两个函数:
uint8_t MPU_Write_Len(uint8_t addr,uint8_t reg,uint8_t len,uint8_t *buf);
uint8_t MPU_Read_Len(uint8_t addr,uint8_t reg,uint8_t len,uint8_t *buf);这两个函数就是连续写连续读,是DMP库需要用到的,最好将一下几个文件全都移植过去,不然极易出错:
最后全部函数定义好后,调用uint8_t mpu_dmp_get_data(float *pitch,float *roll,float *yaw)函数就可以直接得到三轴对地的角度了,就可以直接使用了。
4.超声波模块驱动:以下的模块就都比较简单了,使用GPIO与TIM就行:
从上面的时序图就可以看到超声波模块是怎么使用的了:
1.初始化发射与接收引脚,初始化定时器,并在初始化时让发射引脚置0;
2.将发射引脚置1延时15us在置0,触发模块内部循环发出超声波脉冲;
3.将接收中断开启,触发方式为上伸沿和下降沿,在中断触发时检测为高电平打开定时器开始计数;
4.当中断再次被触发时检测电平为低时关闭定时器,保留计数,清除计数值;
5.根据设置的重装载值和预分频值求出每一次的时间乘上计数值就是声音在模块与障碍物运动的时间,声速340m/s求出距离。
5.蜂鸣器模块驱动:
常见的蜂鸣器频率为2700HZ,有不同的可问问卖家或数据手册;
1.初始化TIM,使 主频/(预分频系数*自动重装载值) 大概等于2700就行了;
2.设置蜂鸣器占空比,使能定时器就可以响了,改变占空比,就可以改变响度。
6.红外传感器使用:
初始化IO口,开启下降沿中断就行,触发中断后就在中断里处理就行。
Ⅱ、功能模块学习
1.语音模块选型及其编程方法:
选型的话选择了一款常见的可编程语音模块ASRPRO,32位的MCU,RISC内核架构,外围电路只需要加上麦克风和喇叭;
(1)编译环境:直接搜索天问Block下载就行
(2)选择主板,检查串口连接:通过ASRPRO的串口0下载程序,PB5是TX,PB6是RX
(3)界面说明:
(4)打开范例程序,设置编译下载模式:
(5)编译下载范例程序,随后下载成功后按照需要的修改修改随后重新生成模型下载即可:
2.蓝牙模块使用及其适配软件:
使用的蓝牙模块是BT04-E,支持BLE,SPP;传输稳定,距离远,且官方有适配的软件,使用串口通讯就行
其中的智能小车组件刚好可以用:
只需要使用USB转TTL连接上此蓝牙的串口,默认蓝牙串口波特率为9600,通过点击上述的按钮在电脑上打印出来就知道每个按钮对应什么数值了,使用这些数值,配合switch语句点击不同按钮,执行不同的小车向前向后语句,就能轻松使用了。
3.思考避障模块在程序中的逻辑嵌入:
此逻辑在程序中才能体现,后续在程序加上就行。
4.麦克风阵列的驱动与使用:
在此项目中需要的功能是检测声源的方位,驱动主控Maix bit是基于嘉楠堪智科技的边缘智能计算芯片K210(RISC-V架构 64位双核)设计的一款AIOT开发板;
配合6+1麦克风阵列就能检测到6个方向的声源大小,具体步骤:
1.配置开发环境:
(1).安装 USB 驱动 - Sipeed Wiki
(2).MaixPy IDE 安装与使用 - Sipeed Wiki
2.对声源定位例程略微修改下,给即将发出的数据限定一个最小值:
3.使用下载软件uPyLoader将固件中的main.py替换成我们所写的main.py(由声源定位改名而来),就可以使开机直接执行我们所修改的程序。
Ⅲ、硬件设计
硬件设计没有太多好讲的,没有涉及到多少模电数电的多少知识,这里挑选一些注意点讲讲:
此图画的有些随意,但这么画主要就是想看起来功耗分配的清晰,有哪些模块,每个模块都想在不用时使用PMOS与NMOS的结构来断电实现无功耗;
1.12V转5V按照数据手册来画原理图就行,标准的12转5V,但是手册描述的0-15V时电感大小为33uH,0-30V时电感为47uH,于是将电感改成了47uH。
2.USB转串口主要是为了调试语音模块,蓝牙模块,声源定位留的,后面有6路开关分别就是选择这3对功能了。
3.MPU6050电路下的语音+声源定位转接由6P的XH2.54的接口接到扩展板上。
4.然后就是蓝牙模块电路,WS2812,避障及警示电路三种电路都有共同PMOS+NMOS结构,PMOS通常用于上管控制,当时想的就是想要在运行其中一种模式时,其它的模式通过MOS管直接断电实现0功耗,每个MOS管都并联了个小开关也是调试用;
CW32的每个IO引脚内部结构都由保护电路,由上下两个同方向的二极管钳位外部的电压不得大于3.7V左右,不得小于-0.3V左右;
所以想控制5V输入给电路,就是用了这种结构,每个电路都使用了单独的5V转3.3V保证各个电路的3.3V不会互相打扰。
Ⅳ、程序联调
1.确定一个思路将所有功能组织在一个大循环里就行:由于之前我们设置好了MPU6050的寄存器,每10ms会进一次中断,我们借此使整个大循环10MS执行一次,实现这种操作只需要:
设立一个触发标志位,在大循环中判断标志位执行程序即可,由于标志位是每10ms会被置位,主循环执行一遍又是小于10ms的,所以主循环每一个10ms都会被执行一次。
对应的main函数里这么执行就行。
2.先由三个串口接收函数来确定小车此时的模式,由于使用接收标志位来判断是否有新数据所以标志位就不会被随意误刷新:
3.通过语音模块的接收数据来确定此时模式,并且在另一种模式没有来之前,是不会被改变的。
4.蓝牙模式:
5.跟随模式:先对接收到的5次声源数据进行采集,并进行排序滤波,以此确认真正的声源方向,然后根据声源偏离方向的不同,给左右两个轮子的前进力度都不同:
6.避障模式:可以通过代码看出在测出距离后根据距离的不同控制不一样的速度:
7.语音模式:融合一些时间和速度达到转到相应的角度,代码看一下就知道了,j每加1就相当于以当前的力度执行了10ms,于是以相同力度执行不同时间就可以造成转至不同角度了:
Ⅴ、完善机械结构
重新拆开优化结构使两轮子轴心一致,高度尽可能小降低重心,定制个亚克力外壳,将外露的导线理顺更加的美观。
Ⅵ、调整参数与PID
1.调PID:
四个参数:
(1)全部归零
(2)直立环慢慢增大Kp直至小车低频高幅震荡
(3)直立环慢慢增大Kd直至小车高频低幅震荡
(4)统一乘以0.6-0.8作为最终参数
(5)速度环Kp与Ki之间的大小关系是200倍,Kp可以从0.1慢慢加0.1看看效果,若加速倒下将两系数都加上负号即可,直至理想效果。
2.调速度等参数:
调整上面所说的,如蓝牙模式下的前进后退速度,语音模式下的转弯力度,避障模式下的变速程度等等。
扫码加入QQ群3群| 610403240
199