STM32使用ADM1191实现多电源监控器

本文档详细记录了使用STM32F405RGTX通过I2C与多个ADM1191电流电压监控器进行数据交互的过程,包括初始化配置、读写控制时序和定时器中断处理函数。通过设置ADM1191的ADC为连续或单次转换,实现了对电源电压电流的实时监控。在遇到问题时,提供了错误处理机制确保I2C总线的正常运行。
摘要由CSDN通过智能技术生成

STM32读取多个ADM1191电流电压,实现电源监控器
最近在通过STM32从多个ADM1191芯片中回读电压电流,踩坑不少,故做此简单记录。
一:芯片简介
1.主控芯片型号:STM32F405RGTX 高性能MCU,主频168MHZ,Cortex4-M4内核
2.ADM1191是一款集成电流检测放大器,可通过I2C接口传送的片内,12位模数转换器(ADC)提供数字电流和电压监控。内部电流检测放大器通过VCC引脚和感测引脚在电源路径中的感测电阻上的电压感测电压。
12位ADC可以测量检测电阻中看到的电流,以及VCC引脚上的电源电压。
行业标准I2C接口允许控制器从ADC读取电流和电压数据。测量可以由I2C命令或通过转换(CONV)引脚启动。 CONC引脚特别适用于同步多个ADM1191设备上的读取。或者,ADC可以连续运行,并且用户可以在需要时读取最新的转换数据。最多可以创建16个唯一的I2C地址,具体取决于A0引脚和A1引脚的方式连接。
器件I2C地址如下表:
在这里插入图片描述
二、电压电源回读
本实验用STM32与三个ADM1191采用I2C总线进行数据交换。
I2C协议常用信号这里不做记录。下面是读写ADM1191的读写控制时序:
在这里插入图片描述
发命令格式:
在这里插入图片描述
各位命令的含义:
在这里插入图片描述
这里是低七位,最高位控制其是发命令(MSB:0)还是向寄存器写入(MSB:1)
下面直接上程序:
1.采样控制引脚初始化:

void CONV_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_AHB1PeriphClockCmd(CONV_GPIO_CLK,ENABLE);
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_Pin =CONV_GPIO_PIN;
	GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
	GPIO_Init(CONV_GPIO_PORT,&GPIO_InitStructure);
	GPIO_ResetBits(CONV_GPIO_PORT,CONV_GPIO_PIN);	
}```c
void CONV_EN()
{
	GPIO_SetBits(CONV_GPIO_PORT,CONV_GPIO_PIN);
}
void CONV_DISABLE()
{
	GPIO_ResetBits(CONV_GPIO_PORT,CONV_GPIO_PIN);
}


2.过流过压引脚初始化:当发送过流过压现象是该引脚被置低

void ALERT_Pin_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(ALERT_ARM_GPIO_CLK|ALERT_OPTICAL_GPIO_CLK|ALERT_SAMPLE_GPIO_CLK,ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_Pin =ALERT_ARM_GPIO_PIN |ALERT_OPTICAL_GPIO_PIN | ALERT_SAMPLE_GPIO_PIN;
GPIO_Init(GPIOA,&GPIO_InitStructure);
}

3.配置ADM1191及回读电压电流:

/*设置ADM1191  ADC为连续转换*/
uint8_t adm1191_convert_con(uint8_t addr)
{
	i2c_Start();
	i2c_SendByte(addr|ADM1191_I2C_WR);
	if(i2c_WaitAck() != 0)
	{
		goto cmd_fail;
	}
	i2c_SendByte(0x05);
	if(i2c_WaitAck() != 0)
	{
		goto cmd_fail;
	}
	i2c_Stop();
	return 1;
	cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
	/* 发送I2C总线停止信号 */
	i2c_Stop();
	return 0;
}
/*设置ADM1191  ADC为单次转换*/
uint8_t adm1191_convert_once(uint8_t addr)
{
	i2c_Start();
	i2c_SendByte(addr|ADM1191_I2C_WR);
	if(i2c_WaitAck() != 0)
	{
		goto cmd_fail;
	}
	i2c_SendByte(0x05);
	if(i2c_WaitAck() != 0)
	{
		goto cmd_fail;
	}
	i2c_Stop();
	return 1;
	cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
	/* 发送I2C总线停止信号 */
	i2c_Stop();
	return 0;
}
/*从ADM1191读取电压电流*/
uint8_t adm1191_rd_VI(uint8_t *_pReadBuf,uint8_t addr)
{
	CONV_EN();
	delay_us(300);
	i2c_Start();
	i2c_SendByte(addr | ADM1191_I2C_RD);
	if (i2c_WaitAck() != 0)
	{
		goto cmd_fail;	/* EEPROM器件无应答 */
	}
	for(uint8_t i=0;i<3;i++)
	{		
			_pReadBuf[i] = i2c_ReadByte();
			if(i == 2)
			{
				i2c_NAck();
			}
			else
				i2c_Ack();	
	}
	i2c_Stop();
	CONV_DISABLE();
	return 1;
	
	cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
	/* 发送I2C总线停止信号 */
	i2c_Stop();
	return 0;
}

uint8_t Get_vi_from_1191(uint8_t addr,uint8_t *_pReadBuf,float *V,float * I)
{
	/*1.检查I2C设备*/
	int vcode,icode;
	uint8_t temp;
	if(Check_I2cdev_Ok(addr))
	{
		if(adm1191_convert_once(addr))
		{
			if(adm1191_rd_VI(_pReadBuf,addr))
			{
				temp=_pReadBuf[2];
				vcode=(_pReadBuf[0]<<4) | (_pReadBuf[2] >> 4);
				icode=(_pReadBuf[1]<<4)	|	(temp & 0x0F);
				*V=(26.52/4096)*vcode;
				*I=(105.84/4096)*icode/1000/0.01;
				return 1;
			}
			else
			{
				__485print_str("read VI failed\r\n");
				return 0;
			}	
		}
		else
		{
			__485print_str("config adm1191 failed\r\n");
			return 0;
		}
	}
	else
	{
		__485print_str("no find i2c device");
		return 0;
	}
}

使用定时器采样,2s采样一次,我使用的是基本定时器TIM6,定时器初始化过程略过,下面是定时器中断处理函数:

void TIM6_DAC_IRQHandler(void)
{
	if ( TIM_GetITStatus( BASIC_TIM, TIM_IT_Update) != RESET ) 
	{	
		/*监控arm电压电流*/
	if(Get_vi_from_1191(ADDRESS_ADM1191_ARM,V_ARM,&arm_v,&arm_i))
			{
					__485print_str("ARM供电电压 : ");	
					__485_print_float(arm_v);
					__485print_str("\r\n");
					__485print_str("ARM供电电流 : ");	
					__485_print_float(arm_i);
					__485print_str("\r\n");
			}
			else
			{
				__485print_str("get arm_v failed\r\n");
			}
			/*监控光源供电电压*/
if(Get_vi_from_1191(ADDRESS_ADM1191_OPTICAL,V_OPTICAL,&opt_v,&opt_i))
			{
				__485print_str("光源供电电压 : ");
				__485_print_float(opt_v);
				__485print_str("\r\n");
				__485print_str("光源供电电流 : ");
				__485_print_float(opt_i);
				__485print_str("\r\n");
			}
			else
			{
				__485print_str("get optical_v failed\r\n");
			}
			/*监控数采板供电电压*/
	if(Get_vi_from_1191(ADDRESS_ADM1191_SAMPLE,V_SAMPLE,&sam_v,&sam_i))
			{
					__485print_str("数采板供电电压 : ");
					__485_print_float(sam_v);
					__485print_str("\r\n");
					__485print_str("数采板供电电流 : ");
					__485_print_float(sam_i);
					__485print_str("\r\n");
			}
			else
			{
				__485print_str("get sample_v failed\r\n");
			}
		TIM_ClearITPendingBit(BASIC_TIM , TIM_IT_Update);  		 
	}		
}
主函数:
float arm_v,sam_v,opt_v;
/*用于存放转换后的真实电流值*/
float arm_i,sam_i,opt_i;

float temp4,temp5;
u16 temp4_adc,temp5_adc;
extern __IO uint16_t adc_value[2];
int main()
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级分组
	LED_GPIO_Config();	//初始化LED
	init_control_power_pin(); //上电使能引脚初始化
	CONV_GPIO_Config();	//ADM1191采样控制引脚初始化
	ALERT_Pin_Config();	//过流过压报警引脚初始化
	EXTI_Config();	//外部中断初始化
	TIMx_Configuration();	//电压电流采样定时器初始化
	delay_init(168);	//初始化延时
	_485_init(115200);	//485初始化
	while(1)
	{
			LED_TOGGLE;
			delay_ms(1000);
	}
}

三、烧写测试
测试结果
到此完结,第一次创作,不喜勿喷~~~~~~~~~~

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值