【电源】通过I2c控制瀚强PSU-3300电源

一、PSU-3300电源

1、功能说明

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

2、参数说明

  • IIC设备地址为0x2c。

  • PSU version为0x04.

  • 通讯速度为50khz.

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

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

3、协议有无说明。

4、调压指令说明

5、错误码说明

6、i2c时序说明

 

 

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

  • 发送开始信号。

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

  • 从机应答ack.

  • 发要读的寄存器地址。

  • 从机应答。

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

  • 发stop信号。

读时序:

  • 发送开始信号。

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

  • 回ACK。

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

  • 回ACK.

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

  • ACK

  • 读取PSU发过来的数据。

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

  • stop.                    

二、主要函数的说明。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* 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:获取到一个字节的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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个字节。

1
2
3
4
5
6
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 */
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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的长度。

1
2
3
4
5
6
7
8
9
10
11
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操作,不然不知道读取哪个地址的数据。

1
2
3
4
5
6
7
8
9
10
11
12
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\n",ctx->addr);
    applog(LOG_DEBUG, "I2C-R-RAW(0x%02x)", ctx->addr);
    return true;
}

6、i2c_slave_open函数

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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

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

1
2
3
4
5
6
7
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、代码实现。

读写函数实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//写多个字节数据
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\n",len);
    ret= fd ->write_raw(fd, buff , len) ;
    if(ret != true)
    {
        printf("write data error!\n");
        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!\n");
        return 0;
    }
    printf("read word data:val=%x\n",*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!\n");
        return 0;
    }
    printf("read single byte data:val=%x\n",*val);
      
    if(fd !=NULL)
    {
        fd->exit(fd);
      
    return 1;
}
(1)读PSU version.

 

 

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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!\n");
        return 0;
         }
      return psuVersion;
          
}
(2)设置主路电压输出开关

 

 

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

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

 

 

 

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

CRC8的算法代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
char crc8(const void* vptr, int len)
{
    const char *data = vptr;
    unsigned crc = 0;
    int i, j;
     printf("\n len=%ld\n",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计算器计算.

 

 

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

1
2
3
4
5
6
7
8
9
10
11
12
13
void avalon9_Set_PowrOn()
{
    uint8_t ret = 0;
    //RegAddr+data+CRC
    uint8_t powerOnCode[] = {0x02, 0x01,0x58};
    printf("Set Power On !\n");
       ret = avalon9_write_PSU(powerOnCode,sizeof(powerOnCode)/sizeof(powerOnCode[0]));
     if(ret == 0)
    {
        printf("Set Power On failed!\n");
    
      
}

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
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!\n");
        return 2;
         }
           return  psuPowerStatus;
      
}
(4)读取主路电压

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

1
2
3
4
5
6
7
8
9
10
11
12
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!\n");
        return 0;
         }
           return  psuVSBout*0.01;
}

  

(5)设置主路电压

设置主路电压12.5V波形

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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\n",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\n");
             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!\n");
    
}

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

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

 

 

posted @   轻轻的吻  阅读(405)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示