【嵌入式模块芯片开发】LP87524电源PMIC芯片配置流程,给雷达供电的延时上电时序及API函数
文章目录
- LP87524
- LP87524初始化
- 雷达供电时需要的上电时序
- LP87524电源PMIC芯片的BUCK供电时序
- LP87524电源PMIC芯片的BUCK默认供电输出
- LP87524相关API函数
- 附录:压缩字符串、大小端格式转换
LP87524
LP8752x-Q1 10-A Buck Converter With Integrated Switche
具有诊断功能的 LP87702-Q1 双路降压转换器和 5V 升压转换器
通过I2C来通信
从机地址为0x60

支持随机地址读取和顺序写入


支持硬件寄存器延时启动的BUCK输出

另外 有两种寄存器配置输出
一个是BUCK VOUT 一个是BUCK FLOOR VOUT
前者是BUCK输出
后者是保障最低输出
比如BUCK输出3.3V 保障输出3.0V 那么电压就会在可负载的3.0-3.3之间
一般FLOOR不用刻意去配置 负载不高的话 都能保得住
LP87524初始化

- 将0x40写入LP87524_CONFIG,CLKIN下拉,其他不选择下拉,此寄存器也可配置信号延时
- 将0x00写入PIN_FUNCTION寄存器,使其不使用GPIO功能
- 依次配置以下寄存器,使B0输出3.3V,B1输出1.8V,B2输出1.2V,B3输出1.0V
#define LP87524_BUCK0_CTRL1 0x02 写入0x34,bit7控制开启
#define LP87524_BUCK1_CTRL1 0x04 写入0x44,bit7和EN1控制开启
#define LP87524_BUCK2_CTRL1 0x06 写入0x54,bit7和EN2控制开启
#define LP87524_BUCK3_CTRL1 0x08 写入0x64,bit7和EN3控制开启
#define LP87524_BUCK0_VOUT 0x0A 写入0xFC
#define LP87524_BUCK0_FLOOR_VOUT 0x0B 写入0xFC
#define LP87524_BUCK1_VOUT 0x0C 写入0xB1
#define LP87524_BUCK1_FLOOR_VOUT 0x0D 写入0xB1
#define LP87524_BUCK2_VOUT 0x0E 写入0x75
#define LP87524_BUCK2_FLOOR_VOUT 0x0F 写入0x75
#define LP87524_BUCK3_VOUT 0x10 写入0x4D
#define LP87524_BUCK3_FLOOR_VOUT 0x11 写入0x4D
若需要延迟开启或关闭,则配置DEALY寄存器
若需启用电压监测,则配置PGOOD寄存器
最后一步再开启电压输出:
BUCK0_CTRL1 先读取再写入,使bit7置1
BUCK1_CTRL1 先读取再写入,使bit7置1,EN1输出高电平
BUCK2_CTRL1 先读取再写入,使bit7置1,EN2输出高电平
BUCK3_CTRL1 先读取再写入,使bit7置1,EN3输出高电平
- 若有需要,可使能EN的GPIO复用功能,编程引脚可以通过配置寄存器来输出高低电平。中断及状态在INT和STAT寄存器内,中断屏蔽配置在MASK寄存器内。SEL_I_LOAD寄存器可开启电流测量。
雷达供电时需要的上电时序
由IWR6843硬件手册可以看到几路供电的上电时序

以及AWR1843的手册:

除了上电时序为1.2 1.8 1.0 3.3之外
还要求电压稳定
如果电压不稳定 则可能跑不进用户代码
现象就是明明没有配置串口回环却实现串口回环
看这个时序 之前的串口回环可能就是SOP的问题 因为上电不稳定 导致进入了错误的SOP模式
按这个上电时序上电以后 3.3V充满电的时候 就是DC power OK 然后就是SOP setup time
然后这个DC power要稳定一段时间 在nRESET之前 NRESET充满电以后 才是SOP Hold time to nRESET
在nRESET这个上升沿时间中 要保持电压稳定 SOP稳定才行
不稳定或不按时序上电 就会导致SOP不稳定 从而进入到了不同的BOOT模式
而且烧录代码就是串口回环
延迟上电不能过慢 也不能过快
100us左右的间隔我测试是可以正常工作的
波形图:

LP87524电源PMIC芯片的BUCK供电时序
LP87524配置见:
【TI毫米波雷达】LP87702/LP87524电源PMIC芯片的BUCK供电配置
我们现在的原理图是这样 BUCK0-BUCK3分别输出3.3 1.2 1.0 1.8的电压

BUCK1 2 3由寄存器开关和EN1 2 3引脚共同决定 BUCK0由寄存器开关控制
按这个原理图的话 时序控制为:
c
//按1.2 1.8 1.0 3.3的顺序上电
//预准备
delay_ms(10);
dat=I2C_Read_y(&LP87524_I2C_Handle,LP87524_Slave_Add,LP87524_BUCK2_CTRL1,1,1,true);
dat|=(1<<7);
I2C_Write_x(&LP87524_I2C_Handle,LP87524_Slave_Add,LP87524_BUCK2_CTRL1,1,&dat,1,true);
delay_ms(10);
Enable_LP87524_BUCK(1,true);
delay_us(4400);
Enable_LP87524_BUCK(3,true);
delay_us(150);
//Enable_LP87524_BUCK(2,true);
Enable_LP87524_EN2;
delay_us(4450);
Enable_LP87524_BUCK(0,true);
波形:

延迟上电间隔约100us
这个是可以使用的
如果加上雷达板负载 可能3.3V这一路会延后 所以还是带上雷达板测试比较好
如果不进行预准备和延时间隔 则波形如图:

这是由于每个buck的硬件延时都不一样导致的 所以必须一个一个来调整
另外 我的延时函数也有误差 不一定完全是这个延时 要根据自己的板子来调整
上电稳定后的波形:

LP87524电源PMIC芯片的BUCK默认供电输出
这一项是在芯片手册中没写的
现象就是:
关闭VCC_5V以后 如果没有负载 几路buck会缓慢掉电 掉电到基本没电的时候 重新打开VCC_5V 立马就会进行默认输出
也就是说 开启5V开关以后 87524上电 但不进行配置 也不进行复位时 BUCK0-3的输出分别是:0.88 3.36 1.55 1.87
这个就是87524的默认BUCK输出值 如果运用在产品中 假设要进行功耗控制 只让雷达工作一段时间 就需要频繁开关VCC_5V电源 所以我建议是把BUCK0-3按照1.0 3.3 1.2 1.8的顺序设计输出 这样的话 与默认BUCK输出的电压相近
(注:这里的VCC_5V指的是除控制用的MCU外的其他器件总电源)
波形:

但其实是关闭电源的操作不对:
现在我加了一个 就是在关闭VCC_5V后 把EN1 EN2 EN3都关闭 然后重新打开VCC_5V 现在就不会有默认输出了
严格按照这个顺序来操作 就不会出现buck默认输出的问题 也不会有电压跳变
也就是说 在关闭LP87524的BUCK输出前 要将EN1 EN2 EN3关闭 最好是先进行关闭函数再关闭总电源
LP87524相关API函数
c
#include "LP87524_CMD.h"
#include "main.h"
#define USE_LP87524_EN 1
#if USE_LP87524_EN
#define Enable_LP87524_EN1 HAL_GPIO_WritePin(LP87524_EN1_GPIO_Port, LP87524_EN1_Pin, GPIO_PIN_SET);
#define Disable_LP87524_EN1 HAL_GPIO_WritePin(LP87524_EN1_GPIO_Port, LP87524_EN1_Pin, GPIO_PIN_RESET);
#define Enable_LP87524_EN2 HAL_GPIO_WritePin(LP87524_EN2_GPIO_Port, LP87524_EN2_Pin, GPIO_PIN_SET);
#define Disable_LP87524_EN2 HAL_GPIO_WritePin(LP87524_EN2_GPIO_Port, LP87524_EN2_Pin, GPIO_PIN_RESET);
#define Enable_LP87524_EN3 HAL_GPIO_WritePin(LP87524_EN3_GPIO_Port, LP87524_EN3_Pin, GPIO_PIN_SET);
#define Disable_LP87524_EN3 HAL_GPIO_WritePin(LP87524_EN3_GPIO_Port, LP87524_EN3_Pin, GPIO_PIN_RESET);
#else
#define Enable_LP87524_EN1
#define Disable_LP87524_EN1
#define Enable_LP87524_EN2
#define Disable_LP87524_EN2
#define Enable_LP87524_EN3
#define Disable_LP87524_EN3
#endif
#ifdef HAL_I2C_MODULE_ENABLED
/*!
* @brief 判断I2C设备是否可以响应
*
* @param [in] hi2c: I2C_HandleTypeDef 变量地址
* [in] DevAddress: 从机地址,7位从机地址,向右对齐
*
* @return 返回bool类型,为true表示可以响应
*/
bool I2C_Judge(I2C_HandleTypeDef *hi2c,uint16_t DevAddress)
{
DevAddress=(DevAddress<<1)&0xFF;
if(HAL_I2C_IsDeviceReady(hi2c,DevAddress,5,0x00ff)==HAL_OK)
{
return true;
}
else
{
return false;
}
}
/*!
* @brief 对I2C设备进行写入
*
* @param [in] hi2c: I2C_HandleTypeDef 变量地址
* [in] DevAddress: 从机地址,7位从机地址,向右对齐
* [in] add: 从机寄存器地址,8位地址
* [in] add_length: 为1表示1Byte(8位),为2表示2Byte(16位)
* [in] pData: 数据变量地址
* [in] x: 写入数据个数
* [in] prologue_flag: 序言标志
* 当prologue_flag为true时,先发送从机地址,再写入寄存器地址,再发一次从机地址后,再写入数据(随机地址写入)
* 当prologue_flag为false时,直接发送从机地址后就写入数据(当前地址写入),此时不会发送从机寄存器地址
*
* @return true/false 发送是否成功
*/
bool I2C_Write_x(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint16_t add,uint16_t add_length,uint8_t *pData,uint8_t x,bool prologue_flag)
{
DevAddress=(DevAddress<<1)&0xFF;
uint16_t MemAddSize=1;
if(pData==NULL || x==0)
{
return false;
}
if(prologue_flag)
{
switch(add_length)
{
case 1:MemAddSize=I2C_MEMADD_SIZE_8BIT;break;
case 2:MemAddSize=I2C_MEMADD_SIZE_16BIT;break;
default:MemAddSize=I2C_MEMADD_SIZE_8BIT;break;
}
if(HAL_I2C_Mem_Write(hi2c,DevAddress,add,MemAddSize,pData,x,0xFFFF)==HAL_OK)
{
return true;
}
else
{
return false;
}
}
else
{
if(HAL_I2C_Master_Transmit(hi2c,DevAddress,pData,x,0xFFFF)==HAL_OK)
{
return true;
}
else
{
return false;
}
}
}
/*!
* @brief 对I2C设备进行读取
*
* @param [in] hi2c: I2C_HandleTypeDef 变量地址
* [in] DevAddress: 从机地址,7位从机地址,向右对齐
* [in] add: 从机寄存器地址,8/16位地址
* [in] add_length: 为1表示1Byte(8位),为2表示2Byte(16位)
* [in] y: 读取数据个数,最大为4,若大于4,则返回0
* [in] prologue_flag: 序言标志
* 当prologue_flag为true时(随机地址读取)
* 当prologue_flag为false时(当前地址读取),此时从机寄存器地址无效
*
* @return dat: I2C读取数据返回
*/
uint32_t I2C_Read_y(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint16_t add,uint16_t add_length,uint8_t y,bool prologue_flag)
{
DevAddress=(DevAddress<<1)&0xFF;
uint8_t pData[y];
memset(pData,0,sizeof(pData));
uint32_t dat=0;
uint16_t MemAddSize=0;
if(y>4 || y==0)
{
return 0;
}
if(prologue_flag)
{
switch(add_length)
{
case 1:MemAddSize=I2C_MEMADD_SIZE_8BIT;break;
case 2:MemAddSize=I2C_MEMADD_SIZE_16BIT;break;
default:MemAddSize=I2C_MEMADD_SIZE_8BIT;break;
}
HAL_I2C_Mem_Read(hi2c,DevAddress,add,MemAddSize,pData,y,0xFFFF);
}
else
{
HAL_I2C_Master_Receive(hi2c,DevAddress,pData,y,0xFFFF);
}
for(uint8_t i=0;i<y;i++)
{
dat|=pData[i]<<(8*(y-1-i));
}
return dat;
}
#endif
/*!
* @brief 复位LP87524
*
* @param None
*
* @return false/true: 错误/正确标志
*/
bool Reset_LP87524(void)
{
uint8_t dat=0;
dat=0x01;
if(I2C_Write_x(&LP87524_I2C_Handle,LP87524_Slave_Add,LP87524_RESET,1,&dat,1,true)!=true)
{
return false;
}
delay_ms(10);
return true;
}
/*!
* @brief 控制LP87524的BUCK输出值,这里只控制VOUT,不控制FLOOR_VOUT
*
* @param [in] buck_flag: BUCK序号,为4时控制所有
* [in] voltage: 要输出的电压值
* [in] WriteNotRead: 写入或读取
*
* @return voltage_v: 写入或读取的电压值,控制所有时,写入的都为3.3V,读取的为四个电压值之和
*/
float Ctrl_LP87524_BUCK_VOUT(uint8_t buck_flag,float voltage_v,bool WriteNotRead)
{
uint8_t dat=0;
uint8_t add=0;
if(buck_flag>=4)
{
voltage_v=Ctrl_LP87524_BUCK_VOUT(0,3.3,WriteNotRead)+Ctrl_LP87524_BUCK_VOUT(1,3.3,WriteNotRead)+Ctrl_LP87524_BUCK_VOUT(2,3.3,WriteNotRead)+Ctrl_LP87524_BUCK_VOUT(3,3.3,WriteNotRead);
}
else
{
add = LP87524_BUCK0_VOUT+2*buck_flag;
if(WriteNotRead)
{
if(voltage_v>=0.6f && voltage_v<=0.73f)
{
dat=0x0A+(voltage_v-0.6f)/0.01f;
}
else if(voltage_v>0.73f && voltage_v<=1.4f)
{
dat=0x17+(voltage_v-0.73f)/0.005f;
}
else if(voltage_v>1.4f && voltage_v<=3.36f)
{
dat=0x9D+(voltage_v-1.4f)/0.02f;
}
else
{
dat=0;
}
I2C_Write_x(&LP87524_I2C_Handle,LP87524_Slave_Add,add,1,&dat,1,true);
}
else
{
dat=I2C_Read_y(&LP87524_I2C_Handle,LP87524_Slave_Add,add,1,1,true);
if(dat>=0x0A && dat<=0x17)
{
voltage_v=(dat-0x0A)*0.01f+0.6f;
}
else if(dat>0x17 && dat<=0x9D)
{
voltage_v=(dat-0x17)*0.005f+0.73f;
}
else if(dat>=0x9D && dat<=0xFF)
{
voltage_v=(dat-0x9D)*0.02f+1.4f;
}
else
{
voltage_v=0;
}
}
}
return voltage_v;
}
/*!
* @brief 控制LP87524的BUCK FLOOR输出值,这里只控制VOUT,不控制FLOOR_VOUT
*
* @param [in] buck_flag: BUCK序号,为4时控制所有
* [in] voltage: 要输出的电压值
* [in] WriteNotRead: 写入或读取
*
* @return voltage_v: 写入或读取的电压值,控制所有时,写入的都为3.3V,读取的为四个电压值之和
*/
float Ctrl_LP87524_BUCK_FLOOR_VOUT(uint8_t buck_flag,float voltage_v,bool WriteNotRead)
{
uint8_t dat=0;
uint8_t add=0;
if(buck_flag>=4)
{
voltage_v=Ctrl_LP87524_BUCK_VOUT(0,3.3,WriteNotRead)+Ctrl_LP87524_BUCK_VOUT(1,3.3,WriteNotRead)+Ctrl_LP87524_BUCK_VOUT(2,3.3,WriteNotRead)+Ctrl_LP87524_BUCK_VOUT(3,3.3,WriteNotRead);
}
else
{
add = LP87524_BUCK0_FLOOR_VOUT+2*buck_flag;
if(WriteNotRead)
{
if(voltage_v>=0.6f && voltage_v<=0.73f)
{
dat=0x0A+(voltage_v-0.6f)/0.01f;
}
else if(voltage_v>0.73f && voltage_v<=1.4f)
{
dat=0x17+(voltage_v-0.73f)/0.005f;
}
else if(voltage_v>1.4f && voltage_v<=3.36f)
{
dat=0x9D+(voltage_v-1.4f)/0.02f;
}
else
{
dat=0;
}
I2C_Write_x(&LP87524_I2C_Handle,LP87524_Slave_Add,add,1,&dat,1,true);
}
else
{
dat=I2C_Read_y(&LP87524_I2C_Handle,LP87524_Slave_Add,add,1,1,true);
if(dat>=0x0A && dat<=0x17)
{
voltage_v=(dat-0x0A)*0.01f+0.6f;
}
else if(dat>0x17 && dat<=0x9D)
{
voltage_v=(dat-0x17)*0.005f+0.73f;
}
else if(dat>=0x9D && dat<=0xFF)
{
voltage_v=(dat-0x9D)*0.02f+1.4f;
}
else
{
voltage_v=0;
}
}
}
return voltage_v;
}
/*!
* @brief 开启LP87524的BUCK输出,这里是使能BUCK的输出,同时对应引脚置高电平
* 初始化配置的是打开VOUT输出,如果要控制打开/关闭 VOUT/FLOOR_VOUT则需要另外配置
*
* @param [in] buck_flag: BUCK序号,为4时控制所有
* [in] EnableNotDisable: 使能或者关闭
*
* @return None
*/
void Enable_LP87524_BUCK(uint8_t buck_flag,bool EnableNotDisable)
{
uint8_t dat=0;
uint8_t add=0;
if(buck_flag>=4)
{
Enable_LP87524_BUCK(0,EnableNotDisable);
Enable_LP87524_BUCK(1,EnableNotDisable);
Enable_LP87524_BUCK(2,EnableNotDisable);
Enable_LP87524_BUCK(3,EnableNotDisable);
}
else
{
add = LP87524_BUCK0_CTRL1+2*buck_flag;
dat=I2C_Read_y(&LP87524_I2C_Handle,LP87524_Slave_Add,add,1,1,true);
if(EnableNotDisable)
{
dat|=(1<<7);
}
else
{
dat&=~(1<<7);
}
I2C_Write_x(&LP87524_I2C_Handle,LP87524_Slave_Add,add,1,&dat,1,true);
switch(buck_flag)
{
case 1:
{
if(EnableNotDisable)
{
Enable_LP87524_EN1;
}
else
{
Disable_LP87524_EN1;
}
break;
}
case 2:
{
if(EnableNotDisable)
{
Enable_LP87524_EN2;
}
else
{
Disable_LP87524_EN2;
}
break;
}
case 3:
{
if(EnableNotDisable)
{
Enable_LP87524_EN3;
}
else
{
Disable_LP87524_EN3;
}
break;
}
default:break;
}
}
}
/*!
* @brief 初始化LP87524
*
* @param None
*
* @return 0/1: 错误/正确标志
*/
uint8_t Init_LP87524(void)
{
//原本这里是LP87524_I2C_Handle 由于原理图出错 现全部换成hi2c2
uint8_t dat=0;
Enable_VCC_5V;
delay_ms(5);
Disable_LP87524_EN1;
Disable_LP87524_EN2;
Disable_LP87524_EN3;
delay_ms(5);
if(!I2C_Judge(&LP87524_I2C_Handle,LP87524_Slave_Add))
{
return 0;
}
if(!Reset_LP87524())
{
return 0;
}
delay_ms(100);
dat=0x40;
I2C_Write_x(&LP87524_I2C_Handle,LP87524_Slave_Add,LP87524_CONFIG,1,&dat,1,true);
dat=0x00;
I2C_Write_x(&LP87524_I2C_Handle,LP87524_Slave_Add,LP87524_PIN_FUNCTION,1,&dat,1,true);
dat=0x34;
I2C_Write_x(&LP87524_I2C_Handle,LP87524_Slave_Add,LP87524_BUCK0_CTRL1,1,&dat,1,true);
dat=0x44;
I2C_Write_x(&LP87524_I2C_Handle,LP87524_Slave_Add,LP87524_BUCK1_CTRL1,1,&dat,1,true);
dat=0x54;
I2C_Write_x(&LP87524_I2C_Handle,LP87524_Slave_Add,LP87524_BUCK2_CTRL1,1,&dat,1,true);
dat=0x64;
I2C_Write_x(&LP87524_I2C_Handle,LP87524_Slave_Add,LP87524_BUCK3_CTRL1,1,&dat,1,true);
Ctrl_LP87524_BUCK_VOUT(0,3.3,true);
Ctrl_LP87524_BUCK_VOUT(1,1.2,true);
Ctrl_LP87524_BUCK_VOUT(2,1.0,true);
Ctrl_LP87524_BUCK_VOUT(3,1.8,true);
if(Ctrl_LP87524_BUCK_VOUT(0,0,false)!=3.3f)
{
printf("[INFO] LP87524: BUCK0 Error");
}
if(Ctrl_LP87524_BUCK_VOUT(1,0,false)!=1.2f)
{
printf("[INFO] LP87524: BUCK1 Error");
}
if(Ctrl_LP87524_BUCK_VOUT(2,0,false)!=1.0f)
{
printf("[INFO] LP87524: BUCK2 Error");
}
if(Ctrl_LP87524_BUCK_VOUT(3,0,false)!=1.8f)
{
printf("[INFO] LP87524: BUCK3 Error");
}
//按1.2 1.8 1.0 3.3的顺序上电
//预准备
delay_ms(10);
dat=I2C_Read_y(&LP87524_I2C_Handle,LP87524_Slave_Add,LP87524_BUCK2_CTRL1,1,1,true);
dat|=(1<<7);
I2C_Write_x(&LP87524_I2C_Handle,LP87524_Slave_Add,LP87524_BUCK2_CTRL1,1,&dat,1,true);
delay_ms(10);
Enable_LP87524_BUCK(1,true);
delay_us(4400);
Enable_LP87524_BUCK(3,true);
delay_us(150);
//Enable_LP87524_BUCK(2,true);
Enable_LP87524_EN2;
delay_us(4450);
Enable_LP87524_BUCK(0,true);
return 1;
}
c
#ifndef __LP87524_CMD_H__
#define __LP87524_CMD_H__
#include <stdint.h>
#include <stdbool.h>
#define LP87524_OTP_REV 0x01
#define LP87524_BUCK0_CTRL1 0x02
#define LP87524_BUCK1_CTRL1 0x04
#define LP87524_BUCK2_CTRL1 0x06
#define LP87524_BUCK3_CTRL1 0x08
#define LP87524_BUCK0_VOUT 0x0A
#define LP87524_BUCK0_FLOOR_VOUT 0x0B
#define LP87524_BUCK1_VOUT 0x0C
#define LP87524_BUCK1_FLOOR_VOUT 0x0D
#define LP87524_BUCK2_VOUT 0x0E
#define LP87524_BUCK2_FLOOR_VOUT 0x0F
#define LP87524_BUCK3_VOUT 0x10
#define LP87524_BUCK3_FLOOR_VOUT 0x11
#define LP87524_BUCK0_DELAY 0x12
#define LP87524_BUCK1_DELAY 0x13
#define LP87524_BUCK2_DELAY 0x14
#define LP87524_BUCK3_DELAY 0x15
#define LP87524_GPIO2_DELAY 0x16
#define LP87524_GPIO3_DELAY 0x17
#define LP87524_RESET 0x18
#define LP87524_CONFIG 0x19
#define LP87524_INT_TOP1 0x1A
#define LP87524_INT_TOP2 0x1B
#define LP87524_INT_BUCK_0_1 0x1C
#define LP87524_INT_BUCK_2_3 0x1D
#define LP87524_TOP_STAT 0x1E
#define LP87524_BUCK_0_1_STAT 0x1F
#define LP87524_BUCK_2_3_STAT 0x20
#define LP87524_TOP_MASK1 0x21
#define LP87524_TOP_MASK2 0x22
#define LP87524_BUCK_0_1_MASK 0x23
#define LP87524_BUCK_2_3_MASK 0x24
#define LP87524_SEL_I_LOAD 0x25
#define LP87524_I_LOAD_2 0x26
#define LP87524_I_LOAD_1 0x27
#define LP87524_PGOOD_CTRL1 0x28
#define LP87524_PGOOD_CTRL2 0x29
#define LP87524_PGOOD_FLT 0x2A
#define LP87524_PLL_CTRL 0x2B
#define LP87524_PIN_FUNCTION 0x2C
#define LP87524_GPIO_CONFIG 0x2D
#define LP87524_GPIO_IN 0x2E
#define LP87524_GPIO_OUT 0x2F
float Ctrl_LP87524_BUCK_VOUT(uint8_t buck_flag,float voltage_v,bool WriteNotRead);
float Ctrl_LP87524_BUCK_FLOOR_VOUT(uint8_t buck_flag,float voltage_v,bool WriteNotRead);
void Enable_LP87524_BUCK(uint8_t buck_flag,bool EnableNotDisable);
uint8_t Init_LP87524(void);
#endif
附录:压缩字符串、大小端格式转换
压缩字符串
首先HART数据格式如下:


重点就是浮点数和字符串类型
Latin-1就不说了 基本用不到
浮点数
浮点数里面 如 0x40 80 00 00表示4.0f
在HART协议里面 浮点数是按大端格式发送的 就是高位先发送 低位后发送
发送出来的数组为:40,80,00,00
但在C语言对浮点数的存储中 是按小端格式来存储的 也就是40在高位 00在低位
浮点数:4.0f
地址0x1000对应00
地址0x1001对应00
地址0x1002对应80
地址0x1003对应40
若直接使用memcpy函数 则需要进行大小端转换 否则会存储为:
地址0x1000对应40
地址0x1001对应80
地址0x1002对应00
地址0x1003对应00
大小端转换:
c
void swap32(void * p)
{
uint32_t *ptr=p;
uint32_t x = *ptr;
x = (x << 16) | (x >> 16);
x = ((x & 0x00FF00FF) << 8) | ((x >> 8) & 0x00FF00FF);
*ptr=x;
}
压缩Packed-ASCII字符串
本质上是将原本的ASCII的最高2位去掉 然后拼接起来 比如空格(0x20)
四个空格拼接后就成了
1000 0010 0000 1000 0010 0000
十六进制:82 08 20
对了一下表 0x20之前的识别不了
也就是只能识别0x20-0x5F的ASCII表

压缩/解压函数后面再写:
c
//传入的字符串和数字必须提前声明 且字符串大小至少为str_len 数组大小至少为str_len%4*3 str_len必须为4的倍数
uint8_t Trans_ASCII_to_Pack(uint8_t * str,uint8_t * buf,const uint8_t str_len)
{
if(str_len%4)
{
return 0;
}
uint8_t i=0;
memset(buf,0,str_len/4*3);
for(i=0;i<str_len;i++)
{
if(str[i]==0x00)
{
str[i]=0x20;
}
}
for(i=0;i<str_len/4;i++)
{
buf[3*i]=(str[4*i]<<2)|((str[4*i+1]>>4)&0x03);
buf[3*i+1]=(str[4*i+1]<<4)|((str[4*i+2]>>2)&0x0F);
buf[3*i+2]=(str[4*i+2]<<6)|(str[4*i+3]&0x3F);
}
return 1;
}
//传入的字符串和数字必须提前声明 且字符串大小至少为str_len 数组大小至少为str_len%4*3 str_len必须为4的倍数
uint8_t Trans_Pack_to_ASCII(uint8_t * str,uint8_t * buf,const uint8_t str_len)
{
if(str_len%4)
{
return 0;
}
uint8_t i=0;
memset(str,0,str_len);
for(i=0;i<str_len/4;i++)
{
str[4*i]=(buf[3*i]>>2)&0x3F;
str[4*i+1]=((buf[3*i]<<4)&0x30)|(buf[3*i+1]>>4);
str[4*i+2]=((buf[3*i+1]<<2)&0x3C)|(buf[3*i+2]>>6);
str[4*i+3]=buf[3*i+2]&0x3F;
}
return 1;
}
大小端转换
在串口等数据解析中 难免遇到大小端格式问题
什么是大端和小端
所谓的大端模式,就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
所谓的小端模式,就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
简单来说:大端------高尾端,小端------低尾端
举个例子,比如数字 0x12 34 56 78在内存中的表示形式为:
1)大端模式:
低地址 -----------------> 高地址
0x12 | 0x34 | 0x56 | 0x78
2)小端模式:
低地址 ------------------> 高地址
0x78 | 0x56 | 0x34 | 0x12
可见,大端模式和字符串的存储模式类似。
数据传输中的大小端
比如地址位、起止位一般都是大端格式
如:
起始位:0x520A
则发送的buf应为{0x52,0x0A}
而数据位一般是小端格式(单字节无大小端之分)
如:
一个16位的数据发送出来为{0x52,0x0A}
则对应的uint16_t类型数为: 0x0A52
而对于浮点数4.0f 转为32位应是:
40 80 00 00
以大端存储来说 发送出来的buf就是依次发送 40 80 00 00
以小端存储来说 则发送 00 00 80 40
由于memcpy等函数 是按字节地址进行复制 其复制的格式为小端格式 所以当数据为小端存储时 不用进行大小端转换
如:
c
uint32_t dat=0;
uint8_t buf[]={0x00,0x00,0x80,0x40};
memcpy(&dat,buf,4);
float f=0.0f;
f=*((float*)&dat); //地址强转
printf("%f",f);
或更优解:
c
uint8_t buf[]={0x00,0x00,0x80,0x40};
float f=0.0f;
memcpy(&f,buf,4);
而对于大端存储的数据(如HART协议数据 全为大端格式) 其复制的格式仍然为小端格式 所以当数据为小端存储时 要进行大小端转换
如:
c
uint32_t dat=0;
uint8_t buf[]={0x40,0x80,0x00,0x00};
memcpy(&dat,buf,4);
float f=0.0f;
swap32(&dat); //大小端转换
f=*((float*)&dat); //地址强转
printf("%f",f);
或:
c
uint8_t buf[]={0x40,0x80,0x00,0x00};
memcpy(&dat,buf,4);
float f=0.0f;
swap32(&f); //大小端转换
printf("%f",f);
或更优解:
c
uint32_t dat=0;
uint8_t buf[]={0x40,0x80,0x00,0x00};
float f=0.0f;
dat=(buf[0]<<24)|(buf[0]<<16)|(buf[0]<<8)|(buf[0]<<0)
f=*((float*)&dat);
总结
固 若数据为小端格式 则可以直接用memcpy函数进行转换 否则通过移位的方式再进行地址强转
对于多位数据 比如同时传两个浮点数 则可以定义结构体之后进行memcpy复制(数据为小端格式)
对于小端数据 直接用memcpy写入即可 若是浮点数 也不用再进行强转
对于大端数据 如果不嫌麻烦 或想使代码更加简洁(但执行效率会降低) 也可以先用memcpy写入结构体之后再调用大小端转换函数 但这里需要注意的是 结构体必须全为无符号整型 浮点型只能在大小端转换写入之后再次强转 若结构体内采用浮点型 则需要强转两次
所以对于大端数据 推荐通过移位的方式来进行赋值 然后再进行个别数的强转 再往通用结构体进行写入
多个不同变量大小的结构体 要主要字节对齐的问题
可以用#pragma pack(1) 使其对齐为1
但会影响效率
大小端转换函数
直接通过对地址的操作来实现 传入的变量为32位的变量
中间变量ptr是传入变量的地址
c
void swap16(void * p)
{
uint16_t *ptr=p;
uint16_t x = *ptr;
x = (x << 8) | (x >> 8);
*ptr=x;
}
void swap32(void * p)
{
uint32_t *ptr=p;
uint32_t x = *ptr;
x = (x << 16) | (x >> 16);
x = ((x & 0x00FF00FF) << 8) | ((x >> 8) & 0x00FF00FF);
*ptr=x;
}
void swap64(void * p)
{
uint64_t *ptr=p;
uint64_t x = *ptr;
x = (x << 32) | (x >> 32);
x = ((x & 0x0000FFFF0000FFFF) << 16) | ((x >> 16) & 0x0000FFFF0000FFFF);
x = ((x & 0x00FF00FF00FF00FF) << 8) | ((x >> 8) & 0x00FF00FF00FF00FF);
*ptr=x;
}