PCB布线过程就不分享了,笔者是野路子没有系统的学习,都是连上能用就行。本篇书接上回记录一下测试版的后续。几天打板,买材料,焊接之后,组装上车
![](https://img-blog.csdnimg.cn/e5d692f817f04182bc7c636f49c36e06.jpeg)
虽然板子主要功能测试都符合预期,但还是有一些意料之外的问题。首先是供电部分,创新的单开关控制两路电源,又犯了野路子常见的错误,MOS管的导通条件VGS是G相对于S的电压,而不是相对于GND的电压。
![](https://img-blog.csdnimg.cn/c73eff9380514e0ab0ed8ecd06f6b3fb.png)
从原理图其实已经能看出问题了,理想中的导通状态MOS的S极是24V,而G是12V,VGS甚至小于0,当然不可能实现。这个电路实际的效果是,12V上电一瞬间,24V的负载端电压为0,所以MOS是会导通的,但随着24V电路上电,电压上升到10V时,VGS已经只有2V,MOS要截止了,但如果MOS截止后端的电压又会降下去,VGS又变大了,MOS就导通,于是MOS就卡在VGS=2V这么一个只导通了一点点的状态,而24V端的电压也只能上升到10V,且此时MOS的导通电阻应当是非常大的,完全不可用。正确的设计应当把MOS放在24V的负极端
![](https://img-blog.csdnimg.cn/9fc2e18cb9ed4ab8a2e02cd581916775.png)
由于GND永远是0V,所以只要12V到位了,MOS就一直导通,而12V撤去后VGS就是0,也就截止,24V无法形成对地回路也就无法工作。三极管和MOS管错误放在正极的经历已经有好几次,希望这次踩坑可以从此杜绝这种情况。
这块板在调试的过程中还遇到了一个小问题,STM32会不定期不能正常工作,可以进入调试模式,但调试模式下一步根本进不了main.c,看到这里屏幕前熟悉STM32的朋友一定想到了,肯定是进bootloader了。我第一时间也是这么想的,但很快否定了这种猜想,因为据我所知STM32G4系列没有bootloader,我用过不少G030,第一次搜索了解到G系列的bootloader需要特殊操作去启用才有作用,所以G030从没管过boot,也从没出现过异常进入boot的情况。我的代码是STM32cubemx自动生成,代码肯定不会有问题,那么排除了boot那就要考虑硬件启动失败,每一路供电的滤波电容都给到了,那么还有啥,复位键是否异常,测量了复位的电平,按下低电平,松开高电平,非常稳定,复位的滤波电容也起作用了。至此就毫无思路了,只好去求助于万能的人均月入20k的群友。果然人外有人,群友一下指出,G4是有boot引脚的,工作条件和F1,F4一样,只是在PB8。我一看cubemx的引脚配置,PB8果然后面有跟着BOOT的字样,大意了。还好PB8作为编码器接口引出了,飞了一个10k上拉,稳定启动,问题解决。
总的来说,板子基本符合预期,一些小瑕疵也可以通过飞线补救,能用就行。小调了一下底层代码,马上搞一个调试保护架就可以开始调平衡了。附上一部分LL库的底层代码。
首先是串口DMA接收完成中断+空闲中断处理陀螺仪数据,这俩中断组合起来是我最喜欢的处理定时定长数据包的操作,效率高且能避免丢字节的情况
//初始化配置
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 11); //陀螺仪一帧11字节
LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)&USART1->RDR); //外设地址为串口接收数据寄存器
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)uart1_rx_buf); //内存地址为长度为11字节的数组
LL_DMA_ClearFlag_TC1(DMA1); //使能中断前最好清空一下标志位防止误触发
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); //DMA接收完成中断
LL_USART_EnableDMAReq_RX(USART1); //使能串口DMA接收请求
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); //使能DMA通道
LL_USART_ClearFlag_IDLE(USART1); //清除标志位
LL_USART_EnableIT_IDLE(USART1); //使能串口空闲中断
LL_USART_Enable(USART1); //使能串口
当串口空闲时,表示陀螺仪一帧数据发送完毕,这时候判断DMA接收是否完成,如果接收未完成就说明一定丢字节了,这一帧数据就可以放弃掉了,直接重新设置DMA接收长度
//串口中断服务函数代码
if(LL_USART_IsActiveFlag_IDLE(USART1)) //判断是否为串口空闲中断
{
if(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_1)>0) //没接收完,误码
{
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 11); //让DMA重新接收
}
LL_USART_ClearFlag_IDLE(USART1); //不管接收成不成功都要清空串口标志位
LL_USART_ReceiveData8(USART1); //清空标志位还要读取一下串口防止异常中断
}
最后是DMA接收完成中断,如果成功触发了这个中断,表示数据长度是正确的,此时还需要稍加判断数据内容即可,由于串口的可靠性已经非常高,所以我就只是确定了下帧头,没有再和校验
//DMA中断服务函数代码
extern unsigned char uart1_rx_buf[];
if(LL_DMA_IsActiveFlag_TC1(DMA1))
LL_DMA_ClearFlag_TC1(DMA1);
//判断帧头是否符合
if(uart1_rx_buf[0]==0X5A && uart1_rx_buf[1]==0x5A && uart1_rx_buf[2]==0x10)
{
//结算陀螺仪数据
atti.roll = (float)(uart1_rx_buf[4]<<8|uart1_rx_buf[5])/100-326.7;
atti.pitch = (float)(uart1_rx_buf[6]<<8|uart1_rx_buf[7])/100-326.7;
atti.yaw = (float)(uart1_rx_buf[8]<<8|uart1_rx_buf[9])/100-326.7;
//修改一下数据格式便于使用
if(atti.pitch>0)
{
atti.pitch = 328.7-atti.pitch;
}
else
{
atti.pitch = -326.7-atti.pitch;
}
if(atti.roll>0)
{
atti.roll = 328.7-atti.roll;
}
else
{
atti.roll = -326.7-atti.roll;
}
if(atti.yaw>0)
{
atti.yaw = 328.7-atti.yaw;
}
else
{
atti.yaw = -326.7-atti.yaw;
}
还有个小插曲记录一下,为了防止电机插口设计反,我预留了正反两种接口,对着插口看哪种对的就焊那个,正因为如此,反而给了我反插的机会,经测试,独轮车配的无刷电机还是比较耐艹的,经历了5分钟的24V反接也只是微微发烫,后来测试一点问题没有。