zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Android系统源代码下载
    Windows Embedded Compact 7初体验
    windowsmobile 开发环境
    Windows X64汇编入门(1)
    汇编语言的Hello World
    如何构建Win32汇编的编程环境(ONEPROBLEM个人推荐)
    音频视频解决方案:GStreamer/ffmpeg/ffdshow/directshow/vfw
    汇编开发环境
    DirectX
    关于DirectShow SDK 和Windows SDK,及DirectX SDK
  • 原文地址:https://www.cnblogs.com/yuanqiangfei/p/15014281.html
Copyright © 2011-2022 走看看