HPM6750

简介

本文主要介绍了HPM6750的控制器局域网CAN(以下简称CAN控制器)的概述以及基于HPM-SDK CAN控制器的开发指导(包括实现CAN2.0、CAN-FD)。

CAN控制器

1. 概述

CAN 是 Controller Area Network 的缩写(以下称为 CAN),是 ISO 国际标准化的串行通信协议。HPM6750 MCU搭载了4路CAN控制器,CAN0/CAN1/CAN2/CAN3,它们具有如下特性:

● 支持 CAN 2.0B 协议,支持多达 8 字节的数据载荷, 数据速率可达 1Mbit/s;

● 支持 CAN FD 协议,支持多达 64 字节的数据载荷, 数据速率可达 2.5Mbit/s;

● 支持 1 ∼ 1/256 的波特率预分频,灵活配置波特率;

● 16 个接收缓冲器;

– FIFO 方式;

– 错误或者不被接收的数据不会覆盖存储的消息;

● 1 个高优先主发送缓冲器 PTB;

● 8 个副发送缓冲器 STB;

– FIFO 方式;

– 优先级仲裁方式;

● 16 组独立的筛选器;

– 支持 11 位标准 ID 和 29 位扩展 ID;

– 可编程 ID CODE 位以及 MASK 位;

● PTB/STB 均支持支持单次发送模式;

● 支持静默模式;

● 支持回环模式;

● 支持待机模式;

● 支持捕捉传输的错误种类以及定位仲裁失败位置;

● 可编程的错误警告值;

● 支持 ISO11898-4 规定时间触发 CAN 以及接收时间戳可配置停止位:1位,1.5位或者2位。

2. 系统框图

1.png

3. 管脚

2.png

CAN控制器功能开发指引

1. API功能描述

CAN开发主要使用以下接口:

//获取CAN默认配置
hpm_stat_t can_get_default_config(can_config_t *config);
//CAN 初始化接口
hpm_stat_t can_init(CAN_Type *base, can_config_t *config, uint32_t src_clk_freq);
//接收过滤器配置
hpm_stat_t can_set_filter(CAN_Type *base, const can_filter_config_t *config);
//CAN 数据发送接口(阻塞模式)
hpm_stat_t can_send_message_blocking(CAN_Type *base, const can_transmit_buf_t *message);
//CAN高优先级数据发送接口(PTB 阻塞模式)
hpm_stat_t can_send_high_priority_message_blocking(CAN_Type *base, const can_transmit_buf_t *message);
//CAN 数据接收接口(阻塞模式)
hpm_stat_t can_receive_message_blocking(CAN_Type *base, can_receive_buf_t *message);
//CAN 数据接收接口(非租塞模式)
hpm_stat_t can_read_received_message(CAN_Type *base, can_receive_buf_t *message);
//设置发送补偿及使能(CAN-FD高速率使用,TDC)
void can_set_transmitter_delay_compensation(CAN_Type *base, uint8_t sample_point, bool enable);

2. API数据结构

2.1 CAN配置

typedef struct{    
  union{        
    struct {           
      //当禁用use_lowlevel_timing_setting时,以下参数有效。            
      uint32_t baudrate;  //CAN 2.0波特率设定            
      uint32_t baudrate_fd; // CAN-FD波特率设定,当enable_canfd使能才有效                       
      uint16_t can20_samplepoint_min; //CAN 2.0最小采样点(0~1000)                       
      uint16_t can20_samplepoint_max; //CAN 2.0最大采样点(0~1000)            
      uint16_t canfd_samplepoint_min; //CAN-FD 最小采样点(0~1000)            
      uint16_t canfd_samplepoint_max; //CAN-FD 最大采样点(0~1000)        
    };        
    struct {
      //当启用use_lowlevel_timing_setting时,以下参数有效。            
      can_bit_timing_param_t can_timing;  //CAN2.0 位时间参数            
      can_bit_timing_param_t canfd_timing; //CAN-FD 位时间参数        
    };    
  };
can_loopback_mode_t loopback_mode;    //CAN回环模式,默认是正常模式
bool use_lowlevel_timing_setting;     //是否启用位时间参数设定     
bool enable_canfd;   //是否启用CAN-FD     
bool enable_self_ack;   //是否启用自ACK帧
bool disable_re_transmission_for_ptb;  //是否禁用高优先级PTB发送重传, false:单发模式 true:重传模式
bool disable_re_transmission_for_stb;  //是否禁用STP发送重传, false:单发模式, true:重传模式
uint16_t filter_list_num;   //接受过滤器list总数
can_filter_config_t *filter_list;  //接受过滤器list指针
} can_config_t;

2.2 CAN过滤配置

/** 
    * @brief CAN acceptance filter modes 
    */
typedef enum _can_filter_mode {    
    can_filter_mode_both_frames,                //标准格式和扩展格式过滤选模式
    can_filter_mode_standard_frames,            //标准格式过滤模式
    can_filter_mode_extended_frames,            //扩展格式过滤模式
} can_filter_mode_t;

/** 
    * @brief CAN acceptance configuration 
*/
typedef struct {
    uint16_t index;                             //过滤器index
    can_filter_mode_t mode;                 //过滤器模式     
    bool enable;                               //过滤器是否使能     
    uint32_t code;                              //ID code     
    uint32_t mask;                              //ID mask
} can_filter_config_t;

2.3 CAN发送

/** 
    * @brief CAN transmit buffer data structure 
*/
typedef union _can_tx_buf {
uint32_t buffer[18];    //发送 buffer,由于是联合体,和下面的共享一块内存区域,buffer大小:4*18=72
struct {        
        struct {            
                uint32_t id: 29;                      //CAN ID            
                uint32_t : 1;            
                uint32_t transmit_timestamp_enable: 1;  //时间戳使能        
         };        
         struct {            
                 uint32_t dlc: 4;                        //数据长度            
                 uint32_t bitrate_switch: 1;             //bitrate开关            
                 uint32_t canfd_frame: 1;                //can-fd标识位            
                 uint32_t remote_frame: 1;               //remote 标识位            
                 uint32_t extend_id: 1;                  //扩展ID            
                 uint32_t : 24;        
          };        
          uint8_t data[];                             //数据指针    
     };
} can_transmit_buf_t;

2.4 CAN接收

/** 
    * @brief CAN receive buffer data structure 
    */
    typedef union _can_rx_buf {    
        uint32_t buffer[20];           //接收buffer,由于是联合体,和下面的数据共享一块内存区域    
        struct {        
            struct {            
                uint32_t id: 29;        //can id            
                uint32_t : 1;            
                uint32_t error_state_indicator: 1;  //错误状态指示        
            };        
            struct {            
                uint32_t dlc: 4;                   //数据长度            
                uint32_t bitrate_switch: 1;        //bitrate开关            
                uint32_t canfd_frame: 1;           //canfd 标识            
                uint32_t remote_frame: 1;          //remote标识            
                uint32_t extend_id: 1;             //扩展ID            
                uint32_t : 4;            
                uint32_t loopback_message: 1;      //回环数据标识            
                uint32_t error_type: 3;            //错误类型            
                uint32_t cycle_time: 16;           //cycle time        
            };        
            uint8_t data[];                        //数据指针    
        };
} can_receive_buf_t;

3. 配置流程

CAN控制器的CAN2.0和CAN-FD配置流程如下图。

3.png

4. 样例

4.1 内部回环样例

需求:

1.CAN-FD协议

2.波特率2.5Mbps

3.内部回环模式

4.数据载荷64字节

5.遍历can-id从0~2047(11位标准ID)

6.每帧数据确保不同

7.阻塞发送、非阻塞接收(非中断模式)

8.对比接收和发送的数据包是否相等,并输出结果

void board_can_loopback_test(void)
{    
    bool result;    
    uint32_t error_cnt = 0;    
    uint32_t can_src_clk_freq;    
    can_config_t can_config;    
    board_init_can(BOARD_APP_CAN_BASE);    
    can_src_clk_freq = board_init_can_clock(BOARD_APP_CAN_BASE);    
    can_config.baudrate = 1000000; /* 1Mbps */    
    can_config.baudrate_fd = 2500000; /*5Mbps*/    
    can_config.loopback_mode = can_loopback_internal; //内部回环    
    can_config.enable_canfd = true;    
    hpm_stat_t status = can_init(BOARD_APP_CAN_BASE, &can_config, can_src_clk_freq);    
    if (status != status_success) {        
        printf("CAN initialization failed, error code: %d\n", status);        
        return;    
    }    
    can_transmit_buf_t tx_buf;    
    can_receive_buf_t rx_buf;    
    memset(&tx_buf, 0, sizeof(tx_buf));    
    memset(&rx_buf, 0, sizeof(rx_buf));    
    tx_buf.dlc = can_payload_size_64;    
    tx_buf.canfd_frame = 1;    
    tx_buf.bitrate_switch = 1;    
    for (uint32_t i = 0; i < 2048; i++) {        
        tx_buf.id = i;        
        for (uint32_t j = 0; j < 64u; j++) {            
            tx_buf.data[j] = (uint8_t)i + j + 1;        
        }        
        can_send_message_blocking(BOARD_APP_CAN_BASE, &tx_buf);        
        can_read_received_message(BOARD_APP_CAN_BASE, &rx_buf);        
        result = can_buf_compare(&tx_buf, &rx_buf);        
        if (!result) {            
            error_cnt++;            
            can_set_transmitter_delay_compensation(BOARD_APP_CAN_BASE, 64, true);            
            hpm_stat_t status = can_init(BOARD_APP_CAN_BASE, &can_config, can_src_clk_freq);            
            if (status != status_success) {                
                printf("CAN initialization failed, error code: %d\n", status);                
                return;            
            }            
            printf("ID=%08x, result:%s\n", rx_buf.id, result ? "passed": "failed");        
        }    
    }    
    printf("    CAN loopback test for extend frame %s, error_cnt:%d\n", error_cnt == 0 ? "passed" : "failed", error_cnt);
}

4.2 两路闭环收发样例

需求:

1.CAN2.0协议

2.波特率1000000,1Mbps

3.CAN0发送,CAN1接收

4.数据载荷8字节

5.CAN0阻塞发送,CAN1阻塞接收

6.对比CAN0发送包和CAN1接收包是否相同,并输出结果

7.压测100次,输出最终结果

void can0_can1_rxrx_loop_test(void)
{    
    pm_stat_t status;    
    can_config_t can_config;    
    bool use_canfd = false;    
    can_get_default_config(&can_config);    
    can_config.baudrate = 1000000; /* 1Mbps */    
    can_config.baudrate_fd = 5000000; /* 2Mbps */    
    can_config.enable_canfd = use_canfd;    
    board_init_can(HPM_CAN0);    
    board_init_can(HPM_CAN1);    
    uint32_t can_src_clk_freq0 = board_init_can_clock(HPM_CAN0);    
    uint32_t can_src_clk_freq1 = board_init_can_clock(HPM_CAN1);    
    hpm_stat_t status0 = can_init(HPM_CAN0, &can_config, can_src_clk_freq0);    
    if (status0 != status_success) {        
        printf("CAN initialization failed, error code: %d\n", status0);        
        return;    
    }    
    hpm_stat_t status1 = can_init(HPM_CAN1, &can_config, can_src_clk_freq1);    
    if (status1 != status_success) {        
        printf("CAN initialization failed, error code: %d\n", status1);        
        return;    
    }    
    printf("CMD_STA_CMD_CTRL(0xA0)= %08x\n", HPM_CAN0->CMD_STA_CMD_CTRL);    
    printf("F_PRESC               = %08x\n", HPM_CAN0->F_PRESC);    
    printf("S_PRESC               = %08x\n", HPM_CAN0->S_PRESC);    
    printf("TDC                   = %08x\n", HPM_CAN0->TDC);    
    uint32_t error_cnt = 0;    bool result = false;    can_transmit_buf_t tx_buf;    
    can_receive_buf_t rx_buf; memset(&tx_buf, 0, sizeof(tx_buf));    
    memset(&rx_buf, 0, sizeof(rx_buf));    
    tx_buf.id = 0x101;    uint32_t id_max;    
    if (!use_canfd) {        
        tx_buf.dlc = can_payload_size_8;        
        id_max = 8;    
    } else {        
        tx_buf.dlc = can_payload_size_8;        
        id_max = 64;        
        tx_buf.canfd_frame = 1;        
        tx_buf.bitrate_switch = 1;    
    }    
    for(int index = 0; index < 100; index++)    
    {            
        for (uint32_t i = 0; i < id_max; i++){            
            tx_buf.data[i] = (uint8_t)(index+i);        
        }        
        can_send_high_priority_message_blocking(HPM_CAN0, &tx_buf);        
        can_receive_message_blocking(HPM_CAN1, &rx_buf);        
        result = can_buf_compare(&tx_buf, &rx_buf);        
        if (!result) {            
            error_cnt++;            
            printf("    CAN0->CAN1 for standard frame %s\n", result ? "passed" : "failed");        
        }        
        can_receive_message_blocking(HPM_CAN0, &rx_buf);        
        result = can_buf_compare(&tx_buf, &rx_buf);        
        if (!result) {            
            error_cnt++;            
            printf("    CAN1->CAN0 for standard frame %s\n", result ? "passed" : "failed");        
        }    
    }    
    printf("    CAN can0 can1 rxrx loop test for result: %s, error_cnt:%d\n", error_cnt == 0 ? "passed" : "failed", error_cnt);
}

4.3 四路收发样例

需求:

1.CAN-FD协议

2.波特率2.5Mbps

3.数据载荷64字节

4.启用中断接收

5.CAN0/CAN1/CAN2/CAN3顺序发送数据

6.确保CAN0/CAN1/CAN2/CAN3 can-id不同

7.确保每次发送的数据包内容不同

8.分别对比每次一路CAN发送数据包和其它三路CAN接收的数据包是否相同,并输出结果

9.压测100次,并输出结果

static can_info_t s_can_info[] = {        
    { .can_base = HPM_CAN0 },        
    { .can_base = HPM_CAN1 },
#if defined(HPM_CAN2)        
    { .can_base = HPM_CAN2 },
#endif
#if defined (HPM_CAN3)        
    { .can_base = HPM_CAN3 },
#endif
};
volatile static bool has_new_rcv_msg_array[4];
volatile static can_receive_buf_t s_can_rx_buf_array[4];
SDK_DECLARE_EXT_ISR_M(IRQn_CAN0, board_can_isr0);
SDK_DECLARE_EXT_ISR_M(IRQn_CAN1, board_can_isr1);
SDK_DECLARE_EXT_ISR_M(IRQn_CAN2, board_can_isr2);
SDK_DECLARE_EXT_ISR_M(IRQn_CAN3, board_can_isr3);
void board_can_isr0(void)
{    
    uint8_t flags = can_get_tx_rx_flags(HPM_CAN0);    
    if ((flags & CAN_EVENT_RECEIVE) != 0) {        
        can_read_received_message(HPM_CAN0, (can_receive_buf_t *)&s_can_rx_buf_array[0]);        
        has_new_rcv_msg_array[0] = true;    
    }    
    can_clear_tx_rx_flags(HPM_CAN0, flags);
}
void board_can_isr1(void){    
    uint8_t flags = can_get_tx_rx_flags(HPM_CAN1);    
    if ((flags & CAN_EVENT_RECEIVE) != 0) {        
        can_read_received_message(HPM_CAN1, (can_receive_buf_t *)&s_can_rx_buf_array[1]);        
        has_new_rcv_msg_array[1] = true;    
    }    
    can_clear_tx_rx_flags(HPM_CAN1, flags);
}
void board_can_isr2(void)
{    
    uint8_t flags = can_get_tx_rx_flags(HPM_CAN2);    
    if ((flags & CAN_EVENT_RECEIVE) != 0) {        
        can_read_received_message(HPM_CAN2, (can_receive_buf_t *)&s_can_rx_buf_array[2]);        
        has_new_rcv_msg_array[2] = true;    
    }    
    can_clear_tx_rx_flags(HPM_CAN2, flags);
}
void board_can_isr3(void)
{    
    uint8_t flags = can_get_tx_rx_flags(HPM_CAN3);    
    if ((flags & CAN_EVENT_RECEIVE) != 0) {        
        can_read_received_message(HPM_CAN3, (can_receive_buf_t *)&s_can_rx_buf_array[3]);        
        has_new_rcv_msg_array[3] = true;    
    }    
    can_clear_tx_rx_flags(HPM_CAN3, flags);
}
void board_can0_1_2_3_txrx_loop_test(void){    
    hpm_stat_t status;    
    can_config_t can_config;    
    bool use_canfd = true;    
    can_get_default_config(&can_config);    
    can_config.baudrate = 1000000; /* 1Mbps */    
    can_config.baudrate_fd = 2500000; /* 5Mbps */    
    can_config.enable_canfd = use_canfd;    
    /* Initialize CAN */    
    for (uint32_t i=0; i < ARRAY_SIZE(s_can_info); i++) {        
        can_info_t  *info = &s_can_info[i];        
        board_init_can(info->can_base);        
        info->clock_freq = board_init_can_clock(info->can_base);        
        status = can_init(info->can_base, &can_config,  info->clock_freq);        
        if (status != status_success) {            
            printf("CAN %d initialization failed, error code: %d\n", i, status);            
            return;        
        }        
        printf("CMD_STA_CMD_CTRL(0xA0)= %08x\n", info->can_base->CMD_STA_CMD_CTRL);        
        printf("F_PRESC               = %08x\n", info->can_base->F_PRESC);        
        printf("S_PRESC               = %08x\n", info->can_base->S_PRESC);        
        printf("TDC                   = %08x\n", info->can_base->TDC);        
        can_enable_tx_rx_irq(info->can_base, CAN_EVENT_RECEIVE);    
    }    
    intc_m_enable_irq_with_priority(IRQn_CAN0, 1);    
    intc_m_enable_irq_with_priority(IRQn_CAN1, 1);    
    intc_m_enable_irq_with_priority(IRQn_CAN2, 1);    
    intc_m_enable_irq_with_priority(IRQn_CAN3, 1);

  
  uint32_t error_cnt = 0;    
  bool result = false;    
  can_transmit_buf_t tx_buf[4];    
  uint32_t data_max;    
  memset(tx_buf, 0, sizeof(tx_buf));    
  for(int i = 0; i < 4; i ++)    
  {        
      tx_buf[i].id = i+1;        
      if (!use_canfd) {            
          tx_buf[i].dlc = can_payload_size_8;            
          data_max = 8;        
      } else {            
          tx_buf[i].canfd_frame = 1;            
          tx_buf[i].bitrate_switch = 1;            
          tx_buf[i].dlc = can_payload_size_64;            
          data_max = 64;        
      }    
  }    
      for(int index = 0; index < 100; index++)    
      {        
          for(uint32_t can_i = 0; can_i < 4; can_i++)        
          {            
              for (uint32_t i = 0; i < data_max; i++) {                
                  tx_buf[can_i].data[i] = (uint8_t)(index+can_i+i);            
              }        
          }        
          for(uint32_t can_i = 0; can_i < 4; can_i++)        
          {            
              can_send_high_priority_message_blocking(s_can_info[can_i].can_base, &tx_buf[can_i]);            
              for(int j= 1; j < 4; j++)            
              {                
                  printf("recv canid:%d\n", (can_i+j)%4);                
                  while(!has_new_rcv_msg_array[(can_i+j)%4])                
                  {                
                  }                
                  has_new_rcv_msg_array[(can_i+j)%4] = false;                
                  result = can_buf_compare(&tx_buf[can_i], &s_can_rx_buf_array[(can_i+j)%4]);                
                  if (!result) {                    
                      error_cnt++;                
                  }                
                  printf("  CAN%d->CAN%d for standard frame %s\n", can_i, (can_i+j)%4, result ? "passed" : "failed");            
              }        
          }    
      }    
      printf("    CAN can0 can1 rxrx loop test for result: %s, error_cnt:%d\n", error_cnt == 0 ? "passed" : "failed", error_cnt);
}

划重点

使用HPM6750的CAN控制器,可以轻松实现4路CAN2.0/CAN-FD同时收发数据,易于实现CAN网络隔离以及网络中继的复杂需求,实现了工业网关的功能。

来源:先楫芯上人

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 11

本文摘自先楫开发者@xiashuang 的测评内容,分享了 先楫HPM6750 如何赋能 ADS1263 实现32位高精度数据采集及处理 ,来看看吧!

---------------  以下为测评内容 ---------------

(本期测评开发板为 HPM6750EVK)

据官方用户手册, TI 的32位ADC:ADS1263 性能很强,最高采样到38.4k(26us),需要的处理器性能必须要具有很强的运算功能,正好试下HPM6750,于是画了一块PCB板,经过一周的等待终于到了,焊接了必要的元件就开干!

1.jpg

2.jpg 

为了先验证板子的情况,先用软件模拟 SPI 进行实验,需要 7 根信号线进行连接通讯,定义的GPIO如下:

*RST---------PE25
*DRDY--------PE26
*MISO--------SPI2.MISO  PB25
*MOSI--------SPI2.MOSI  PB22
*SCK---------SPI2.SCK   PB21
*CS----------PF1
*START-------PF4

先初始化IO口,时钟的开启已经在board_init()中完成,所以指示配置一下寄存器就行

/*设置IO口为通用IO*/  
HPM_IOC->PAD[IOC_PAD_PE25].FUNC_CTL = IOC_PE25_FUNC_CTL_GPIO_E_25;     
HPM_IOC->PAD[IOC_PAD_PE26].FUNC_CTL = IOC_PE26_FUNC_CTL_GPIO_E_26;    
HPM_IOC->PAD[IOC_PAD_PB25].FUNC_CTL = IOC_PB25_FUNC_CTL_GPIO_B_25;    
HPM_IOC->PAD[IOC_PAD_PB22].FUNC_CTL = IOC_PB22_FUNC_CTL_GPIO_B_22;    
HPM_IOC->PAD[IOC_PAD_PB21].FUNC_CTL = IOC_PB21_FUNC_CTL_GPIO_B_21;    
HPM_IOC->PAD[IOC_PAD_PF01].FUNC_CTL = IOC_PF01_FUNC_CTL_GPIO_F_01;    
HPM_IOC->PAD[IOC_PAD_PF04].FUNC_CTL = IOC_PF04_FUNC_CTL_GPIO_F_04;
/*配置输入输出*/    
    /*ps-上下拉电阻 PE-上下拉开关 SMT-施密特 DS-驱动强度 OD-开漏  MS-电压选择*/    
    uint32_t pad_ctl_out = IOC_PAD_PAD_CTL_PE_SET(1) | IOC_PAD_PAD_CTL_PS_SET(1);    
    uint32_t pad_ctl_in = IOC_PAD_PAD_CTL_PE_SET(1) | IOC_PAD_PAD_CTL_PS_SET(1)|IOC_PAD_PAD_CTL_SMT_SET(1);    
    gpio_set_pin_output_with_initial(HPM_GPIO0, GPIO_DO_GPIOE, 25, 1);    
    gpio_set_pin_output_with_initial(HPM_GPIO0, GPIO_DO_GPIOB, 22, 1);    
    gpio_set_pin_output_with_initial(HPM_GPIO0, GPIO_DO_GPIOB, 21, 1);    
    gpio_set_pin_output_with_initial(HPM_GPIO0, GPIO_DO_GPIOF, 01, 1);    
    gpio_set_pin_output_with_initial(HPM_GPIO0, GPIO_DO_GPIOF, 04, 1);    
    HPM_IOC->PAD[IOC_PAD_PE25].PAD_CTL = pad_ctl_out;    
    HPM_IOC->PAD[IOC_PAD_PB22].PAD_CTL = pad_ctl_out;    
    HPM_IOC->PAD[IOC_PAD_PB21].PAD_CTL = pad_ctl_out;    
    HPM_IOC->PAD[IOC_PAD_PF01].PAD_CTL = pad_ctl_out;    
    HPM_IOC->PAD[IOC_PAD_PF04].PAD_CTL = pad_ctl_out;
    
     HPM_IOC->PAD[IOC_PAD_PE26].PAD_CTL = pad_ctl_in;    
     HPM_IOC->PAD[IOC_PAD_PB25].PAD_CTL = pad_ctl_in;

 相应的IO输出及输入读取如下:

#define ADS1263_RESET_H gpio_write_pin(HPM_GPIO0, GPIO_DO_GPIOE, 25, 1)
#define ADS1263_RESET_L gpio_write_pin(HPM_GPIO0, GPIO_DO_GPIOE, 25, 0)
#define ADS1263_START_H gpio_write_pin(HPM_GPIO0, GPIO_DO_GPIOF, 4, 1)
#define ADS1263_START_L gpio_write_pin(HPM_GPIO0, GPIO_DO_GPIOF, 4, 0)
#define ADS1263_CS_H    gpio_write_pin(HPM_GPIO0, GPIO_DO_GPIOF, 1, 1)
#define ADS1263_CS_L    gpio_write_pin(HPM_GPIO0, GPIO_DO_GPIOF, 1, 0)
#define ADS1263_SCLK_H  gpio_write_pin(HPM_GPIO0, GPIO_DO_GPIOB, 21, 1)
#define ADS1263_SCLK_L  gpio_write_pin(HPM_GPIO0, GPIO_DO_GPIOB, 21, 0)
#define ADS1263_DIN_H   gpio_write_pin(HPM_GPIO0, GPIO_DO_GPIOB, 22, 1)
#define ADS1263_DIN_L   gpio_write_pin(HPM_GPIO0, GPIO_DO_GPIOB, 22, 0)
#define ADS1263_DOUT    gpio_read_pin(HPM_GPIO0, GPIO_DI_GPIOB, 25)
#define ADS1263_DRDY    gpio_read_pin(HPM_GPIO0, GPIO_DI_GPIOE, 26)

初始化ADS1263

int init_ads1263(void)

{    
    printf("*************************************************************\r\n");    
    printf("*                                                           *\r\n");    
    printf("* ADS1263 TEST ^_^                                          *\r\n");    
    printf("*                                                           *\r\n");    
    printf("*************************************************************\r\n");
    
   GPIO_Configuration();    
   ADS1263_INIT();//ADS1263初始化    
   ADS1263_CS_L;    
   Delay(0xf);       
   ADS1263_WRITE(0x08);//START1 command,当START引脚为低电平时,可由此命令启动ADC1的转换。    
   ADS1263_CS_H;    
   Delay(0xf);    
   return 0;
}
读取AD值
int read_ads1263(void)
{    
    if(ADS1263_DRDY != 1)    
    {        
        ADS1263_CS_L;        
        Delay(0xf);        
        ADS1263_WRITE(0x12);//读取ADC1        
        STATUS=ADS1263_READ_REG();        
        ADC1_DATA=ADS1263_READ();        
        checksum = ADS1263_READ_REG();        
        ADS1263_CS_H;         
        count++;        
        D[47]++;        
        if(D[76] > 0)        
        {            
            ADC1_DATA = Filter_ch1(ADC1_DATA,D[76],D[77]);        
         }        
         ADC1_DATA = CELL_ADSOURSE_FILTER(ADC1_DATA,D[78],D[79]);
      
      mv_Now = ADC1_DATA / 2147483648.0 * 2500 / 32;//mv数        
      //测试重量参数        
      Weight = (mv_Now - mv_Zero)/ 10.0f * mv_Full * mv_Cali;        
      INT32toREG(ADC1_DATA,&D[0]);//源码        
      FP32toREG(mv_Now,&D[2]);//mv数        
      FP32toREG(Weight,&D[4]);//重量        
      //校秤参数        
      mv_Zero = REGtoFP32(&D[70]);//mv零点        
      mv_Full = REGtoFP32(&D[72]);//满量程        
      mv_Cali = REGtoFP32(&D[74]);//校准系数    
        
      return ADC1_DATA;   
   }    
   else        
       return 0;
}

利用上次移植的modbus 裸机例子,在主循环中不断查询AD状态读取。

连接好线,接上称重传感器开始测试

3.jpg

MODBUS上位机画面,使用50kg C3电阻应变桥式称重传感器采样400次精度在±1g,后来降低采样到60次/s,滑动平均5次,精度在±0.2g 。

4.png

逻辑分析仪抓取波形图。采用软件模拟SPI,速度在3M左右,读取命令+状态+4字节数据+校验共7个字节数据在20.5us左右

5.png

 【实验总结】

HPM6750 在配置IO时要注意名称,因为IO引脚较多,宏定义也比较多,在初始化容易写错(这次因为IO编号写错导致两个IO口没有输出,查了1个多小时)。看来图形化代码工具还是很有必要的(*悄咪咪告诉你们,先楫图形化代码工具已经上线使用中啦);

HPM6750 的驱动强度和施密特单独出来和I.MX RT系列比较像增加了IO控制的灵活性,PCB上高速信号的抗信号反射电阻和驱动限流电阻都可以省去了,等以后试下芯片的施密特能否代替外部输入上的 74HC14;

HPM6750 运算速度很快,等以后试试高阶FIR看看;

ADS1263 的高速采样及其以来模拟电源的纹波,对内部DCDC电源还是要增加滤波器,采样60HZ对50-hz和60hz纹波抑制后精度一下就上来了;

来源:先楫芯上人

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 16

本文导读

HPM6750支持jlink、dap和ft2232等多种调试方式,本文将对这几种调试方式展开介绍,方便大家灵活选用不同的调试方式。

硬件

本文所使用的开发板为HPM6750EVK。JTAG接口如图右下角所示,在外接使用JLINK或DAP时使用排线直接连接到开发板即可。

1.png

使用外接调试器时需要确保板载调试器不工作,因此可通过断开R111电阻来断开板载调试器的电源。

2.png

通过丝印图可轻易找到该电阻位于ft2232调试器上方。

3.png

JLINK 调试

使用数据线连接电脑和jlink后打开设备管理器,可以看到当前已成功识别到J-Link driver。

4.png

由于openocd无法通过jlink的驱动连接到芯片,因此需要借助Zadig将jlink驱动转换为WinUSB。打开Zadig后点击Options打开菜单栏并勾选上List All Devices显示所有驱动。

5.jpg

点击打开驱动下拉框,找到jlink设备。

6.jpg

确认设备选择正确后点击Replace Driver修改jlink为WinUSB。

7.jpg

修改成功后重新打开设备管理器,此时J-Link driver已被成功修改为BULK interface。

8.png

此时打开工程,通过鼠标右键工程名并点击Options打开工程配置。

9.jpg

在Debug目录中选择GDB Server,然后双击GDB Server Command Line打开修改窗口并将ft2232修改为jlink,最后全部点击OK保存退出。

10.png

使用排线连接好jlink和开发板后,通过打开菜单栏的Debug点击Go运行调试后,光标成功停在main函数即表明jlink调试功能已正常使用。

11.jpg

DAP 调试

连接电脑和DAP后,打开设备管理器会看到“通用串行总线控制器”中多了一个设备,通过查看详细信息可确认DAP已成功连接。

12.png

与jlink不同的是openocd支持使用dap,因此不需要修改usb驱动可直接打开工程配置,把原来的ft2232或jlink修改为cmsis_dap,保存退出,使用排线连接好dap和开发板后就可以直接通过dap进行调试了。

13.png

FT2232 调试

FT2232是HPM6750EVK开发板的板载调试器,使用的时候仅需要使用一条Type-C线与电脑连接即可(接大负载需额外供电)。如果之前使用过JLINK或DAP进行调试,请将电阻R111焊回去以确保FT2232供电正常,此时如有外接的调试器也请将其断开。

14.jpg

打开Zadig查看设备列表,能看到两个Dual RS232-HS设备,这里也选择编号较小的 Interface 0。然后点击Replace Driver安装驱动。

15.jpg

新创建的工程默认GDB Server就是ft2232无需修改,如果前面测试JLINK或DAP的时候修改过该配置,请将其重新修改为ft2232。

16.jpg

驱动验证

较简单的验证方式可在连接好开发板后,打开SEGGER Embedded Studio直接点击Debug – Go进入调试,代码自动编译下载后断点停在main函数说明驱动配置成功。

17.jpg

进入调试模式失败时,Output会有相关的信息提示。此时请仔细检查接线和上述驱动安装过程是否有问题。

18.jpg

想要查看更多连接信息可使用第二种验证方式,双击sdk根目录下的start_cmd.cmd打开调试窗口。

19.jpg

输入

set OPENOCD_SCRIPTS=%HPM_SDK_BASE%\boards\openocd

设置名为OPENOCD_SCRIPTS的环境变量:

20.png

然后输入

openocd -f probes/ft2232.cfg -f soc/hpm6750-single-core.cfg

运行openocd,如连接成功将如下所示打印相关信息:

21.png

当调试失败时,调试窗口也会打印错误信息。相较于使用SEGGER Embedded Studio,这里显示的调试信息更详细,有助于更快地排查出问题。

22.png

至此,三种调试方式已经介绍完了,你偏向使用哪一种呢?

来源:立功科技

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 94

简介

本文主要介绍了HPM6750的控制器局域网CAN(以下简称CAN控制器)的概述以及基于HPM-SDK CAN控制器的开发指导(包括实现CAN2.0、CAN-FD)。

CAN控制器

1. 概述

CAN 是 Controller Area Network 的缩写(以下称为 CAN),是 ISO 国际标准化的串行通信协议。HPM6750 MCU搭载了4路CAN控制器,CAN0/CAN1/CAN2/CAN3,它们具有如下特性:

● 支持 CAN 2.0B 协议,支持多达 8 字节的数据载荷, 数据速率可达 1Mbit/s;

● 支持 CAN FD 协议,支持多达 64 字节的数据载荷, 数据速率可达 2.5Mbit/s;

● 支持 1 ∼ 1/256 的波特率预分频,灵活配置波特率;

● 16 个接收缓冲器;

– FIFO 方式;

– 错误或者不被接收的数据不会覆盖存储的消息;

● 1 个高优先主发送缓冲器 PTB;

● 8 个副发送缓冲器 STB;

– FIFO 方式;

– 优先级仲裁方式;

● 16 组独立的筛选器;

– 支持 11 位标准 ID 和 29 位扩展 ID;

– 可编程 ID CODE 位以及 MASK 位;

● PTB/STB 均支持支持单次发送模式;

● 支持静默模式;

● 支持回环模式;

● 支持待机模式;

● 支持捕捉传输的错误种类以及定位仲裁失败位置;

● 可编程的错误警告值;

● 支持 ISO11898-4 规定时间触发 CAN 以及接收时间戳可配置停止位:1位,1.5位或者2位。

2. 系统框图

1.png

3. 管脚

2.png

CAN控制器功能开发指引

1. API功能描述

CAN开发主要使用以下接口:

//获取CAN默认配置
hpm_stat_t can_get_default_config(can_config_t *config);
//CAN 初始化接口
hpm_stat_t can_init(CAN_Type *base, can_config_t *config, uint32_t src_clk_freq);
//接收过滤器配置
hpm_stat_t can_set_filter(CAN_Type *base, const can_filter_config_t *config);
//CAN 数据发送接口(阻塞模式)
hpm_stat_t can_send_message_blocking(CAN_Type *base, const can_transmit_buf_t *message);
//CAN高优先级数据发送接口(PTB 阻塞模式)
hpm_stat_t can_send_high_priority_message_blocking(CAN_Type *base, const can_transmit_buf_t *message);
//CAN 数据接收接口(阻塞模式)
hpm_stat_t can_receive_message_blocking(CAN_Type *base, can_receive_buf_t *message);
//CAN 数据接收接口(非租塞模式)
hpm_stat_t can_read_received_message(CAN_Type *base, can_receive_buf_t *message);
//设置发送补偿及使能(CAN-FD高速率使用,TDC)
void can_set_transmitter_delay_compensation(CAN_Type *base, uint8_t sample_point, bool enable);

2. API数据结构

2.1 CAN配置

typedef struct {    
   union {        
      struct {           
         //当禁用use_lowlevel_timing_setting时,以下参数有效。            
         uint32_t baudrate;  //CAN 2.0波特率设定            
         uint32_t baudrate_fd; // CAN-FD波特率设定,当enable_canfd使能才有效                       
         uint16_t can20_samplepoint_min; //CAN 2.0最小采样点(0~1000)                       
         uint16_t can20_samplepoint_max; //CAN 2.0最大采样点(0~1000)            
         uint16_t canfd_samplepoint_min; //CAN-FD 最小采样点(0~1000)            
         uint16_t canfd_samplepoint_max; //CAN-FD 最大采样点(0~1000)        
      };        
      struct {
//当启用use_lowlevel_timing_setting时,以下参数有效。            
         can_bit_timing_param_t can_timing;  //CAN2.0 位时间参数            
         can_bit_timing_param_t canfd_timing; //CAN-FD 位时间参数        
      };    
   };
can_loopback_mode_t loopback_mode;    //CAN回环模式,默认是正常模式
bool use_lowlevel_timing_setting;     //是否启用位时间参数设定     
   bool enable_canfd;   //是否启用CAN-FD     
   bool enable_self_ack;   //是否启用自ACK帧
bool disable_re_transmission_for_ptb;  //是否禁用高优先级PTB发送重传, false:单发模式 true:重传模式
bool disable_re_transmission_for_stb;  //是否禁用STP发送重传, false:单发模式, true:重传模式
uint16_t filter_list_num;   //接受过滤器list总数can_filter_config_t *filter_list;  //接受过滤器list指针
} can_config_t;

2.2 CAN过滤配置

/** 
 * @brief CAN acceptance filter modes 
 */
typedef enum _can_filter_mode {    
    can_filter_mode_both_frames,                //标准格式和扩展格式过滤选模式
can_filter_mode_standard_frames,            //标准格式过滤模式
can_filter_mode_extended_frames,            //扩展格式过滤模式
} can_filter_mode_t;


/** 
 * @brief CAN acceptance configuration 
 */
typedef struct {
uint16_t index;                             //过滤器index
can_filter_mode_t mode;                 //过滤器模式     
     bool enable;                               //过滤器是否使能     
     uint32_t code;                              //ID code     
     uint32_t mask;                              //ID mask
} can_filter_config_t;

2.3 CAN发送

/** 
 * @brief CAN transmit buffer data structure 
 */
typedef union _can_tx_buf {
uint32_t buffer[18];    //发送 buffer,由于是联合体,和下面的共享一块内存区域,buffer大小:4*18=72
struct {        
        struct {            
           uint32_t id: 29;                      //CAN ID            
           uint32_t : 1;            
           uint32_t transmit_timestamp_enable: 1;  //时间戳使能        
        };        
        struct {            
           uint32_t dlc: 4;                        //数据长度            
           uint32_t bitrate_switch: 1;             //bitrate开关            
           uint32_t canfd_frame: 1;                //can-fd标识位            
           uint32_t remote_frame: 1;               //remote 标识位            
           uint32_t extend_id: 1;                  //扩展ID            
           uint32_t : 24;        
        };        
        uint8_t data[];                             //数据指针    
     };
} can_transmit_buf_t;

2.4 CAN接收

/** 
 * @brief CAN receive buffer data structure 
*/
typedef union _can_rx_buf {    
    uint32_t buffer[20];           //接收buffer,由于是联合体,和下面的数据共享一块内存区域    
    struct {        
        struct {            
            uint32_t id: 29;        //can id            
            uint32_t : 1;            
            uint32_t error_state_indicator: 1;  //错误状态指示        
        };        
        struct {            
            uint32_t dlc: 4;                   //数据长度            
            uint32_t bitrate_switch: 1;        //bitrate开关            
            uint32_t canfd_frame: 1;           //canfd 标识            
            uint32_t remote_frame: 1;          //remote标识            
            uint32_t extend_id: 1;             //扩展ID            
            uint32_t : 4;            
            uint32_t loopback_message: 1;      //回环数据标识            
            uint32_t error_type: 3;            //错误类型            
            uint32_t cycle_time: 16;           //cycle time        
        };        
        uint8_t data[];                        //数据指针    
    };
} can_receive_buf_t;

3. 配置流程

CAN控制器的CAN2.0和CAN-FD配置流程如下图。

3.png

4. 样例

4.1 内部回环样例

需求:

1.CAN-FD协议

2.波特率2.5Mbps

3.内部回环模式

4.数据载荷64字节

5.遍历can-id从0~2047(11位标准ID)

6.每帧数据确保不同

7.阻塞发送、非阻塞接收(非中断模式)

8.对比接收和发送的数据包是否相等,并输出结果

 void board_can_loopback_test(void)
 {    
     bool result;    
     uint32_t error_cnt = 0;    
     uint32_t can_src_clk_freq;    
     can_config_t can_config;    
     board_init_can(BOARD_APP_CAN_BASE);    
     can_src_clk_freq = board_init_can_clock(BOARD_APP_CAN_BASE);    
     can_config.baudrate = 1000000; /* 1Mbps */    
     can_config.baudrate_fd = 2500000; /*5Mbps*/    
     can_config.loopback_mode = can_loopback_internal; //内部回环    
     can_config.enable_canfd = true;    
     hpm_stat_t status = can_init(BOARD_APP_CAN_BASE, &can_config, can_src_clk_freq);    
     if (status != status_success) {        
          printf("CAN initialization failed, error code: %d\n", status);        
          return;    
     }    
     can_transmit_buf_t tx_buf;    
     can_receive_buf_t rx_buf;    
     memset(&tx_buf, 0, sizeof(tx_buf));    
     memset(&rx_buf, 0, sizeof(rx_buf));    
     tx_buf.dlc = can_payload_size_64;    
     tx_buf.canfd_frame = 1;    
     tx_buf.bitrate_switch = 1;    
     for (uint32_t i = 0; i < 2048; i++) {        
           tx_buf.id = i;        
           for (uint32_t j = 0; j < 64u; j++) {            
                tx_buf.data[j] = (uint8_t)i + j + 1;        
           }        
           can_send_message_blocking(BOARD_APP_CAN_BASE, &tx_buf);        
           can_read_received_message(BOARD_APP_CAN_BASE, &rx_buf);        
           result = can_buf_compare(&tx_buf, &rx_buf);        
           if (!result) {            
                error_cnt++;            
                can_set_transmitter_delay_compensation(BOARD_APP_CAN_BASE, 64, true);            
                hpm_stat_t status = can_init(BOARD_APP_CAN_BASE, &can_config, can_src_clk_freq);            
                if (status != status_success) {                
                     printf("CAN initialization failed, error code: %d\n", status);                
                     return;            
                }            
                printf("ID=%08x, result:%s\n", rx_buf.id, result ? "passed": "failed");        
           }    
      }    
      printf("    CAN loopback test for extend frame %s, error_cnt:%d\n", error_cnt == 0 ? "passed" : "failed", error_cnt);
}

4.2 两路闭环收发样例

需求:

1.CAN2.0协议

2.波特率1000000,1Mbps

3.CAN0发送,CAN1接收

4.数据载荷8字节

5.CAN0阻塞发送,CAN1阻塞接收

6.对比CAN0发送包和CAN1接收包是否相同,并输出结果

7.压测100次,输出最终结果

void can0_can1_rxrx_loop_test(void)
{    
   pm_stat_t status;    
   can_config_t can_config;    
   bool use_canfd = false;    
   can_get_default_config(&can_config);    
   can_config.baudrate = 1000000; /* 1Mbps */    
   can_config.baudrate_fd = 5000000; /* 2Mbps */    
   can_config.enable_canfd = use_canfd;    
   board_init_can(HPM_CAN0);    
   board_init_can(HPM_CAN1);   
   uint32_t can_src_clk_freq0 = board_init_can_clock(HPM_CAN0);    
   uint32_t can_src_clk_freq1 = board_init_can_clock(HPM_CAN1);    
   hpm_stat_t status0 = can_init(HPM_CAN0, &can_config, can_src_clk_freq0);    
   if (status0 != status_success) {        
      printf("CAN initialization failed, error code: %d\n", status0);        
      return;    
   }    
   hpm_stat_t status1 = can_init(HPM_CAN1, &can_config, can_src_clk_freq1);    
   if (status1 != status_success) {        
      printf("CAN initialization failed, error code: %d\n", status1);        
      return;    
   }    
   printf("CMD_STA_CMD_CTRL(0xA0)= %08x\n", HPM_CAN0->CMD_STA_CMD_CTRL);    
   printf("F_PRESC               = %08x\n", HPM_CAN0->F_PRESC);    
   printf("S_PRESC               = %08x\n", HPM_CAN0->S_PRESC);    
   printf("TDC                   = %08x\n", HPM_CAN0->TDC);    
   uint32_t error_cnt = 0;    
   bool result = false;    
   can_transmit_buf_t tx_buf;    
   can_receive_buf_t rx_buf; 
      memset(&tx_buf, 0, sizeof(tx_buf));    
   memset(&rx_buf, 0, sizeof(rx_buf));    
   tx_buf.id = 0x101;    
   uint32_t id_max;    
   if (!use_canfd) {        
        tx_buf.dlc = can_payload_size_8;        
        id_max = 8;    
   } else {        
        tx_buf.dlc = can_payload_size_8;        
        id_max = 64;        
        tx_buf.canfd_frame = 1;        
        tx_buf.bitrate_switch = 1;    
   }    
   for(int index = 0; index < 100; index++)    
   {        
       for (uint32_t i = 0; i < id_max; i++) {            
            tx_buf.data[i] = (uint8_t)(index+i);        
       }        
       can_send_high_priority_message_blocking(HPM_CAN0, &tx_buf);        
       can_receive_message_blocking(HPM_CAN1, &rx_buf);        
       result = can_buf_compare(&tx_buf, &rx_buf);        
       if (!result) {            
            error_cnt++;            
            printf("    CAN0->CAN1 for standard frame %s\n", result ? "passed" : "failed");        
       }        
       can_receive_message_blocking(HPM_CAN0, &rx_buf);        
       result = can_buf_compare(&tx_buf, &rx_buf);        
       if (!result) {            
            error_cnt++;            
            printf("    CAN1->CAN0 for standard frame %s\n", result ? "passed" : "failed");        
       }    
  }    
  printf("    CAN can0 can1 rxrx loop test for result: %s, error_cnt:%d\n", error_cnt == 0 ? "passed" : "failed", error_cnt);
}

4.3 四路收发样例

需求:

1.CAN-FD协议

2.波特率2.5Mbps

3.数据载荷64字节

4.启用中断接收

5.CAN0/CAN1/CAN2/CAN3顺序发送数据

6.确保CAN0/CAN1/CAN2/CAN3 can-id不同

7.确保每次发送的数据包内容不同

8.分别对比每次一路CAN发送数据包和其它三路CAN接收的数据包是否相同,并输出结果

9.压测100次,并输出结果

static can_info_t s_can_info[] = {        
         { .can_base = HPM_CAN0 },        
         { .can_base = HPM_CAN1 },
#if defined(HPM_CAN2)        
         { .can_base = HPM_CAN2 },
#endif
#if defined (HPM_CAN3)        
         { .can_base = HPM_CAN3 },
#endif
};
volatile static bool has_new_rcv_msg_array[4];
volatile static can_receive_buf_t s_can_rx_buf_array[4];
SDK_DECLARE_EXT_ISR_M(IRQn_CAN0, board_can_isr0);
SDK_DECLARE_EXT_ISR_M(IRQn_CAN1, board_can_isr1);
SDK_DECLARE_EXT_ISR_M(IRQn_CAN2, board_can_isr2);
SDK_DECLARE_EXT_ISR_M(IRQn_CAN3, board_can_isr3);
void board_can_isr0(void)
{    
     uint8_t flags = can_get_tx_rx_flags(HPM_CAN0);    
     if ((flags & CAN_EVENT_RECEIVE) != 0) {        
          can_read_received_message(HPM_CAN0, (can_receive_buf_t *)&s_can_rx_buf_array[0]);        
          has_new_rcv_msg_array[0] = true;    
     }    
     can_clear_tx_rx_flags(HPM_CAN0, flags);
}
void board_can_isr1(void)
{    
     uint8_t flags = can_get_tx_rx_flags(HPM_CAN1);    
     if ((flags & CAN_EVENT_RECEIVE) != 0) {        
          can_read_received_message(HPM_CAN1, (can_receive_buf_t *)&s_can_rx_buf_array[1]);        
          has_new_rcv_msg_array[1] = true;    
     }    
     can_clear_tx_rx_flags(HPM_CAN1, flags);
}
void board_can_isr2(void)
{    
     uint8_t flags = can_get_tx_rx_flags(HPM_CAN2);    
     if ((flags & CAN_EVENT_RECEIVE) != 0) {        
          can_read_received_message(HPM_CAN2, (can_receive_buf_t *)&s_can_rx_buf_array[2]);        
          has_new_rcv_msg_array[2] = true;    
     }    
          can_clear_tx_rx_flags(HPM_CAN2, flags);
}
void board_can_isr3(void)
{    
     uint8_t flags = can_get_tx_rx_flags(HPM_CAN3);    
     if ((flags & CAN_EVENT_RECEIVE) != 0) {        
          can_read_received_message(HPM_CAN3, (can_receive_buf_t *)&s_can_rx_buf_array[3]);        
          has_new_rcv_msg_array[3] = true;    
     }    
     can_clear_tx_rx_flags(HPM_CAN3, flags);
}
void board_can0_1_2_3_txrx_loop_test(void)
{    
     hpm_stat_t status;    
     can_config_t can_config;    
     bool use_canfd = true;    
     can_get_default_config(&can_config);    
     can_config.baudrate = 1000000; /* 1Mbps */    
     can_config.baudrate_fd = 2500000; /* 5Mbps */    
     can_config.enable_canfd = use_canfd;    
     /* Initialize CAN */    
     for (uint32_t i=0; i < ARRAY_SIZE(s_can_info); i++) {        
          can_info_t  *info = &s_can_info[i];        
          board_init_can(info->can_base);        
          info->clock_freq = board_init_can_clock(info->can_base);        
          status = can_init(info->can_base, &can_config,  info->clock_freq);        
          if (status != status_success) {            
               printf("CAN %d initialization failed, error code: %d\n", i, status);            
               return;        
          }        
          printf("CMD_STA_CMD_CTRL(0xA0)= %08x\n", info->can_base->CMD_STA_CMD_CTRL);        
          printf("F_PRESC               = %08x\n", info->can_base->F_PRESC);        
          printf("S_PRESC               = %08x\n", info->can_base->S_PRESC);        
          printf("TDC                   = %08x\n", info->can_base->TDC);        
          can_enable_tx_rx_irq(info->can_base, CAN_EVENT_RECEIVE);    
          }    
          intc_m_enable_irq_with_priority(IRQn_CAN0, 1);    
          intc_m_enable_irq_with_priority(IRQn_CAN1, 1);    
          intc_m_enable_irq_with_priority(IRQn_CAN2, 1);    
          intc_m_enable_irq_with_priority(IRQn_CAN3, 1);
          
    uint32_t error_cnt = 0;    
    bool result = false;    
    can_transmit_buf_t tx_buf[4];    
    uint32_t data_max;    
    memset(tx_buf, 0, sizeof(tx_buf));    
    for(int i = 0; i < 4; i ++)    
    {        
        tx_buf[i].id = i+1;        
        if (!use_canfd) {            
             tx_buf[i].dlc = can_payload_size_8;            
             data_max = 8;        
        } else {            
             tx_buf[i].canfd_frame = 1;            
             tx_buf[i].bitrate_switch = 1;            
             tx_buf[i].dlc = can_payload_size_64;            
             data_max = 64;        
        }    
    }    
    for(int index = 0; index < 100; index++)    
    {        
        for(uint32_t can_i = 0; can_i < 4; can_i++)        
        {            
            for (uint32_t i = 0; i < data_max; i++) {                
                 tx_buf[can_i].data[i] = (uint8_t)(index+can_i+i);            
             }        
        }        
        for(uint32_t can_i = 0; can_i < 4; can_i++)        
        {            
            can_send_high_priority_message_blocking(s_can_info[can_i].can_base, &tx_buf[can_i]);            
            for(int j= 1; j < 4; j++)            
            {                
                printf("recv canid:%d\n", (can_i+j)%4);                
                while(!has_new_rcv_msg_array[(can_i+j)%4])                
                {                
                }                
                has_new_rcv_msg_array[(can_i+j)%4] = false;                
                result = can_buf_compare(&tx_buf[can_i], &s_can_rx_buf_array[(can_i+j)%4]);                
                if (!result) {                    
                     error_cnt++;                
                }                
                printf("  CAN%d->CAN%d for standard frame %s\n", can_i, (can_i+j)%4, result ? "passed" : "failed");            
            }        
        }    
   }    
   printf("    CAN can0 can1 rxrx loop test for result: %s, error_cnt:%d\n", error_cnt == 0 ? "passed" : "failed", error_cnt);
}

划重点

使用HPM6750的CAN控制器,可以轻松实现4路CAN2.0/CAN-FD同时收发数据,易于实现CAN网络隔离以及网络中继的复杂需求,实现了工业网关的功能。

来源:先楫芯上人

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 37

本文内容来自先楫开发者 @Xusiwei1236,介绍了如何在HPM6750上运行边缘AI框架,感兴趣的小伙伴快点来看看。

---------------  以下为测评内容 ---------------

TFLM是什么?

你或许都听说过TensorFlow——由谷歌开发并开源的一个机器学习库,它支持模型训练和模型推理。

今天介绍的TFLM,全称是TensorFlow Lite for Microcontrollers,翻译过来就是“针对微控制器的TensorFlow Lite”。那TensorFlow Lite又是什么呢?

TensorFlow Lite(通常简称TFLite)其实是TensorFlow团队为了将模型部署到移动设备而开发的一套解决方案,通俗的说就是手机版的TensorFlow。下面是TensorFlow官网上关于TFLite的一段介绍:

“TensorFlow Lite 是一组工具,可帮助开发者在移动设备、嵌入式设备和 loT 设备上运行模型,以便实现设备端机器学习。”

而我们今天要介绍的TensorFlow Lite for Microcontrollers(TFLM)则是 TensorFlow Lite的微控制器版本。这里是官网上的一段介绍:

“ TensorFlow Lite for Microcontrollers (以下简称TFLM)是 TensorFlow Lite 的一个实验性移植版本,它适用于微控制器和其他一些仅有数千字节内存的设备。它可以直接在“裸机”上运行,不需要操作系统支持、任何标准 C/C++ 库和动态内存分配。核心运行时(core runtime)在 Cortex M3 上运行时仅需 16KB,加上足以用来运行语音关键字检测模型的操作,也只需 22KB 的空间。”

这三者一脉相承,都出自谷歌,区别是TensorFlow同时支持训练和推理,而后两者只支持推理。TFLite主要用于支持手机、平板等移动设备,TFLM则可以支持单片机。从发展历程上来说,后两者都是TensorFlow项目的“支线项目”。或者说这三者是一个树形的发展过程,具体来说,TFLite是从TensorFlow项目分裂出来的,TFLite-Micro是从TFLite分裂出来的,目前是三个并行发展的。在很长一段时间内,这三个项目的源码都在一个代码仓中维护,从源码目录的包含关系上来说,TensorFlow包含后两者,TFLite包含tflite-micro。

HPM SDK中的TFLM

  • TFLM中间件

HPM SDK中集成了TFLM中间件(类似库,但是没有单独编译为库),位于hpm_sdk\middleware子目录:

1.png

这个子目录的代码是由TFLM开源项目裁剪而来,删除了很多不需要的文件。

  • TFLM示例

HPM SDK中也提供了TFLM示例,位于hpm_sdk\samples\tflm子目录:

2.png

示例代码是从官方的persion_detection示例修改而来,添加了摄像头采集图像和LCD显示结果。

由于我手里没有配套的摄像头和显示屏,所以本篇没有以这个示例作为实验。

在HPM6750上运行TFLM基准测试

接下来以person detection benchmark为例,讲解如何在HPM6750上运行TFLM基准测试。

  • 将person detection benchmark源代码添加到HPM SDK环境

按照如下步骤,在HPM SDK环境中添加person detection benchmark源代码文件:

1、在HPM SDK的samples子目录创建tflm_person_detect_benchmark目录,并在其中创建src目录;

2、从上文描述的已经运行过person detection benchmark的tflite-micro目录中拷贝如下文件到src目录:

  • tensorflow\lite\micro\benchmarks\person_detection_benchmark.cc

  • tensorflow\lite\micro\benchmarks\micro_benchmark.h

  • tensorflow\lite\micro\examples\person_detection\model_settings.h

  • tensorflow\lite\micro\examples\person_detection\model_settings.cc

3、在src目录创建testdata子目录,并将tflite-micro目录下如下目录中的文件拷贝全部到testdata中:

  1. tensorflow\lite\micro\tools\make\gen\linux_x86_64_default\genfiles\tensorflow\lite\micro\examples\person_detection\testdata

4、修改person_detection_benchmark.cc、model_settings.cc、no_person_image_data.cc、person_image_data.cc 文件中部分#include预处理指令的文件路径(根据拷贝后的相对路径修改);

5、person_detection_benchmark.cc文件中,main函数的一开始添加一行board_init();、顶部添加一行#include "board.h”

  • 添加CMakeLists.txt和app.yaml文件

在src平级创建CMakeLists.txt文件,内容如下:

cmake_minimum_required(VERSION 3.13)

set(CONFIG_TFLM 1)

find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE})
project(tflm_person_detect_benchmark)
set(CMAKE_CXX_STANDARD 11)

sdk_app_src(src/model_settings.cc)
sdk_app_src(src/person_detection_benchmark.cc)
sdk_app_src(src/testdata/no_person_image_data.cc)
sdk_app_src(src/testdata/person_image_data.cc)
sdk_app_inc(src)sdk_ld_options("-lm")
sdk_ld_options("--std=c++11")
sdk_compile_definitions(__HPMICRO__)
sdk_compile_definitions(-DINIT_EXT_RAM_FOR_DATA=1)
# sdk_compile_options("-mabi=ilp32f")
# sdk_compile_options("-march=rv32imafc")
sdk_compile_options("-O2")
# sdk_compile_options("-O3")
set(SEGGER_LEVEL_O3 1)
generate_ses_project()

在src平级创建app.yaml文件,内容如下:

dependency:  
   - tflm
  • 编译和运行TFLM基准测试

接下来就是大家熟悉的——编译运行了。首先,使用generate_project生产项目:

3.png

接着,将HPM6750开发板连接到PC,在Embedded Studio中打卡刚刚生产的项目:

4.png

这个项目因为引入了TFLM的源码,文件较多,所以右边的源码导航窗里面的Indexing要执行很久才能结束。

然后,就可以使用F7编译、F5调试项目了:

5.png

编译完成后,先打卡串口终端连接到设备串口,波特率115200。启动调试后,直接继续运行,就可以在串口终端中看到基准测试的输出了:

============================== 
hpm6750evkmini clock summary
==============================
cpu0:            816000000Hz
cpu1:            816000000H
zaxi0:            200000000Hz
axi1:            200000000Hz
axi2:            200000000Hz
ahb:             200000000Hz
mchtmr0:         24000000Hz
mchtmr1:         1000000Hz
xpi0:            133333333Hz
xpi1:            400000000Hz
dram:            166666666Hz
display:         74250000Hz
cam0:            59400000Hz
cam1:            59400000Hz
jpeg:            200000000Hz
pdma:            200000000Hz
=============================

----------------------------------------------------------------------
$$\   $$\ $$$$$$$\  $$\      $$\ $$\
$$ |  $$ |$$  __$$\ $$$\    $$$ |\__|
$$ |  $$ |$$ |  $$ |$$$$\  $$$$ |$$\  $$$$$$$\  $$$$$$\   $$$$$$\
$$$$$$$$ |$$$$$$$  |$$\$$\$$ $$ |$$ |$$  _____|$$  __$$\ $$  __$$\
$$  __$$ |$$  ____/ $$ \$$$  $$ |$$ |$$ /      $$ |  \__|$$ /  $$ |
$$ |  $$ |$$ |      $$ |\$  /$$ |$$ |$$ |      $$ |      $$ |  $$ |
$$ |  $$ |$$ |      $$ | \_/ $$ |$$ |\$$$$$$$\ $$ |      \$$$$$$  |
\__|  \__|\__|      \__|     \__|\__| \_______|\__|       \______/
----------------------------------------------------------------------

InitializeBenchmarkRunner took 114969 ticks (4 ms).
WithPersonDataIterations(1) took 10694521 ticks (445 ms)
DEPTHWISE_CONV_2D took 275798 ticks (11 ms).
DEPTHWISE_CONV_2D took 280579 ticks (11 ms).
CONV_2D took 516051 ticks (21 ms).
DEPTHWISE_CONV_2D took 139000 ticks (5 ms).
CONV_2D took 459646 ticks (19 ms).
DEPTHWISE_CONV_2D took 274903 ticks (11 ms).
CONV_2D took 868518 ticks (36 ms).
DEPTHWISE_CONV_2D took 68180 ticks (2 ms).
CONV_2D took 434392 ticks (18 ms).
DEPTHWISE_CONV_2D took 132918 ticks (5 ms).
CONV_2D took 843014 ticks (35 ms).
DEPTHWISE_CONV_2D took 33228 ticks (1 ms).
CONV_2D took 423288 ticks (17 ms).
DEPTHWISE_CONV_2D took 62040 ticks (2 ms).
CONV_2D took 833033 ticks (34 ms).
DEPTHWISE_CONV_2D took 62198 ticks (2 ms).
CONV_2D took 834644 ticks (34 ms).
DEPTHWISE_CONV_2D took 62176 ticks (2 ms).
CONV_2D took 838212 ticks (34 ms).
DEPTHWISE_CONV_2D took 62206 ticks (2 ms).
CONV_2D took 832857 ticks (34 ms).
DEPTHWISE_CONV_2D took 62194 ticks (2 ms).
CONV_2D took 832882 ticks (34 ms).
DEPTHWISE_CONV_2D took 16050 ticks (0 ms).
CONV_2D took 438774 ticks (18 ms).
DEPTHWISE_CONV_2D took 27494 ticks (1 ms).
CONV_2D took 974362 ticks (40 ms).
AVERAGE_POOL_2D took 2323 ticks (0 ms).
CONV_2D took 1128 ticks (0 ms).
RESHAPE took 184 ticks (0 ms).
SOFTMAX took 2249 ticks (0 ms).

NoPersonDataIterations(1) took 10694160 ticks (445 ms)
DEPTHWISE_CONV_2D took 274922 ticks (11 ms).
DEPTHWISE_CONV_2D took 281095 ticks (11 ms).
CONV_2D took 515380 ticks (21 ms).
DEPTHWISE_CONV_2D took 139428 ticks (5 ms).
CONV_2D took 460039 ticks (19 ms).
DEPTHWISE_CONV_2D took 275255 ticks (11 ms).
CONV_2D took 868787 ticks (36 ms).
DEPTHWISE_CONV_2D took 68384 ticks (2 ms).
CONV_2D took 434537 ticks (18 ms).
DEPTHWISE_CONV_2D took 133071 ticks (5 ms).
CONV_2D took 843202 ticks (35 ms).
DEPTHWISE_CONV_2D took 33291 ticks (1 ms).
CONV_2D took 423388 ticks (17 ms).
DEPTHWISE_CONV_2D took 62190 ticks (2 ms).
CONV_2D took 832978 ticks (34 ms).
DEPTHWISE_CONV_2D took 62205 ticks (2 ms).
CONV_2D took 834636 ticks (34 ms).
DEPTHWISE_CONV_2D took 62213 ticks (2 ms).
CONV_2D took 838212 ticks (34 ms).
DEPTHWISE_CONV_2D took 62239 ticks (2 ms).
CONV_2D took 832850 ticks (34 ms).
DEPTHWISE_CONV_2D took 62217 ticks (2 ms).
CONV_2D took 832856 ticks (34 ms).
DEPTHWISE_CONV_2D took 16040 ticks (0 ms).
CONV_2D took 438779 ticks (18 ms).
DEPTHWISE_CONV_2D took 27481 ticks (1 ms).
CONV_2D took 974354 ticks (40 ms).
AVERAGE_POOL_2D took 1812 ticks (0 ms).
CONV_2D took 1077 ticks (0 ms).
RESHAPE took 341 ticks (0 ms).
SOFTMAX took 901 ticks (0 ms).

WithPersonDataIterations(10) took 106960312 ticks (4456 ms)

NoPersonDataIterations(10) took 106964554 ticks (4456 ms)

可以看到,在HPM6750EVKMINI开发板上,连续运行10次人像检测模型,总体耗时4456毫秒,每次平均耗时445.6毫秒。

在树莓派3B+上运行TFLM基准测试

  • 在树莓派上运行TFLM基准测试

树莓派3B+上可以和PC上类似,直接运行PC端的测试命令,得到基准测试结果:

6.png

可以看到,在树莓派3B+上的,对于有人脸的图片,连续运行10次人脸检测模型,总体耗时4186毫秒,每次平均耗时418.6毫秒;对于无人脸的图片,连续运行10次人脸检测模型,耗时4190毫秒,每次平均耗时419毫秒。

  • HPM6750和树莓派3B+、AMD R7 4800H上的基准测试结果对比

这里将HPM6750EVKMINI开发板、树莓派3B+和AMD R7 4800H上运行人脸检测模型的平均耗时结果汇总如下:

7.png

可以看到,在TFLM人脸检测模型计算场景下,HPM6750EVKMINI和树莓派3B+成绩相当。虽然HPM6750的816MHz CPU频率比树莓派3B+搭载的BCM2837 Cortex-A53 1.4GHz的主频低,但是在单核心计算能力上没有相差太多。

这里树莓派3B+上的TFLM基准测试程序是运行在64位Debian Linux发行版上的,而HPM6750上的测试程序是直接运行在裸机上的。由于操作系统内核中任务调度器的存在,会对CPU的计算能力带来一定损耗。所以,这里进行的并不是一个严格意义上的对比测试,测试结果仅供参考。

(本文参考链接:http://m.eeworld.com.cn/bbs_thread-1208270-1-1.html

直接转载来源:先楫芯上人

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 119

本期内容由先楫开发者社区大咖@xusiwei1236分享基于先楫HPM6750的轻量级AI推理框架,赶紧来瞧瞧~

一、TinyMaix是什么?

TinyMaix是国内sipeed团队开发一个轻量级AI推理框架,官方介绍如下:

TinyMaix 是面向单片机的超轻量级的神经网络推理库,即 TinyML 推理库,可以让你在任意单片机上运行轻量级深度学习模型。

甚至在 Arduino ATmega328 (32KB Flash, 2KB RAM) 上都能基于 TinyMaix 进行手写数字识别。

TinyMaix官网提供了详细介绍,可以在本文末尾的参考链接中找到链接。

二、TinyMaix移植

本节介绍如何将TinyMaix移植到HPM6750,详细步骤如下:

2.1 开发环境搭建

先楫官方支持SDK开发环境和RT-Thread开发环境,两种开发环境的搭建方法均可在官方提供的开发板用户手册(HPM6750EVKMINI USER GUIDE.pdf 或 HPM6750EVK USER GUIDE.pdf 文件)中找到,也可以参考我此前发布的帖子,具体见本文最后的参考链接。

考虑到TinyMaix对于现已支持的MCU,基准测试都是基于裸机进行的,因此这里使用的是HPM SDK开发环境。另外,基于裸机的移植在RTOS环境下一般也可以运行。因此,对于MCU芯片的计算类开源项目的移植(例如这里的TinyMaix),最好是基于裸机进行。

使用的HPM SDK版本为0.14.0,使用的SEGGER Embedded Studio版本信息为:

SEGGER Embedded Studio for RISC-VRelease 6.40 Build 2022102501.51567Windows x64© 2014-2022 SEGGER Microcontroller GmbH© 1997-2022 Rowley Associates Ltd.segger-cc: version 15.0.0segger-ld: version 4.36.0segger-rtl: version 4.20.0GCC/BINUTILS: built using the GNU RISC-V Toolchain version GCC 12.20/Binutils 2.39 source distributionClang/LLVM: built using the version 15.0.0 source distribution

2.2 TinyMaix移植步骤

由于TinyMaix本身的源代码文件不多,整个移植过程相对还是比较简单的。

整体基本上分为三步:

  • 目录规划;
  • 修改源码;
  • 编译运行;

下面介绍具体操作步骤。

2.2.1 目录规划

考虑到TinyMaix和hpm_sdk都是使用CMake构建的,为了不对TinyMaix进行过多侵入性修改,这里采取的策略是——添加一个中间层。具体是将HPM6750平台的CMakeLists.txt文件放在TinyMaix源码目录的上一层,如下所示:
hpm_sdk/app/├── CMakeLists.txt     # HPM6750平台的CMakeLists.txt├── src│   └── benchmark.c└── TinyMaix/          # TinyMaix源码目录

2.2.2 修改源码

这里在src/benchmark.c文件内容如下:

#include <stdio.h>
#include "board.h"
#define MODEL_MNIST 1
#define MODEL_CIFAR10 2
#define MODEL_VWW 3
#define MODEL_MBNET 4
#define CONFIG_MODEL MODEL_CIFAR10 // 修改这一行切换 测试程序

#define main benchmark_main
#if (CONFIG_MODEL == MODEL_MNIST)
#include "mnist/main.c"
#elif (CONFIG_MODEL == MODEL_CIFAR10)
#include "cifar10/main.c"
#elif (CONFIG_MODEL == MODEL_VWW)
#include "vww/main.c"
#elif (CONFIG_MODEL == MODEL_MBNET)
#include "mbnet/label.c"#include "mbnet/main.c"
#endif
#undef main

int main(void)
{
    board_init();
 printf("benchmark start...\n");
     benchmark_main(0, NULL);    
     __asm__("wfi");
return 0;
}

为了不直接拷贝基准测试代码,简化代码结构,这里使用了不太常见的:

直接#include 某个.c文件;

在#include "xxx/main.c"前面,定义宏#define main benchmark_main,之后取消宏定义;

这样实现了将TinyMaix原有的测试代码作为benchmark.c一部分,而又不与这里的main函数相冲突的目的。

PS:这里为了简便,并没有把TinyMaix放到hpm_sdk的middleware目录,实际项目中使用的话最好将TinyMaix放到middleware目录。

另外,还需要修改`tm_port.h文件:

diff --git a/include/tm_port.h b/include/tm_port.h
index 357fc6b..5d1768c 100644
--- a/include/tm_port.h+++ b/include/tm_port.h
@@ -31,7 +31,7 @@ limitations under the License.
#define TM_OPT_LEVEL    TM_OPT0
#define TM_MDL_TYPE     TM_MDL_INT8
#define TM_FASTSCALE    (0)         //enable if your chip don't have FPU, may speed up 1/3, but decrease accuracy
-#define TM_LOCAL_MATH   (0)         //use local math func (like exp()) to avoid libm
+#define TM_LOCAL_MATH   (1)         //use local math func (like exp()) to avoid libm
#define TM_ENABLE_STAT  (1)         //enable mdl stat functions
#define TM_MAX_CSIZE    (1000)      //max channel num //used if INT8 mdl  //cost TM_MAX_CSIZE*4 Byte
#define TM_MAX_KSIZE    (5*5)       //max kernel_size   //cost TM_MAX_KSIZE*4 Byte
@@ -49,9 +49,10 @@ limitations under the License.
#define TM_DBGL()      TM_PRINTF("###L%d\n",__LINE__);

/******************************* DBG TIME CONFIG  ************************************/
-#include <sys/time.h>
-#include <time.h>
-#define  TM_GET_US()       ((uint32_t)((uint64_t)clock()*1000000/CLOCKS_PER_SEC))+
#include "board.h"
+#define  TM_GET_US()       (uint32_t)(HPM_MCHTMR->MTIME * 1000000uLL / clock_get_frequency(clock_mchtmr0))

#define TM_DBGT_INIT()     uint32_t _start,_finish;float _time;_start=TM_GET_US(); 
#define TM_DBGT_START()    _start=TM_GET_US();

2.2.3 编译运行

HPM6750项目的生成命令:

generate_project -b hpm6750evkmini -t flash_xip -f

HPM6750项目的编译、运行,具体可以开发环境搭建文章,链接见本文末尾。

手写数字识别(mnist模型),运行后,串口输出结果如下:

1.png

三、基准测试

下面是TinyMaix四种常用的基准测试模型的基准测试,四个模型分别为:

  • mnist——手写数字识别模型,输入28x28x1
  • cifar——10分类模型,输入32x32x3
  • vww——人体检测二分类模型,输入96x96x3,输出有无人
  • mbnet——1000分类模型,输入128x128x3

3.1 场景1: TM_MDL_INT8 + TM_OPT0

2.png

3.2 场景2: TM_MDL_INT8 + TM_OPT1
3.png
3.3 场景3: TM_MDL_FP32 + TM_OPT0

4.png

3.4 注意事项

  • 在SEGGER Embedded Studio中, 可以通过如下菜单Project 'xxx' Options -> Code -> Code Generation -> Optimization Level修改优化等级;
  • 在SEGGER Embedded Studio中, 默认的堆大小设置为16384 字节(16KB),不够运行vww96 和 mbnet128 模型,你可以通过菜单 Code -> Runtime Memory Area -> Heap Size修改具体配置大小,例如可以为524288(512KB);
  • 对于FP32模型,需要将RISC-V ISA设置从默认的rv32imac改为rv32gc(Code -> Code Generation -> RIS-V ISA),确保编译器可以生成浮点数操作指令。

四、代码仓

移植代码仓(包含全部修改):https://github.com/xusiwei/HPM6750_TinyMaix

benchmark代码仓(包含基准测试全部代码,包括CMakeLists.txt):https://github.com/xusiwei/HPM6750_TinyMaix_Benchmark

五、参考链接

  1. TinyMaix :超轻量级推理框架 - Sipeed Wiki

    https://wiki.sipeed.com/news/others/tinymaix_cnx/tinymaix_cnx.html

  2. 【先楫HPM6750系列】HPM SDK开发环境搭建和Hello World

    https://blog.csdn.net/xusiwei1236/article/details/124853859

  3. GCC floating-point options - SEGGER Wiki

    https://wiki.segger.com/GCC_floating-point_options

  4. Embedded Studio Library Heap - SEGGER Wiki

    https://wiki.segger.com/Embedded_Studio_Library_Heap

来源:先楫芯上人

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 45

LED大屏显示系统利用发光二极管构成的点阵模块或像素单元组成大面积显示屏显示字符、图像等信息,具有低功耗、低成本、高亮度、长寿命、宽视角等优点,近年来随着LED显示技术的不断更新,广泛应用在各类媒体场合。

LED大屏由无数个LED RGB灯组成,通过实时控制每个LED RGB的亮度及色彩,来显示不同的图案。在LED大屏显示应用中,传统的核心控制芯片选择可谓多种多样,MCU、FPGA或PLD都可以被作为核心控制芯片来进行设计,实现的效果也是各有优劣。

在LED大屏显示应用不断拓展的同时,大屏显示系统对刷新频率和延迟也提出了更严苛的要求:

  • 更高的刷新频率:刷新频率越高,显示的图像越稳定,眼睛感觉到疲劳可能性就越小;

  • 更低的延迟:整个数据链路的延迟进一步降低,减少闪屏或卡顿。

为确保LED大屏高刷新频率和低延迟,避免闪屏或卡顿的情况,就必须通过高速的链路来实时控制这些LED RGB灯。要实现更高刷新频率、更低延迟的LED大屏系统系统设计,主控芯片的选择非常关键。以MCU为核心的LED大屏显示控制系统常常被诟病数据处理速度无法满足实时性要求很高的场合,其实不然。

RISC-V内核的MCU,得益于RISC-V本身的简洁性和模块化设计,CPU能以更高的频率运行,带来更高的性能。因此,基于RISC-V的高主频MCU能让LED大屏显示系统实现更高的驱动频率及更高的实时性。

HPM6750就是上海先楫半导体公司开发的采用RISC-V 内核、具有高主频及创新总线架构的双核高性能MCU。HPM6750拥有两个完全独立的CPU且主频高达816MHz,模拟外设包括4组共32路精度达2.5ns的PWM、3个12位高速ADC以及1个16位高精度ADC,其性能在市场同类产品中居于领先水平。

不仅如此,对于LED大屏显示应用,高性能HPM6750能通过双千兆以太网透传的方案加双核加持完美解决高速的链路设计。HPM6750支持2个以太网控制器ENET,支持标准以太网MAC,支持10/100/1000Mbps数据传输速率,支持RMII和RGMII模式的PHY,ENET以太网控制器自带DMA,因此在数据接收和发送上CPU负载为0。HPM6750通过以太网控制器,将两个PHY收到的以太网数据相互透传,透传速率达780Mb/s。基于高性能HPM6750的LED大屏显示方案在对实时控制要求极高的应用中丝毫不逊色于FPGA方案,不仅成本更低而且进一步降低了开发难度。

< 解决方案分享>

先楫高性能MCU 

LED大屏显示双千兆以太网透传应用

案例为720P 1280x720分辨率的LED大屏显示控制,也就是至少需要实时控制1280x720个LED RGB灯,调整亮度及色彩来显示不同的图案。为确保LED大屏高刷新率及低延迟,避免闪屏或卡顿的情况,就必须通过高速的链路来实时控制这1280x720个LED RGB灯。本案例使用两个RTL8211 RGMII模式的千兆PHY。

通信框图如下:

1.png

LED RGB灯需要通过特定的波形驱动,案例由UCS1903 LED驱动控制器驱动。UCS1903时序波形图如下:

2.png

3.png

整体的应用方案使用高精定时器GPTMR+DMA+GPIO的方案来驱动LED。

DMA:

HPM6750支持2个DMA控制器:XDMA和HDMA,可以实现实时的外设寄存器和内存及内存之间的数据搬移。

支持DMA请求路由器(DMAMUX)。将来自各个外设模块的DMA请求分配到16个输出信号,作为XDMA和HDMA的DMA传输请求源。

DMAMUX 的输出0-7连接到外设总线DMA控制器HDMA,DMAMUX的输出8-15连接到系统总线DMA控制器XDMA。

用户可以通过配置DMAMUX寄存器,把来自特定外设的DMA请求,连接到XDMA或者HDMA的各个通道。

DMA控制器支持8对请求-响应的握手信号,请求信号来自功能模块并经由DMAMUX路由至DMA控制器,DMA控制器根据配置使用某个通道执行改请求,完成长度为SrcBurstSize的数据传输后发送响应信号给DMAMUX,完成硬件握手。

GPTMR:

HPM6750支持11个通用定时器,每个通用定时器支持4个通道,每个通道支持32位计数器,重载寄存器和一个输入捕获/输出比较通道,支持通用计时,输入捕获,输出比较,PWM 生成,以及产生中断和DMA请求。

其中:

  • 8 个定时器位于系统电源域称为通用定时器 GPTMR0∼7

  • 2 个定时器位于系统电源域的 CONN 子系统,称为网络定时器 NTMR0, NTMR1

  • 1 个定时器位于电源管理域,称为电源管理域定时器 PTMR。 

GPIO:

HPM6750支持2个GPIO控制器和

2个快速GPIO控制器。快速GPIO控制器IO翻转速率可达到主频的一半400Mhz。GPIO控制器IO翻转速率在10Mhz左右。

快速GPIO控制器FGPIO是CPU私有,外设无法直接访问。

本案例中波形频率是800khz,使用GPIO控制器速率完全足够。

< 应用细节 >

为获得高刷新帧率,结合HPM6750双核加持,一个核专门用来触发ENET DMA 做网络数据透传,另一个核用来解析、转换、触发HDMA,HDMA结合GPTMR拉取波形。故网络透传、数据转换处理、拉取波形三者并发执行。

案例中使用HDMA和GPTMR硬件握手来驱动GPIO高低生成指定的波形,CPU完全不参与,CPU的工作仅仅是解析网络数据包和触发DMA,故CPU在波形产生上,负载为0。因为使用了HDMA访问,因此要强调的是HDMA访问地址必须连续,需要使用的PIN管脚必须是同一组管脚,如:PA0~31/PB0~31/PC0~31/PD0~31等。

LED控制器UCS1903波形周期为800Khz,即一个周期1250ns。一个周期分为5段,即GPTMR定时器的频率为4x800Khz=4Mhz,每段1250/5=250ns(频率及分段情况,根据实际项目调整)。1码前4高后1低(高电平时长:250x4=1000ns);0码前2高后2低(高电平时长:250x2=500ns)。每路灯驱动点亮延时(每路第一个灯珠)在4ns左右,且完全的并发。

4.png

本方案最大支持32路,以32路计算占用RAM,4(32路32bit,4byte)×5(每个周期分为5段)×24(每个灯占用24个周期)×720(每路720个灯) 为338k,。为提升执行速率,源数据建议放到DLM或SRAM中,SDRAM速率相对较慢,SRAM读写时间短,静态功耗更低,总线利用率高,更适合性能要求较高的应用。

5.png

LED大屏幕刷新频率(帧率)则和LED大屏分辨率以及LED灯组合方式相关。本案例为1280x720的分辨率,每路串联720个灯驱动需耗时21.6ms(该耗时为UCS1903物理特性,和MCU性能无关),需要1280/32=40个MCU通过千兆串联。

一帧图像的数据大小为1280x720x3(RGB三种颜色值)=2700KByte=21.1Mbit,HPM6750千兆透传速率780Mb/s, 所以一帧图片传输耗时27.05ms。

每帧数据解析后将32(每个32路)x720x3图像数据转换成指定格式并触发HDMA,实测耗时26.03ms。以上拉波形耗时21.6ms、网络传输耗时27.05ms、数据转换处理耗时26.03ms均为并行执行,故1280x720分辨率的刷新一帧耗时为27.05ms,刷新帧率36.97帧/s fps。

双千兆以太网透传速率实测可达780Mb/s,通过上面计算可以看到,HPM6750双核加持下的高速链路完全能满足实时控制需求,确保了LED大屏显示的高刷新率及低延迟。

6.png

高性能驱动

HPM6750芯片介绍

HPM6750采用双RISC-V内核,主频达816MHz,凭借先楫半导体的创新总线架构、高效的L1缓存和本地存储器,创下了高达9220CoreMark和高达4651 DMIPS 的MCU性能新纪录。与ARM Cortex-M家族中性能领先的M7相比,HPM6750的 Coremark/MHz 跑分高出约10%。

除了高算力RISC-V CPU,HPM6700系列产品还创造性地整合了一系列高性能外设,包括支持2D图形加速的显示系统、高速USB、千兆以太网、CAN FD等通讯接口,高速12位和高精度16位模数转换器,面向高性能电机控制和数字电源的运动控制系统。

来源:先楫半导体HPMicro

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 69

本文导读

ADC的主要参数指标分为静态参数和动态参数两类,基于这两类指标,本文将对先楫半导体HPM6750 MCU片内16位ADC的精度进行全面测试,一起看看结果怎么样。

ADC参数测试原理

1.1 ADC参数

国内16位SAR型ADC芯片目前较少,MCU内置16位SAR型ADC的则更少。如何衡量ADC的性能,参考S*公司带有16位ADC的MCU芯片S**32H750手册,分为静态参数、动态参数两部分,如表 1.1、表 1.2所示。

静态参数主要有差分非线性(DNL)、积分非线性(INL),衡量ADC测量直流及低频信号时的性能。

表 1.1 ADC的静态参数(S**32H750)

1.png

动态参数主要有有效位数(ENOB)、信噪失真比(SINAD)、信噪比(SNR)、总谐波失真(THD),衡量ADC测量交流动态信号时的性能,例如测试1KHz正弦波。

表 1.2 ADC的动态参数(S**32H750)

2.png

1.2 IEEE1241标准

IEEE1241对ADC器件的指标参数和测试方法进行定义,是一种为ADC器件厂家制定的标准。ADI和TI等厂家的独立ADC芯片、MCU厂家的片内ADC,均遵循该标准的方法,进行静态参数和动态参数的测试。

IEEE1241于2000年发布,较新的版本为IEEE1241-2010。ADC测试评估的主要任务是确定其电压传输关系,理想情况下输入电压与ADC输出代码中点的传输关系是一条直线,每个输出代码的宽度相同。实际的电压传输关系不同于理想情况,IEEE1241标准给出了几种可选的测试步骤和方法。一种方法是使用斜坡电压信号,通过复杂的伺服环路系统,使用比被测ADC分辨率高得多的DAC,精确步进,得到被测ADC各个LSB的实际跳变电压。

另一种方法是使用正弦波信号,该信号必须具有比被测ADC预期信噪失真比(SINAD)至少高20 dB的总谐波失真和噪声。例如,一个理想16位ADC具有98dB的信噪比(SNR),假设没有失真,那么SINAD就为98dB。要想对该ADC进行测试,要求使用一个-118dB以上THD+N的正弦波信号。该低失真正弦信号,可以通过多阶带通滤波器实现,硬件相对简单。因此使用正弦波信号,是目前主要的ADC测试方法。

EEE1241标准的各章节中,给出了基于正弦波信号和统计直方图,实现静态参数DNL、INL,动态参数ENOB、SINAD、SNR、THD的测试方法和计算公式。

【6.4节】给出ADC电压传输关系的测试方法。使用一个幅度稍微超过ADC测量范围的纯正正弦波,输入到ADC,获取多个连续的采样数据并统计直方图,由公式(27)计算电压传输关系。

3.png

其中,T[k]是第k个二进制代码对应的电压值,Hc[k-1]是累计直方图,S是总采样点数。该节还给出了正弦波频率和ADC采样频率的选择,每次采集的数据点数,总的采集数据点数,正弦波幅度过载量要求。

【7.4.1节】给出增益误差G、失调电压Vos的测试方法和计算公式,基于对T[k]的最小二乘法拟合。

【8.2节】给出积分非线性INL的测试方法。测量值T[k]校正增益和失调误差之后,与理想值Tnom[k]相减,它们的差值代入公式(40),计算输出码k处的积分非线性ε[k]。

4.png

由公式(40)将LSB单位的INL,换算成百分比形式。

5.png

【8.4节】给出差分非线性DNL的测试方法,由公式(43)计算。当DNL[k]<0.9时,输出码k被定义为缺失码(missing code)。

6.png

【8.8节】给出总谐波失真THD的测试方法。ADC 对周期信号进行采样时,动态误差和积分非线性都会导致谐波失真,总谐波失真用于量化此类影响。总谐波是指一组目标谐波分量的均方根值(二次、三次等)与所施加信号均方根值的比值,由公式(50)计算。

7.png

该节给出了目标谐波的次数要求,由输入正弦波的最低9个谐波组成,包括第2次到第10次。用于计算的采样数据中,应该包含整数个输入正弦波周期,以最小化频谱泄漏,例如10个整周期。

【9.2~9.4节】给出SINAD、SNR、ENOB的测试方法。将指定频率和幅度的纯正正弦波输入到ADC,首选幅度接近满量程的大信号,但是不能出现削波(例如95%FS信号)。首先计算噪声和失真NAD,通过计算测量数据波形的DFT频谱,从频谱中删除直流和测试频率处的分量后,所有剩余傅立叶分量的和方根是NAD,由公式(67)计算。

8.png

通过将NAD和Arms代入公式(66),计算SINAD。

9.png

通过将NAD、Arm、THD代入公式(69)和(68),计算SNR。

10.png

11.png

通过将NAD代入公式(70),计算ENOB。其中εQ是理想的量化误差rms值,等于LSB/√12。

12.png


根据IEEE1241标准的以上计算过程和公式,编写科学计算软件代码,可以实现各参数的测量。

测试环境搭建

2.1 硬件框图

ADC测试的硬件由正弦波信号源、HPM6750测试板、USB转TTL线、U盘等组成,如图 2.1所示。正弦波信号源通过SMA连接线,连到HPM6750测试板。

13.png

图 2.1 测试环境硬件框图

静态参数测试时,需要大量采样数据,采样数据先存储在HPM6750测试板的内存中,之后多份采样数据存储到U盘中。动态参数测试时,需要的采样数据较少,采集完成之后直接通过HPM6750的UART打印,由TTL转USB线传输到电脑进行计算。

电脑上需要的软件工具如表 2.1所示。

表 2.1 测试所需的软件工具

14.png

2.2 正弦波信号源

用于测试的正弦波信号需要具有比被测ADC预期信噪失真比(SINAD)高20 dB左右的总谐波失真和噪声。一个理想16位ADC具有98dB的信噪比(SNR),如果没有失真,SINAD为98dB。因此,需要一个-118dB以上THD+N的正弦波信号对16位ADC进行测试。

本测试使用TI 的PSIEVM精密信号注入器,板上有8阶带通滤波器生成低失真正弦波,THD参数为-123dB,以符合测试要求。PSIEVM板如图 2.2所示,它的正弦波输出频率固定为2KHz,输出幅度和直流偏移电压可调,配套有PC端的GUI界面进行设置。

15.jpg

图 2.2 正弦波信号源PSIEVM板

需要注意PSIEVM板的输出阻抗,需要手工改成50Ω。

2.3 外围电路要求

HPM6750片内ADC的外围电路设计,对保证ADC的信噪比,至关重要。SAR型ADC可以等效理解为一个多输入端口的比较器,模拟电源AVDD、基准输入VREFH、接地平面、输入通道上的噪声直接影响ADC输出代码的跳动。

本测试使用专用的HPM6750测试板,如图 2.3。

16.jpg

图 2.3 HPM6750测试板

HPM6750测试板的ADC外围电路处理方式如下文所述,硬件设计时建议参考处理。

【模拟电源AVDD】通过LDO从数字电源5V获得低噪声模拟电源。需要注意LDO的电源抑制比在10kHz及以上频率时下降,导致高频纹波和尖峰噪声仍可以传导至LDO输出。建议在LDO之前加入10Ω左右电阻和磁珠与输入端10uF左右电容,形成低通滤波,滤除高频纹波与尖峰噪声。

【基准VREFH】基准电路设计包括两部分:电容选取、基准噪声。VREFH管脚位置的大电容是片内SAR型ADC的一部分,此类ADC基于开关电容电荷重新分配原理,在确定输出代码LSB过程中,需要从VREFH管脚获得瞬态电荷。例如,使用了两个10uF的X5R材质低ESR陶瓷电容和104电容并联,并且在PCB布局时以尽量短的走线和覆铜连接到VREFH管脚,电容的接地焊盘需要就进放置多个过孔至PCB接地平面,以降低连接阻抗。

基准的噪声需要选择低噪声基准。例如,使用了低成本的AZ432搭建3.1V基准,低频噪声10uVpp,典型温漂20ppm。对温漂有更高要求时,可以选用TPR3525,低频噪声50uVpp,典型温漂10ppm。

【接地平面】AGND和VREHL管脚需要就近放置过孔,连接到接地平面。PCB布局时需要把模拟器件、数字器件分区域放置,引导数字信号的开关电流不流经模拟电路的低平面,以避免串入数字开关噪声。详细地平面设计说明参考资料[6]。

【输入通道】需要注意,本测试中输入通道不能有普通电容,普通电容的容量随输入电压变化,使得低通截止频率变化,会引入明显失真。正常使用时,输入通道需要限制信号带宽,例如加入RC低通滤波,限制宽带噪声。

使用以上处理,HPM6750测试板的测试数据详见5.1节的表5.1。

静态参数测试

3.1 测试条件

使用正弦波输入信号,基于概率密度原理和累计直方图测试DNL、INL。当输入信号是理想正弦波时,ADC以固定频率采集,所输出数字代码的出现概率,理论上为固定值,如图 3.1所示。出现概率通过某一数字代码的出现次数,除以总采样点数计算。各个输出数字代码的测试出现概率,与理想出现概率之间的差值,是这个代码的宽度误差。统计最大宽度误差,得到差分非线性DNL。得到DNL之后,DNL的累计误差是积分非线性INL。

17.png

图 3.1 正弦波输入时的ADC输出代码直方图

图 3.1的直方图高度非线性,不能直接计算,通过累计直方图实现积分计算,可以实现直方图线性化(参考资料[3])。IEEE1241标准6.4、7.4、8.2、8.4节,给出了以上基于正弦波信号和概率密度直方图方法的计算过程、公式、及测试条件。

结合IEEE1241中6.4节要求,本测试实际使用测试条件设置如图 3.1所示。

表 3.1 静态参数测试条件

18.png

【输入信号幅度】IEEE1241的6.4节描述,输入信号幅度需要轻微超出ADC测量范围,过载量根据输入噪声而定。因为在输入正弦波的波峰、波谷位置,ADC两个临近输出代码对应的输入电压差小,容易受噪声影响。本测试中,选用10%过载量,根据ADC输入范围0~3.1V,PSIEVM输出正弦波幅度设置为-0.3~3.4V,offset设置为1.55V。

【ADC采样速率】IEEE1241的6.4.1节描述,采样速率和输入信号频率必须互为质数,实现均匀遍历到所有的ADC输出代码。本测试中,输入正弦波频率2KHz,采样速率664Ksps,每个周期获得332个采样点,具有332个不同输出代码,通过小数位频率和大量采样点,实现均匀遍历所有的输出代码。

【采样点数】IEEE1241的6.4.1节描述,每一次采集的连续采样点数,包含整数个输入信号周期。这样保证每次的采样点在0~2π的相位上均匀分布。因为会使得多次采集的数据进行拼接时,输出代码的出现概率均匀分布。本测试中,每次采样点数约120K。

IEEE1241的6.4.3节描述,根据概率密度进行测试时,样本数量与测试精度、置信度的计算公式。本测试中使用大约30M采样点进行计算,存储在U盘中。

3.2 测试步骤

静态参数测试时,单次的连续采样数据,MCU先存储在HPM6750测试板的内存中,然后顺序存到U盘中,将多份采样数据拼接成几十兆采样点的数据记录文件,用于参数计算。测试的步骤如下。

(1) PC上位机设定正弦波信号源PSIEVM的频率频率、幅度、失调,使能输出;

(2) Segger Embedded Studio环境中启动ADC采集数据,并存储到U盘;

(3) U盘中的数据记录文件复制到电脑,数据文件的路径写入科学计算软件;

(4) 运行科学计算软件代码计算DNL、INL;

(5) 查看科学计算软件输出的图表和数据。

3.3 测试数据

本测试中从U盘读取的33M采样数据文件大小为128MB,导入科学计算软件获得的输出代码直方图如图 3.2所示,其中横轴X为ADC的输出代码值,纵轴Y为该代码的出现次数,以对数坐标显示。可以看到图 3.2包含了0~65535个输出代码,符合16位ADC的输出代码个数。

19.png

图 3.2 采样数据直方图

3.3.1 DNL

采样数据通过科学计算软件计算得到的DNL,如图 3.3所示,DNL最大值为+1.1~-0.92LSB。

20.png

图 3.3 DNL测试数据

3.3.2 INL

采样数据通过科学计算软件计算得到的INL,如图 3.4所示,INL最大值为+4~-4.2LSB。

21.png

图 3.4 INL测试数据

3.3.3 小结

根据以上测试数据,HPM6750片内16位ADC测得DNL为+1.1/-0.92LSB,INL为+4/-4.2LSB。

动态参数测试

4.1 测试条件

使用正弦波输入信号,基于FFT频谱分析,从频谱成分计算出SINAD、ENOB、SNR、THD参数。将噪声和谐波成分等效到ADC输入端,根据理想ADC的信噪比公式,可以得到有效位数ENOB。IEEE1241的9.2~9.4节,描述基于FFT方法的ENOB等参数计算过程、公式、及测试条件。

22.png

结合IEEE1241要求,本测试实际使用测试条件设置如表 1.1所示。

表 4.1 动态参数测试条件

23.png

【输入信号幅度】IEEE1241的9.2.3节描述,输入信号幅度接近ADC的满量程,但是不能出现削波。因为信噪比SNR和失真THD直接和输入信号幅值相关,但是幅度过大,接近削波时,将出现明显失真。本测试中选用93%FS,根据ADC输入范围0~3.1V,PSIEVM输出正弦波幅度设置为0.0775~3.0225V,offset设置为1.55V。

【采样速率】IEEE1241的9.3节描述,可选相干采样,或非相干采样加窗。本测试中选用后者,为了衡量ADC性能,采样速率选用最高值2MSPS。

【采样点数】IEEE1241的9.4.3节描述,采样点数增加时,随机噪声对正弦波测试结果的影响降低,可重复性更好。采样点数不应过多,以免正弦波信号源或ADC时钟信号中的频率漂移或相位噪声影响结果。本测试中选用20个整周波采样点数,即20K samples。

4.2 测试步骤

动态参数测试时,采样数据需要较少,ADC采集20个输入信号周期的连续数据,采集完成之后通过HPM6750的UART打印,通过TTL转USB线传输到电脑进行计算。

(1) PC上位机设定正弦波信号源PSIEVM的频率频率、幅度、失调,使能输出;

(2) Segger Embedded Studio环境中启动ADC采集数据,并通过UART打印;

(3) 采样数据文件的路径写入科学计算软件;

(4) 运行科学计算软件代码计算ENOB等参数;

(5) 查看科学计算软件输出的图表和数据。

4.3 测试数据

本测试中从UART打印20K点数据,导入科学计算软件看到的原始数据波形如图 4.1所示,可以看到波形幅值接近满量程。

24.png

图 4.1 动态参数测试数据的原始波形

4.4 ENOB、SINAD、SNR、THD

通过科学计算软件计算输出的频谱如图 4.2,蓝色是输入信号,红色是谐波,黑色是噪声。

25.png

图 4.2 动态参数测试数据的频谱

通过科学计算软件计算输出的动态参数值如表 4.2。需要注意动态参数测试与噪声相关,容易受干扰,需参考2.3节的描述仔细设计ADC外围电路。

表 4.2 动态参数测试数据

26.png

HPM6750片内16位ADC的采样速率最高可以设置至4MSPS,这种情况下的动态参数测试值如表 4.3。可以看到,ENOB等参数有一定幅度下降,需要更高采样速率而不是更高精度时,可以选择使用该设置。

表 4.3 动态参数测试数据(4MSPS)

27.png

4.5 小结

根据以上测试数据,HPM6750片内16位ADC在2MSPS最高采样速率下,测得ENOB为12.1位,SINAD为74.6dB,SNR为74.7dB,THD为-88.9dB。

采样速率最高可支持至4MSPS,测得ENOB为11位。

测试总结

5.1 实测参数与手册参数对比

汇总以上测试数据,HPM6750片内16位ADC的静态参数和动态参数如表 5.1所示。表中与HPM6750手册中的参数进行了对比,可以看到实测参数基本与手册符合。

表 5.1 实测参数与手册参数

28.png

如下表,静态参数部分与国外领先厂家的同类型SOC片内16位ADC参数进行对比。HPM6750的静态参数较好,DNL优于对比型号。INL约为±4LSB,优于S**32H750,与Lxx553x接近。

表 5.2 与国外厂家的静态参数对比

29.png

如下表,动态参数部分与国外领先厂家的同类型SOC片内16位ADC参数进行对比。HPM6750的动态参数与对比型号基本在同一水平,ENOB为12.1位,S**32H750为12.2位,Lxx553x为11.8位。

表 5.3 与国外厂家的动态参数对比

30.png

通过以上对比,HPM6750片内16位ADC的实测数据具有与同类型号S**32H750、Lxx553x几乎等同的性能,ENOB有效位数为12位,INL较好约±4LSB。

5.2 有效位数的区别

对于高分辨率ADC,关注实际能做到多少位,但是大部分情况下不具备搭建IEEE1421中正弦波测试环境的条件,测试有效位数ENOB。通常使用测量DC电压的方式,统计输出数据不跳动的位数,检查ADC的无噪声分辨率NFR。需要区分无噪声分辨率NFR,不等同于手册中的有效位数ENOB。NFR是测量直流或低频信号时,所关注的不跳动位数,而ENOB是测量交流动态信号关注的有效位数。NFR测量的是噪声的峰峰值,但没有包括ADC的非线性,ENOB测量的是噪声的均方值,还包括了ADC的非线性。高速ADC的手册中通常只标注动态参数ENOB,没有标注NFR,但是在ADC的非线性远小于噪声的峰峰值的情况下,可以从动态参数ENOB,估算能够获得的NFR。

【无噪声分辨率NFR的测试方法及计算】无噪声分辨率衡量ADC能够测量到最小直流信号,测试方法:输入端接地,或连接到一个通过大电容深度去耦低噪声的直流电压,然后采集大量采样点,并将其表示为直方图。外围电路设计良好时,等效到ADC输入端的噪声为白噪声,直方图呈正态分布。直方图的代码分布个数,表示峰峰值噪声,对应无噪声分辨率。

【有效位数ENOB的测试方法及计算】有效位数衡量ADC能够测量到的最小交流信号。测试方法:输入正弦信号,对采样数据进行FFT分析,计算所有噪声(包括量化噪声)和失真项的和方根值SINAD,并等效为ADC输入噪声,代替SNR,根据理想N位ADC的理论SNR公式,换算位数N。

SNR = 6.02N + 1.76dB

【通过ENOB估算NFR】根据参考资料[5],针对交流输入信号的ENOB,与直流低频信号的无噪声分辨率NFR,有如下的换算关系:

ENOB = NFR+0.92

对于直流低频信号,ADC的ENOB约比NFR大1位(0.92位)。但是以上计算过程,没有考虑ADC非线性,外围电路噪声、以及输入信号噪声影响,是理想情况下能获得的无噪声分辨率NFR。实际电路中,NFR与外围电路直接相关,ADC外围AVDD管脚、VFEFH管脚、接地平面,以及直流输入信号自身的噪声,均会直接影响ADC输出代码跳动,需要仔细设计外围电路和PCB(参考资料[6]),才能获得预期的无噪声分辨率。

测量直流低频信号时,除了硬件措施,对高速ADC输出代码做数字平均滤波,是提高无噪声分辨率的有效方法。HPM6750片内16位ADC,做数字平均之后的无噪声分辨率如表 5.4所示。被测的信号是一节1.5V干电池,可以看到平均4次之后,NFR为11位以上;平均32次之后,NFR为12位以上。

表 5.4 HPM6750数字平均之后的无噪声分辨率

31.png

 参考资料

[1] IEEE Standard for Terminology and Test Methods for Analog-to-Digital Converters.IEEE Std 1241-2010.

[2] Histogram Measurement of ADC Nonlinearities Using Sine Waves.Jerome Blair,1994.

[3] Full-Speed Testing of A/D Converters.Joey Doernberg ,1984.

[4] ADC Input Noise: The Good, The Bad, and The Ugly. Is No Noise Good Noise.ADI,Analong Dialogue 40-02,February(2006) .

[5] 高速模数转换器精度透视(第二部分).ADI,技术文章.

[6] 第二章 ADC信号调理电路设计.周立功 ,面向AMetal框架与接口的编程(上)。

来源:立功科技

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 914
订阅 RSS - HPM6750