这次记录下最近调用的外设。DMA以前用得很少,只通过ADC采集使用,开启后就直接读那个数组就可以了。
我对dma的理解就是不占用cpu,数据传输速度快,可以直接从外设和内存间相互读取。
目前的工作用的芯片只能使用hal库开发,stm32g0系列性价比高。用cubemx把基础外设全配置跑了一遍,给我最大感受就是方便快捷,在st自家芯片中移植性强。
时钟和晶振配置不多说了,网上都很全了,可以不使用外部晶振,但HCLK最大只能到64M,最好还是外接晶振让系统更可靠稳定吧。
这里我创建了个新的ioc文件,之前写了10多个传感器的板子不想动了,这篇文章算是我头一个记录hal+cubemx的记录(虽然之前自己已经写过不少了),顺便把引脚更改写下,希望能帮助到新人。
Baud Rate(波特率):两边设备通讯需要相同,这里我设定的是115200
其它基础设置数据位,无校验位,停止位,没特殊要求我按的默认。
进阶选项看需求,我这里收发都需要,其它没有设置。
一些暂时用不到的设置,错误返回值默认使能的。
传输数据高位在前默认没有使能,一般spi通讯先读MSB。
当启用功能后,GPIO会自动帮你设置好,可以自行更改。
串口功能开启后,引脚就变绿了,如果没有在左边开启串口,引脚会是黄色的。
右键选择第一项,可写个对此引脚写个标签,建议写,以后方便移植,也好直观看出引脚功能。
在生成的工程中,main.h里也会出现宏定义
中断记得要勾上,系统会自动给你初始化好nvic的中断优先级。
DMA这里点击 Add 进行添加,把发送和接收添加到对应的DMA通道里。
单击一行,出现设置。
优先级为低。
1:模式为普通。调用一次dma发送函数只发一次,而Circular为循环。调用一次后就不需要管它了,直接读取放进形参的数组/指针
(如果开启多个串口dma,请务必注意发送间隔,不然有时会发送意义不明的数值。接收也改为普通模式)
1:仅赋值需要的库文件
2: 勾选这个选项后,初始化的外设会自动生成到别的文件夹,不勾选的话全部生成在main.c
进阶选项中可以设置初始化函数的顺序,之前在网上看到别人说过如果DMA初始化不在串口前面,串口中初始化里有对应DMA的设置,DMA就会失效。我在使用前会把相应的底层库看一遍,目前定时器,串口,AD,PWM,输入捕获,IIC(这个地址要注意下,要结合需要驱动的芯片手册)的使用都没有任何问题。
接下来直接看代码。
和标准库不同的是,hal库使用的是回调函数,之前我们在中断服务函数里写中断程序,现在全使用回调函数。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim==&htim17) //定时器17 1ms
{
if(Sys_Data.Updata_Time.MH440D++>100)
{
Sys_Data.Updata_TimeFlag.MH440D=1;
Sys_Data.Updata_Time.MH440D=0;
}
}
}
串口程序这里没有使用回调函数。
#include "MH440D.h"
/*和校验*/
uint8_t GetCheckSum(uint8_t *packet)
{
uint8_t i, checksum;
for( i = 1; i < 8; i++)
{
checksum += packet[i];
}
checksum = 0xff-checksum;
checksum += 1;
return checksum;
}
void MH440D_SendCom(_MH440D* MH440D)
{
uint8_t i;
uint8_t Sendbuff[9]={0xff,MH440D->CH4_N,MH440D->CH4_Com,0,0,0,0,0,0};
MH440D->CheckSum=GetCheckSum(Sendbuff);
Sendbuff[8]=MH440D->CheckSum;
memcpy(MH440D->SendBuff,Sendbuff,9); //复制数组
HAL_UART_Transmit_DMA(&huart1,MH440D->SendBuff ,9); //发送一次
}
FlagStatus MH440D_Read(_MH440D* MH440D)
{
HAL_UART_Receive_DMA(&huart1,MH440D->RecBuff ,9 );
if( MH440D->RecBuff[0]==0XFF && MH440D->RecBuff[1] ==0x86) //判断设备首地址和命令
{
MH440D->CheckSum =GetCheckSum(MH440D->RecBuff);
if(MH440D->RecBuff[8]==MH440D->CheckSum)
{
MH440D->CH4_H=MH440D->RecBuff[2]; //读出传感器数据
MH440D->CH4_L=MH440D->RecBuff[3];
return 1;
}
else
return 0;
}
else
return 0;
}
接收我使用的是循环读取,其实调用一次就可以了,但我在主函数循环里还是重复调用了。
模拟了下发送和接收。
我以前写接收时,每次接收只读一位,然后写的比较复杂,从开始位来判断标志位,循环读多少,来判断对方芯片发送的格式和校验,一不小心写错保存的数据就出差错了,十分麻烦。发现用DMA直接将数组/指针首地址放入后,确定好数据长度,可直接存取正确的顺序,剩下只需要处理数据就好了。spi和iic也差不多,基本上是对变量的长度和地址进行操作。以前也很少用硬件,全是用的io模拟,理解通信协议后 ,我在使用过的芯片,pic,stc系列(51/12/8/15我都当做51核了),stm32系列都能很好移植了。
学生时期用了4年多标准库,工作时候有机会使用到了cubemx生成代码+hal库,还是挺爽的,但我还是少不了看寄存器手册的地方= =
也是头一次我开始尝试写结构体套结构体,以前只直接struct,各种函数调用,套来套去,现在根据公司要求规则,换了个写法,正在慢慢改进。