I2c控制瀚强PSU-3300电源

一、PSU-3300电源

1、功能说明

    主要通过此电源的主路电压给算力板供电,侧路电压接口给控制板供电,可以通过控制板的i2c接口去动态调节算力板供电电压。

2、参数说明

  • IIC设备地址为0x2c。

  • PSU version为0x04.

  • 通讯速度为50khz.

  • 数据传输指令格式为N byte data + 1Byte CRC8校验码。

  • 控制板通过发送指令调整主路电压输出。

3、协议有无说明。

image.png

4、调压指令说明

image.png

 

5、错误码说明

image.png

6、i2c时序说明

image.png

由时序可知,主机写数据给PSU的时序为:

  • 发送开始信号。

  • 发设备地址(7位地址+1个读写位)。

  • 从机应答ack.

  • 发要读的寄存器地址。

  • 从机应答。

  • 发送数据(先发高位,再发地位)。

  • 发stop信号。

读时序:

  • 发送开始信号。

  • 发送要读的设备地址(7位地址+W)

  • 回ACK。

  • 发送要读的寄存器地址。

  • 回ACK.

  • 再发送设备地址   (7为地址+R)

  • ACK

  • 读取PSU发过来的数据。

  • 读取CRC8(经示波器实验,并没有收到CRC8校验码)。

  • stop.                    

二、主要函数的说明。

由PSU的时序可知是标准的IIC通讯时序。

cgminer 中有以下几个接口进行i2c设备的读写。

/* common i2c context */
struct i2c_ctx {
	/* destructor */
	void (*exit)(struct i2c_ctx *ctx);
	/* write one byte to given register */
	bool (*write)(struct i2c_ctx *ctx, uint8_t reg, uint8_t val);
	/* read one byte from given register */
	bool (*read)(struct i2c_ctx *ctx, uint8_t reg, uint8_t *val);
	/* read one word from given register */
	bool (*read_word)(struct i2c_ctx *ctx, uint8_t reg, uint16_t *val);
	/* write multiple bytes to addr */
	bool (*write_raw)(struct i2c_ctx *ctx, uint8_t *buf, uint32_t len);
	/* read multiple bytes from addr */
	bool (*read_raw)(struct i2c_ctx *ctx, uint8_t *buf, uint32_t len);
	/* common data */
	uint8_t addr;
	int file;
};

     static bool i2c_slave_read(struct i2c_ctx *ctx, uint8_t reg, uint8_t *val);
     static bool i2c_slave_write(struct i2c_ctx *ctx, uint8_t reg, uint8_t val)
     static bool i2c_slave_read_word(struct i2c_ctx *ctx, uint8_t reg, uint16_t *val);
     static bool i2c_slave_write_raw(struct i2c_ctx *ctx, uint8_t *buf, uint32_t len);
     static bool i2c_slave_read_raw(struct i2c_ctx *ctx, uint8_t *buf, uint32_t len);

1、i2c_slave_read函数说明。

功能:读取一个字节的数据。

参数说明

  • ctx :结构体对象,可通过指针去访问上面几个read,write函数

  • reg:要读的寄存器的地址。

  • val:获取到一个字节的数据

static bool i2c_slave_read(struct i2c_ctx *ctx, uint8_t reg, uint8_t *val)
{
	union i2c_smbus_data data;
	struct i2c_smbus_ioctl_data args;

	args.read_write = I2C_SMBUS_READ;
	args.command = reg;
	args.size = I2C_SMBUS_BYTE_DATA;  //读取一个字节的数据
	args.data = &data;

	if (ioctl(ctx->file, I2C_SMBUS, &args) == -1) {
		applog(LOG_INFO, "i2c 0x%02x: failed to read from fdesc %d: %s",
		       ctx->addr, ctx->file, strerror(errno));
		return false;
	}
	*val = data.byte;
	applog(LOG_DEBUG, "I2C-R(0x%02x/0x%02x)=0x%02x", ctx->addr, reg, *val);
	return true;
}

2、i2c_slave_write

功能:写一个字节的数据。

参数说明

  • ctx :结构体对象,可通过指针去访问上面几个read,write函数

  • reg:要读的寄存器的地址。

  • val:要写入的一个字节的数据。

3、i2c_slave_read_word

    由psu规格书可知,在读电流、电压等数据时要发送两个字节的数据。根据i2c_slave_read函数,可知args.size可以设置读取的字节数。根据i2c驱动代码可知,有以下三种类型选择,byte:一个字节;word:两个字节;block:最多可以写32个字节。

union i2c_smbus_data {
	__u8 byte;
	__u16 word;
	__u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
			       /* and one more for user-space compatibility */
};
static bool i2c_slave_read_word(struct i2c_ctx *ctx, uint8_t reg, uint16_t *val)
{
	union i2c_smbus_data data;
	struct i2c_smbus_ioctl_data args;

	args.read_write = I2C_SMBUS_READ;
	args.command = reg;
	args.size =I2C_SMBUS_WORD_DATA;
	args.data = &data;

	if (ioctl(ctx->file, I2C_SMBUS, &args) == -1) {
		applog(LOG_INFO, "i2c 0x%02x: failed to read from fdesc %d: %s",
		       ctx->addr, ctx->file, strerror(errno));
		return false;
	}
	*val = data.word;
	//printf("data.word=%x",data.word);
	applog(LOG_DEBUG, "I2C-R(0x%02x/0x%02x)=0x%02x", ctx->addr, reg, *val);
	return true;
}

4、i2c_slave_write_raw。

功能:写多个字节的数据。

参数说明:

  •  ctx:结构体对象,可通过指针去访问上面几个read,write函数。

  • buf:写入数据的buf

  • len:buf的长度。

static bool i2c_slave_write_raw(struct i2c_ctx *ctx, uint8_t *buf, uint32_t len)
{
	/* SMBus cann't support write bytes > 32, use plain i2c write */
	if (len != write(ctx->file, buf, len)) {
		applog(LOG_INFO, "i2c 0x%02x: failed to write raw to fdesc %d: %s",
		       ctx->addr, ctx->file, strerror(errno));
		return false;
	}
	applog(LOG_DEBUG, "I2C-W-RAW(0x%02x)", ctx->addr);
	return true;
}

5、i2c_slave_read_raw

    此函数可读取多个字节的数据。但是在read之前要进行write操作,不然不知道读取哪个地址的数据。

static bool i2c_slave_read_raw(struct i2c_ctx *ctx, uint8_t *buf, uint32_t len)
{
	/* SMBus cann't support read bytes > 32, use plain i2c read */
	if (len != read(ctx->file, buf, len)) {
		applog(LOG_INFO, "i2c 0x%02x: failed to read raw from fdesc %d: %s",
		       ctx->addr, ctx->file, strerror(errno));
		return false;
	}
	printf("ctx->addr=%x
",ctx->addr);
	applog(LOG_DEBUG, "I2C-R-RAW(0x%02x)", ctx->addr);
	return true;
}

6、i2c_slave_open函数

功能:open i2c进行读写,并进行从设备地址的设置。

extern struct i2c_ctx *i2c_slave_open(char *i2c_bus, uint8_t slave_addr)
{
	int file = open(i2c_bus, O_RDWR);
	if (file < 0) {
		applog(LOG_INFO, "Failed to open %s: %s", i2c_bus, strerror(errno));
		return NULL;
	}

	if (ioctl(file, I2C_SLAVE, slave_addr) < 0) {
		close(file);
		return NULL;
	}
	struct i2c_ctx *ctx = malloc(sizeof(*ctx));
	assert(ctx != NULL);

	ctx->addr = slave_addr;
	ctx->file = file;
	ctx->exit = i2c_slave_exit;
	ctx->read = i2c_slave_read;
	ctx->read_word = i2c_slave_read_word;
	ctx->write = i2c_slave_write;
	ctx->read_raw = i2c_slave_read_raw;
	ctx->write_raw = i2c_slave_write_raw;
	return ctx;
}

  

7、i2c_slave_exit

    功能:关闭文件句柄,释放内存空间。

static void i2c_slave_exit(struct i2c_ctx *ctx)
{
	if (ctx->file == -1)
		return;
	close(ctx->file);
	free(ctx);
}

三、功能实现。

根据PSU-3300规格书主要分为以下功能:

  • 读PSU Version。

  • 主路电压进行开关操作,并读取开关状态。

  • 读PSU的error code.

  • 读侧路电压(接控制板)。

  • 读主路电压(接算力板)。

  • 读主路电流。

  • 主路输出功率。

  • 读SR温度(后级散热器温度)。

  • 读PFC温度(前级散热器温度)。   

  • 读PSU fan1的转速。

  • 设置主路输出电压。

1、代码实现。

读写函数实现:

//写多个字节数据
uint8_t   avalon9_write_PSU(uint8_t* buff,uint8_t len)
{
	uint8_t ret;
	struct i2c_ctx * fd;
	fd = i2c_slave_open("/dev/i2c-3", 0x2c);
	if(fd == NULL)
	{
	   	printf("open dev error!");
		return 0;
	}
    printf("write %d byte
",len);
	ret= fd ->write_raw(fd, buff , len) ;
	if(ret != true)
	{
		printf("write data error!
");
		return 0;
	}
	
	if(fd !=NULL)
	{
		fd->exit(fd);
      }	

	return 1;
	
}
//读2个字节
uint8_t   avalon9_Read_Word_from_PSU(uint8_t reg, uint16_t *val)
{
	uint8_t ret;
	struct i2c_ctx * fd;
	fd = i2c_slave_open("/dev/i2c-3", 0x2c);
	if(fd == NULL)
	{
	   	printf("open dev error!");
		return 0;
	}
	ret = fd->read_word(fd, reg , val);
	if(ret != true)
	{
		printf("read data error!
");
		return 0;
	}
	printf("read word data:val=%x
",*val);	
		
	
	if(fd !=NULL)
	{
		fd->exit(fd);
      }	
	return 1;
}

//读1个字节
uint8_t   avalon9_Read_Single_byte_from_PSU(uint8_t reg, uint8_t *val)
{
	uint8_t ret;
	struct i2c_ctx * fd;
	
	fd = i2c_slave_open("/dev/i2c-3", 0x2c);
	if(fd == NULL)
	{
	   	printf("open dev error!");
		return 0;
	}
	ret= fd ->read(fd, reg , val) ;
	
	if(ret != true)
	{
		printf("read data error!
");
		return 0;
	}
	printf("read single byte data:val=%x
",*val);
	
	if(fd !=NULL)
	{
		fd->exit(fd);
      }	
	return 1;
}

  

(1)读PSU version.

image.png

由规格书可知,psu version的寄存器地址位0x00,version只占一个字节。

image.png

uint8_t avalon9_Get_PSU_Version()
{
 	 uint8_t psuVersionReg = 0x00;
	 uint8_t psuVersion=0,ret = 0;
	 ret = avalon9_Read_Single_byte_from_PSU(psuVersionReg,&psuVersion);
	  if(ret == 0)
	  {
			printf("read psu Version data error!
");
		return 0;	
         }
	  return psuVersion;
		
}
(2)设置主路电压输出开关

image.png

设置主路电压输出状态的寄存器地址为0x02,写0x01 enable,写0x00 disable,目前PSU默认时关闭主路电压输出的。image.png

image.png

    由规格书可知,控制板写数据到PSU的写设备地址为0x58,读设备地址为0x59.

image.png

CRC8校验码的计算方式是,CRC(设备地址,寄存器地址,要写入的数据)。

CRC8的算法代码

char crc8(const void* vptr, int len)
{
	const char *data = vptr;
	unsigned crc = 0;
	int i, j;
	 printf("
 len=%ld
",sizeof(vptr)/sizeof(vptr[0]));
	for (j = len; j; j--, data++) {
		crc ^= (*data << 8);
		for(i = 8; i; i--) {
			if (crc & 0x8000)
			crc ^= (0x1070 << 3);
			crc <<= 1;
		}
		
	}
	return (char)(crc >> 8);
}

将 {0x58,0x02,0x01}代入会计算出CRC值为0x58,也可以只用CRC8计算器计算.

image.png

发送{0x02,0x01,0x58}给PSU就打开主路电压输出。

void avalon9_Set_PowrOn()
{
	uint8_t ret = 0;
	//RegAddr+data+CRC
	uint8_t powerOnCode[] = {0x02, 0x01,0x58};
	printf("Set Power On !
");
       ret = avalon9_write_PSU(powerOnCode,sizeof(powerOnCode)/sizeof(powerOnCode[0]));
	 if(ret == 0)
	{
		printf("Set Power On failed!
");
	}	
	
}

关闭也是同理,这里不再赘述。

(3)读取主路电压状态(1个字节).

uint8_t avalon9_Get_PowrOnOff()
{
 	uint8_t psuPowerstausReg = 0x02;
 	uint8_t psuPowerStatus=0xff,ret=0;
      ret = avalon9_Read_Single_byte_from_PSU(psuPowerstausReg,&psuPowerStatus);
	  if(ret == 0)
	  {
			printf("read psu Power Staus Failed!
");
		return 2;	
	     }
      	 return  psuPowerStatus;
	
}
(4)读取主路电压

image.png

    寄存器地址为0x07,读取2个字节。先读到高位,再读低位。读取到16bit的数据转为十进制然后乘0.01就是实际的电压值。

image.png

float avalon9_Get_PSUSideRoad_Voltage()
{
	uint8_t psuVSBoutReg = 0x06,ret=0;  //侧路电压
	uint16_t psuVSBout = 0;
	  ret = avalon9_Read_Word_from_PSU(psuVSBoutReg,&psuVSBout);
	  if(ret == 0)
	  {
			printf("Read PSU Error Code Failed!
");
		return 0;	
	     }
      	 return  psuVSBout*0.01;
}

(5)设置主路电压

设置主路电压12.5V波形image.png

void avalon9_Set_PSUMainRoad_Voltage(float val)
{	
	uint8_t ret = 0;
	uint8_t Voutcode[11][4] = {
							{0x12,0xb0,0x04,0x6b},    //12.00
							{0x12,0xba,0x04,0xe9},    //12.10
							{0x12,0xc4,0x04,0x9d},   //12.20
							{0x12,0xce,0x04,0x1f},   //12.30
							{0x12,0xd8,0x04,0x36},   //12.40
							{0x12,0xe2,0x04,0x4d},  //12.50	
							{0x12,0xec,0x04,0x9b},  //12.60
							{0x12,0xf6,0x04,0x4e},  //12.70
							{0x12,0x00,0x05,0x23},  //12.80
							{0x12,0x0a,0x05,0xa1},  //12.90
							{0x12,0x14,0x05,0x20} //13.00
						};
	printf("set MainRoad Voltage=%f
",val);
	
	switch((uint8_t)((val)*10))  //switch参数必须为整数。
	{
		
		case 121:
			 ret = avalon9_write_PSU(Voutcode[1],4);
			break;
		case 122:
			 ret = avalon9_write_PSU(Voutcode[2],4);
			break;	
		case 123:
			 ret = avalon9_write_PSU(Voutcode[3],4);
			break;
		case 124:
			 ret = avalon9_write_PSU(Voutcode[4],4);
			break;
		case 125:
			printf("12.5
");
			 ret = avalon9_write_PSU(Voutcode[5],4);
			break;
		case 126:
		 	ret = avalon9_write_PSU(Voutcode[6],4);
			break;
		case 127:
		 	ret = avalon9_write_PSU(Voutcode[7],4);
			break;
		case 128:
		 	ret = avalon9_write_PSU(Voutcode[8],4);
			break;
		case 129:
		 	ret = avalon9_write_PSU(Voutcode[9],4);
			break;	
		case 130:
		 	ret = avalon9_write_PSU(Voutcode[10],4);
			break;	
			
		default:
			 ret = avalon9_write_PSU(Voutcode[0],4);
			break;	
	}
	
	 if(ret == 0)
	{
		printf("Set mainRoad Voltage Failed!
");
	}	
}

  

其他的功能类似就不再赘述了。

6、设置第四路i2c通讯速度

image.png

原文地址:https://www.cnblogs.com/yuanqiangfei/p/15014281.html