开发者分享 | 构建完整控制系统:HPM5E00 外设综合应用

    科创经济 朗峰江湖 2026-02-13 3328 次浏览

    概要

    1、I2C传感器驱动

    2、UART自定义波形

    3、ADC采集模拟量

    4、modbus数据采集和设备控制

    5、motor电机步进控制

    1、I2C传感器驱动

    1.1 介绍

    众所周知,在自动化系统中,传感器是一个扮演着十分重要角色的反馈环节,通过实际监测的数据设计特殊的算法来修正控制系统的偏差,可以达到高效准确控制的目的;HT30是一款i2c接口的温湿度传感器电源采用3.3V供电,可以用来监测工业产线、农业生产、物联网环境下的温度和湿度值;先楫HPM5E00EVK开发板提供了4路I2C总线接口,支持标准模式100kbps,快速模式400kbps和快速+模式1 Mbps,为工业、农业、物联网应用提供了高效、便捷的数字量采集方式(下图是我手工焊接的模块)

    9cf968c6-07c7-11f1-96ea-92fbcf53809c.jpg

    1.2 原理图

    HPM_I2C0对应的GPIO:PC8 -> SCL、PC9 -> SDA,这一组接口位于开发板PPI连接槽

    1.3 硬件接口

    HT30连接HPM_I2C0接口引脚示意图,注意VCC为3.3V,电源正负极不能接反

    9d595f6a-07c7-11f1-96ea-92fbcf53809c.jpg

    1.4 模块驱动

    I2C0

    初始化时钟、引脚,复位总线

    voidinit_i2c_pins(I2C_Type*ptr)

    {

    if(ptr==HPM_I2C0){

    HPM_IOC->PAD[IOC_PAD_PC08].FUNC_CTL=IOC_PC08_FUNC_CTL_I2C0_SCL|IOC_PAD_FUNC_CTL_LOOP_BACK_MASK;

    HPM_IOC->PAD[IOC_PAD_PC09].FUNC_CTL=IOC_PC09_FUNC_CTL_I2C0_SDA|IOC_PAD_FUNC_CTL_LOOP_BACK_MASK;

    HPM_IOC->PAD[IOC_PAD_PC08].PAD_CTL=IOC_PAD_PAD_CTL_OD_SET(1)|IOC_PAD_PAD_CTL_PE_SET(1)|IOC_PAD_PAD_CTL_PS_SET(1);

    HPM_IOC->PAD[IOC_PAD_PC09].PAD_CTL=IOC_PAD_PAD_CTL_OD_SET(1)|IOC_PAD_PAD_CTL_PE_SET(1)|IOC_PAD_PAD_CTL_PS_SET(1);

    }

    voidboard_init_i2c(I2C_Type*ptr)

    {

    i2c_config_tconfig;

    hpm_stat_tstat;

    uint32_tfreq;

    freq=board_init_i2c_clock(ptr);

    init_i2c_pins(ptr);

    board_i2c_bus_clear(ptr);

    config.i2c_mode=i2c_mode_normal;

    config.is_10bit_addressing=false;

    stat=i2c_init_master(ptr,freq,&config);

    if(stat!=status_success){

    printf("failed to initialize i2c 0x%lx\n",(uint32_t)ptr);

    while(1){

    }

    }

    }

    读取接口(跟stm32nxp、infineon芯片无异)

    hpm_stat_ti2c_master_read(I2C_Type*ptr,constuint16_tdevice_address,

    uint8_t*buf,constuint32_tsize)

    写入接口

    hpm_stat_ti2c_master_write(I2C_Type*ptr,constuint16_tdevice_address,

    uint8_t*buf,constuint32_tsize)

    HT30

    读取HT30数据

    staticht30_error_tht30_read_data(uint8_t*data,uint8_tlen)

    {

    if(status_success!=i2c_master_read(TEST_I2C,HT30_I2C_ADDR,data,len)){

    printf("HT30 read data failed\n");

    returnHT30_ERROR_READ;

    }

    returnHT30_OK;

    }

    发送命令到HT30

    staticht30_error_tht30_send_command(uint16_tcommand)

    {

    uint8_tcmd_buf[2];

    cmd_buf[0]=(command>>8)&0xFF;// 高字节

    cmd_buf[1]=command&0xFF;// 低字节

    if(status_success!=i2c_master_write(TEST_I2C,HT30_I2C_ADDR,cmd_buf,2)){

    printf("HT30 send command failed: 0x%04X\n",command);

    returnHT30_ERROR_MEASURE;

    }

    returnHT30_OK;

    }

    初始化HT30传感器

    ht30_error_tht30_init(void)

    {

    printf("Initializing HT30 sensor...\n");

    // 发送软复位命令

    if(ht30_send_command(HT30_CMD_SOFT_RESET)!=HT30_OK){

    printf("HT30 soft reset failed\n");

    returnHT30_ERROR_INIT;

    }

    // 等待复位完成

    board_delay_ms(10);

    printf("HT30 sensor initialized successfully\n");

    returnHT30_OK;

    }

    读取HT30温湿度数据

    ht30_error_tht30_read_temperature_humidity(ht30_data_t*data)

    {

    uint8_trx_data[6];

    uint16_ttemp_raw,hum_raw;

    uint8_ttemp_crc,hum_crc;

    // 发送测量命令(高精度

    if(ht30_send_command(HT30_CMD_MEASURE_HIGH)!=HT30_OK){

    printf("HT30 measurement command failed\n");

    returnHT30_ERROR_MEASURE;

    }

    // 等待测量完成(高精度测量需要约15ms)

    board_delay_ms(20);

    // 读取6字节数据:温度(2字节) + CRC(1字节) + 湿度(2字节) + CRC(1字节)

    if(ht30_read_data(rx_data,6)!=HT30_OK){

    printf("HT30 read measurement data failed\n");

    returnHT30_ERROR_READ;

    }

    // 验证温度数据CRC

    temp_crc=ht30_calculate_crc(rx_data,2);

    if(temp_crc!=rx_data[2]){

    printf("HT30 temperature CRC error: expected 0x%02X, got 0x%02X\n",

    temp_crc,rx_data[2]);

    returnHT30_ERROR_CHECKSUM;

    }

    // 验证湿度数据CRC

    hum_crc=ht30_calculate_crc(&rx_data[3],2);

    if(hum_crc!=rx_data[5]){

    printf("HT30 humidity CRC error: expected 0x%02X, got 0x%02X\n",

    hum_crc,rx_data[5]);

    returnHT30_ERROR_CHECKSUM;

    }

    // 转换温度数据

    temp_raw=(rx_data[0]<<8)|rx_data[1];

    data->temperature=-45.0f+175.0f*((float)temp_raw/65535.0f);

    // 转换湿度数据

    hum_raw=(rx_data[3]<<8)|rx_data[4];

    data->humidity=100.0f*((float)hum_raw/65535.0f);

    returnHT30_OK;

    }

    读取HT30状态寄存器

    ht30_error_tht30_read_status(uint8_t*status)

    {

    uint8_trx_data[3];

    // 发送状态读取命令

    if(ht30_send_command(HT30_CMD_STATUS)!=HT30_OK){

    printf("HT30 status command failed\n");

    returnHT30_ERROR_MEASURE;

    }

    // 等待命令处理

    board_delay_ms(10);

    // 读取状态数据(2字节状态 + 1字节CRC)

    if(ht30_read_data(rx_data,3)!=HT30_OK){

    printf("HT30 read status data failed\n");

    returnHT30_ERROR_READ;

    }

    // 验证CRC

    uint8_tstatus_crc=ht30_calculate_crc(rx_data,2);

    if(status_crc!=rx_data[2]){

    printf("HT30 status CRC error: expected 0x%02X, got 0x%02X\n",

    status_crc,rx_data[2]);

    returnHT30_ERROR_CHECKSUM;

    }

    *status=rx_data[1];// 状态字节

    returnHT30_OK;

    }

    计算CRC校验 (CRC-8多项式0x31)

    staticuint8_tht30_calculate_crc(uint8_t*data,uint8_tlen)

    {

    uint8_tcrc=0xFF;

    uint8_ti,j;

    for(i=0;i<len;i++){

    crc=data[i];

    for(j=0;j<8;j++){

    if(crc&0x80){

    crc=(crc<<1)0x31;

    }else{

    crc=crc<<1;

    }

    }

    }

    returncrc;

    }

    检查HT30传感器是否响应

    ht30_error_tht30_check_connection(void)

    {

    uint8_tstatus;

    returnht30_read_status(&status);

    }

    打印HT30数据

    voidht30_print_data(ht30_data_t*data)

    {

    printf("HT30 Sensor Data:\n");

    printf("Temperature: %.2f °C\n",data->temperature);

    printf("Humidity: %.2f %%RH\n",data->humidity);

    printf("Status: 0x%02X",data->status);

    // 解析状态寄存器

    if(data->status==0x08){

    printf(" (Normal operation)");

    }elseif(data->status&0x01){

    printf(" (Alert pending)");

    }elseif(data->status&0x02){

    printf(" (Heater on)");

    }elseif(data->status&0x04){

    printf(" (RH tracking alert)");

    }elseif(data->status&0x08){

    printf(" (T tracking alert)");

    }elseif(data->status&0x10){

    printf(" (Reset detected)");

    }elseif(data->status&0x20){

    printf(" (Command not processed)");

    }elseif(data->status&0x40){

    printf(" (Write data checksum error)");

    }elseif(data->status&0x80){

    printf(" (Write data error)");

    }

    printf("\n");

    }

    1.5 测试用例

    轮询温度值、湿度值、工作状态,把数值反馈给上位机PC端

    intmain(void)

    {

    board_init();

    board_init_i2c(TEST_I2C);

    printf("HT30 Temperature and Humidity Sensor Example\n");

    printf("============================================\n");

    printf("I2C Address: 0x%02X\n",HT30_I2C_ADDR);

    // 检查传感器连接

    printf("Checking HT30 sensor connection...\n");

    if(ht30_check_connection()!=HT30_OK){

    printf("HT30 sensor not found or not responding!\n");

    printf("Please check:\n");

    printf("1. Sensor is properly connected to I2C bus\n");

    printf("2. I2C address is correct (0x%02X)\n",HT30_I2C_ADDR);

    printf("3. Power supply is stable\n");

    while(1){

    }

    }

    printf("HT30 sensor found and responding!\n");

    // 初始化HT30传感器

    if(ht30_init()!=HT30_OK){

    printf("Failed to initialize HT30 sensor\n");

    while(1){

    }

    }

    // 读取传感器状态

    uint8_tstatus;

    if(ht30_read_status(&status)==HT30_OK){

    printf("HT30 Status: 0x%02X\n",status);

    }

    ht30_data_tsensor_data;

    uint32_tmeasurement_count=0;

    printf("\nStarting continuous measurement...\n");

    printf("Press Ctrl+C to stop\n\n");

    while(1){

    // 读取温湿度数据

    if(ht30_read_temperature_humidity(&sensor_data)==HT30_OK){

    printf("\n--- Measurement #%d ---\n",++measurement_count);

    ht30_print_data(&sensor_data);

    }else{

    printf("Failed to read HT30 sensor data\n");

    }

    // 等待5秒后进行下一次测量

    board_delay_ms(1000);

    }

    return0;

    }

    1.6 实验现象

    串口终端查看采集到的温湿度传感器数据和传感器的工作状态

    1.7 开源

    完整代码已上传Gitee:https://gitee.com/hywing/hpm5e00evk

    2、UART自定义波形

    2.1 介绍

    HPM5E00芯片集成了9个通用异步收发器UART,其中1个 (PUART) 位于电源管理域,支持低功耗唤醒;串口通信在开发中常用作调试、诊断、升级、通信功能,通常搭配PC上位机一起使用;这次实验以HPM_UART0为例,展示基于串口的通信测试过程

    2.2 原理图

    HPM_UART0对应的引脚为PA0、PA1,分别对应功能:UART0_TXD、UART0_RXD,在HPM5E00EVK开发板上作为默认的调试串口,通过FT2232芯片连接到上位机

    9dbad218-07c7-11f1-96ea-92fbcf53809c.png

    PC上安装好FTDI的驱动(先楫提供的SDK包里边有)会有串口设备映射

    9de755fe-07c7-11f1-96ea-92fbcf53809c.png

    2.3 驱动代码

    串口引脚绑定PA0、PA1

    voidinit_uart_pins(UART_Type*ptr)

    {

    if(ptr==HPM_UART0){

    HPM_IOC->PAD[IOC_PAD_PA00].FUNC_CTL=IOC_PA00_FUNC_CTL_UART0_TXD;

    HPM_IOC->PAD[IOC_PAD_PA01].FUNC_CTL=IOC_PA01_FUNC_CTL_UART0_RXD;

    }elseif(ptr==HPM_UART4){

    HPM_IOC->PAD[IOC_PAD_PC16].FUNC_CTL=IOC_PC16_FUNC_CTL_UART4_TXD;

    HPM_IOC->PAD[IOC_PAD_PC17].FUNC_CTL=IOC_PC17_FUNC_CTL_UART4_RXD;

    }else{

    ;

    }

    }

    串口通信配置,设置波特率为115200,停止位1,数据位8,奇偶校验位0

    uart_config_tconfig={0};

    uart_default_config(TEST_UART,&config);

    #ifdefined(CONFIG_UART_FIFO_MODE)&&(CONFIG_UART_FIFO_MODE==1)

    config.fifo_enable=true;

    #else

    config.fifo_enable=false;

    #endif

    #ifdefTEST_UART_SRC_FREQ

    config.src_freq_in_hz=TEST_UART_SRC_FREQ;

    #else

    config.src_freq_in_hz=clock_get_frequency(TEST_UART_CLK_NAME);

    #endif

    config.tx_fifo_level=uart_tx_fifo_trg_lt_half;

    config.baudrate=115200;

    stat=uart_init(TEST_UART,&config);

    启用发送中断和接收中断

    uart_enable_irq(TEST_UART,uart_intr_tx_slot_avail);

    uart_enable_irq(TEST_UART,uart_intr_rx_data_avail_or_timeout);

    intc_m_enable_irq_with_priority(TEST_UART_IRQ,1);

    实现中断服务函数

    SDK_DECLARE_EXT_ISR_M(TEST_UART_IRQ,uart_isr)

    voiduart_isr(void)

    {

    uint8_tirq_id=uart_get_irq_id(TEST_UART);

    // 处理接收中断

    if(irq_id==uart_intr_id_rx_data_avail){

    while(uart_check_status(TEST_UART,uart_stat_data_ready)){

    data_buff[buff_index++]=uart_read_byte(TEST_UART);

    if(buff_index>=TEST_UART_MAX_BUFFER_SIZE){

    buff_index=0;// 防止缓冲区溢出

    }

    }

    // 接收到数据后,启用发送中断来发送回显数据

    uart_enable_irq(TEST_UART,uart_intr_tx_slot_avail);

    }

    // 处理发送中断

    if(irq_id==uart_intr_id_tx_slot_avail){

    // 优先处理printf缓冲区

    if(printf_busy&&printf_read_index!=printf_write_index){

    uart_write_byte(TEST_UART,printf_buffer[printf_read_index]);

    printf_read_index=(printf_read_index+1)%PRINTF_BUFFER_SIZE;

    // 如果printf缓冲区发送完毕

    if(printf_read_index==printf_write_index){

    printf_busy=false;

    }

    }

    // 然后处理回显缓冲区

    elseif(buff_index>0){

    for(uint8_ti=0;i<buff_index;i++){

    uart_write_byte(TEST_UART,data_buff[i]);

    }

    buff_index=0;// 清空缓冲区索引

    }

    // 如果两个缓冲区都发送完毕,禁用发送中断

    if(!printf_busy&&buff_index==0){

    uart_disable_irq(TEST_UART,uart_intr_tx_slot_avail);

    }

    }

    }

    2.4 测试用例

    阻塞版的printf,官方demo已经有实现,为了更直观的展示数据通信过程和让串口通信更高效,我们自定义printf函数,并且使用中断发送

    voidmy_printf(constchar*format,...)

    {

    charbuffer[128];

    va_list args;

    va_start(args,format);

    intlen=vsnprintf(buffer,sizeof(buffer),format,args);

    va_end(args);

    if(len>0&&len<sizeof(buffer)){

    printf_interrupt_send(buffer,len);

    }

    }

    printf中断发送函数

    uint8_tbuff_index;

    uint8_tdata_count;

    ATTR_PLACE_AT_NONCACHEABLEuint8_tdata_buff[TEST_UART_MAX_BUFFER_SIZE];

    // printf中断发送相关变量

    volatilebool printf_busy=false;

    volatileuint16_tprintf_write_index=0;

    volatileuint16_tprintf_read_index=0;

    ATTR_PLACE_AT_NONCACHEABLEuint8_tprintf_buffer[PRINTF_BUFFER_SIZE];

    // printf中断发送函数

    voidprintf_interrupt_send(constchar*str,intlen)

    {

    for(inti=0;i<len;i++){

    // 等待缓冲区有空间

    while(((printf_write_index+1)%PRINTF_BUFFER_SIZE)==printf_read_index){

    // 缓冲区满,等待发送

    __asm("wfi;\n");

    }

    printf_buffer[printf_write_index]=str[i];

    printf_write_index=(printf_write_index+1)%PRINTF_BUFFER_SIZE;

    // 如果printf发送空闲,启动发送

    if(!printf_busy){

    printf_busy=true;

    uart_enable_irq(TEST_UART,uart_intr_tx_slot_avail);

    }

    }

    }

    生成模拟的数据集:生成FireWater数据流,通过UART0中断+FIFO形式发给上位机

    while(1)

    {

    staticfloatangle=0.0f;

    floatsinVal,cosVal,sinCosVal,cosSquareVal;

    // 计算三角函数值

    sinVal=sinf(angle);

    cosVal=cosf(angle);

    sinCosVal=sinVal*cosVal;

    cosSquareVal=cosVal*cosVal;

    // 使用指定格式输出数据:"%f,%f,%f,%f\n"

    my_printf("%f,%f,%f,%f\r\n",sinVal,cosVal,sinCosVal,cosSquareVal);

    // 增加角度,每次增加0.1弧度

    angle+=0.1f;

    if(angle>=2*M_PI){

    angle=0.0f;// 重置角度

    }

    // 添加延时,避免输出过快

    board_delay_us(100);

    }

    2.5 实验效果

    这样我们就实现了一个用中断+FIFO实现的自定义printf函数,这个在日常开发中可以很方便的查看数据的变化过程,方便诊断和记录,下面是VOFA+的实时展示来自HPM5E00EVK开发板串口数据波形效果

    9e13fc08-07c7-11f1-96ea-92fbcf53809c.gif

    2.6 开源

    完整代码已上传Gitee:https://gitee.com/hywing/hpm5e00evk

    3、ADC采集模拟量

    3.1 介绍

    工业自动化环境中,生产设备或者零部件经常需要监控各种各样的物理状态参数:温度、压强、真空度、流量、湿度、电压、电流、功率、光照等,而这些都是连续变化的信号,属于模拟量,我们可以通过数模转换器(ADC)采集这些物理信号,通过处理、收集和分发反馈到设备组态监控HMI控制台,从而达到实时监测、控制、安全生产的目的;先楫HPM5E00芯片集成多路高精度模拟量采集通道,模拟外设包括:2个16位模拟数字转换器ADC和1个模拟比较控制器 ACMP(共管理 2 个模拟比较器),我们可以用它来进行各种模拟信号的采集和处理

    3.2 原理图

    测量采用ADC_C,位于MOTOR_IF连接槽,关联引脚PF26

    9e53cfc2-07c7-11f1-96ea-92fbcf53809c.png

    3.3 热敏电阻

    手动焊接了一个热敏电阻的测试电路:NTC热敏电阻串联10kΩ电阻,NTC的标称阻值为10kΩ,B值为3950

    9e89c2d0-07c7-11f1-96ea-92fbcf53809c.png

    实际连接:串联两端连接3.3V和GND,中间引脚连接ADC端口

    9ec98b72-07c7-11f1-96ea-92fbcf53809c.jpg

    3.4 驱动代码

    HPM_ADC0引脚初始化:绑定PF26引脚

    voidinit_adc16_pins(void)

    {

    HPM_IOC->PAD[IOC_PAD_PF26].FUNC_CTL=IOC_PAD_FUNC_CTL_ANALOG_MASK;

    }

    ADC时钟初始化

    uint32_tboard_init_adc_clock(void*ptr,bool clk_src_bus)/* motor system should be use clk_adc_src_ahb0 */

    {

    uint32_tfreq=0;

    if(ptr==(void*)HPM_ADC0){

    clock_add_to_group(clock_adc0,0);

    if(clk_src_bus){

    /* Configure the ADC clock from AHB (@200MHz by default)*/

    clock_set_adc_source(clock_adc0,clk_adc_src_ahb0);

    }else{

    /* Configure the ADC clock from ANA (@200MHz by default)*/

    clock_set_adc_source(clock_adc0,clk_adc_src_ana0);

    }

    freq=clock_get_frequency(clock_adc0);

    }

    returnfreq;

    }

    读取ADC值

    uint16_tresult;

    floattemperature;

    floatvoltage;

    adc16_get_prd_result(BOARD_APP_ADC16_BASE,BOARD_APP_ADC16_CH_1,&result);

    3.5 测试用例

    ADC值和热敏电阻阻值变化关系

    staticconsttemp_lookup_entry_ttemp_lookup_table[]={

    {0x0637,-40.0f},/* -40°C */

    {0x06ab,-39.0f},/* -39°C */

    {0x0726,-38.0f},/* -38°C */

    {0x07a9,-37.0f},/* -37°C */

    {0x0834,-36.0f},/* -36°C */

    {0x08c7,-35.0f},/* -35°C */

    {0x0963,-34.0f},/* -34°C */

    {0x0a07,-33.0f},/* -33°C */

    {0x0ab5,-32.0f},/* -32°C */

    {0x0b6c,-31.0f},/* -31°C */

    {0x0c2d,-30.0f},/* -30°C */

    {0x0cf9,-29.0f},/* -29°C */

    {0x0dcf,-28.0f},/* -28°C */

    {0x0eb1,-27.0f},/* -27°C */

    {0x0f9e,-26.0f},/* -26°C */

    {0x1096,-25.0f},/* -25°C */

    {0x119b,-24.0f},/* -24°C */

    {0x12ad,-23.0f},/* -23°C */

    {0x13cb,-22.0f},/* -22°C */

    {0x14f6,-21.0f},/* -21°C */

    {0x162f,-20.0f},/* -20°C */

    {0x1776,-19.0f},/* -19°C */

    {0x18cb,-18.0f},/* -18°C */

    {0x1a2e,-17.0f},/* -17°C */

    {0x1b9f,-16.0f},/* -16°C */

    {0x1d1f,-15.0f},/* -15°C */

    {0x1eae,-14.0f},/* -14°C */

    {0x204d,-13.0f},/* -13°C */

    {0x21fa,-12.0f},/* -12°C */

    {0x23b6,-11.0f},/* -11°C */

    {0x2582,-10.0f},/* -10°C */

    {0x275d,-9.0f},/* -9°C */

    {0x2948,-8.0f},/* -8°C */

    {0x2b41,-7.0f},/* -7°C */

    {0x2d4a,-6.0f},/* -6°C */

    {0x2f62,-5.0f},/* -5°C */

    {0x3188,-4.0f},/* -4°C */

    {0x33bd,-3.0f},/* -3°C */

    {0x3600,-2.0f},/* -2°C */

    {0x3851,-1.0f},/* -1°C */

    {0x3aaf,0.0f},/* 0°C */

    {0x3d1b,1.0f},/* 1°C */

    {0x3f93,2.0f},/* 2°C */

    {0x4217,3.0f},/* 3°C */

    {0x44a7,4.0f},/* 4°C */

    {0x4742,5.0f},/* 5°C */

    {0x49e7,6.0f},/* 6°C */

    {0x4c96,7.0f},/* 7°C */

    {0x4f4e,8.0f},/* 8°C */

    {0x520e,9.0f},/* 9°C */

    {0x54d6,10.0f},/* 10°C */

    {0x57a5,11.0f},/* 11°C */

    {0x5a7a,12.0f},/* 12°C */

    {0x5d54,13.0f},/* 13°C */

    {0x6032,14.0f},/* 14°C */

    {0x6314,15.0f},/* 15°C */

    {0x65f9,16.0f},/* 16°C */

    {0x68e0,17.0f},/* 17°C */

    {0x6bc8,18.0f},/* 18°C */

    {0x6eb0,19.0f},/* 19°C */

    {0x7199,20.0f},/* 20°C */

    {0x747f,21.0f},/* 21°C */

    {0x7764,22.0f},/* 22°C */

    {0x7a46,23.0f},/* 23°C */

    {0x7d25,24.0f},/* 24°C */

    {0x7fff,25.0f},/* 25°C */

    {0x82d4,26.0f},/* 26°C */

    {0x85a4,27.0f},/* 27°C */

    {0x886e,28.0f},/* 28°C */

    {0x8b31,29.0f},/* 29°C */

    {0x8ded,30.0f},/* 30°C */

    {0x90a1,31.0f},/* 31°C */

    {0x934c,32.0f},/* 32°C */

    {0x95ef,33.0f},/* 33°C */

    {0x9888,34.0f},/* 34°C */

    {0x9b18,35.0f},/* 35°C */

    {0x9d9e,36.0f},/* 36°C */

    {0xa01a,37.0f},/* 37°C */

    {0xa28b,38.0f},/* 38°C */

    {0xa4f1,39.0f},/* 39°C */

    {0xa74d,40.0f},/* 40°C */

    {0xa99d,41.0f},/* 41°C */

    {0xabe2,42.0f},/* 42°C */

    {0xae1b,43.0f},/* 43°C */

    {0xb048,44.0f},/* 44°C */

    {0xb26a,45.0f},/* 45°C */

    {0xb481,46.0f},/* 46°C */

    {0xb68b,47.0f},/* 47°C */

    {0xb88a,48.0f},/* 48°C */

    {0xba7d,49.0f},/* 49°C */

    {0xbc65,50.0f},/* 50°C */

    {0xbe41,51.0f},/* 51°C */

    {0xc011,52.0f},/* 52°C */

    {0xc1d6,53.0f},/* 53°C */

    {0xc390,54.0f},/* 54°C */

    {0xc53f,55.0f},/* 55°C */

    {0xc6e2,56.0f},/* 56°C */

    {0xc87b,57.0f},/* 57°C */

    {0xca09,58.0f},/* 58°C */

    {0xcb8d,59.0f},/* 59°C */

    {0xcd06,60.0f},/* 60°C */

    {0xce74,61.0f},/* 61°C */

    {0xcfd9,62.0f},/* 62°C */

    {0xd134,63.0f},/* 63°C */

    {0xd286,64.0f},/* 64°C */

    {0xd3ce,65.0f},/* 65°C */

    {0xd50c,66.0f},/* 66°C */

    {0xd642,67.0f},/* 67°C */

    {0xd76f,68.0f},/* 68°C */

    {0xd893,69.0f},/* 69°C */

    {0xd9af,70.0f},/* 70°C */

    {0xdac3,71.0f},/* 71°C */

    {0xdbcf,72.0f},/* 72°C */

    {0xdcd3,73.0f},/* 73°C */

    {0xddcf,74.0f},/* 74°C */

    {0xdec4,75.0f},/* 75°C */

    {0xdfb1,76.0f},/* 76°C */

    {0xe098,77.0f},/* 77°C */

    {0xe178,78.0f},/* 78°C */

    {0xe251,79.0f},/* 79°C */

    {0xe324,80.0f},/* 80°C */

    {0xe3f0,81.0f},/* 81°C */

    {0xe4b7,82.0f},/* 82°C */

    {0xe577,83.0f},/* 83°C */

    {0xe632,84.0f},/* 84°C */

    {0xe6e7,85.0f},/* 85°C */

    {0xe797,86.0f},/* 86°C */

    {0xe841,87.0f},/* 87°C */

    {0xe8e7,88.0f},/* 88°C */

    {0xe987,89.0f},/* 89°C */

    {0xea23,90.0f},/* 90°C */

    {0xeaba,91.0f},/* 91°C */

    {0xeb4c,92.0f},/* 92°C */

    {0xebda,93.0f},/* 93°C */

    {0xec64,94.0f},/* 94°C */

    {0xecea,95.0f},/* 95°C */

    {0xed6c,96.0f},/* 96°C */

    {0xedea,97.0f},/* 97°C */

    {0xee64,98.0f},/* 98°C */

    {0xeeda,99.0f},/* 99°C */

    {0xef4d,100.0f},/* 100°C */

    {0xefbd,101.0f},/* 101°C */

    {0xf029,102.0f},/* 102°C */

    {0xf092,103.0f},/* 103°C */

    {0xf0f9,104.0f},/* 104°C */

    {0xf15c,105.0f},/* 105°C */

    {0xf1bc,106.0f},/* 106°C */

    {0xf219,107.0f},/* 107°C */

    {0xf274,108.0f},/* 108°C */

    {0xf2cc,109.0f},/* 109°C */

    {0xf321,110.0f},/* 110°C */

    {0xf374,111.0f},/* 111°C */

    {0xf3c5,112.0f},/* 112°C */

    {0xf413,113.0f},/* 113°C */

    {0xf45f,114.0f},/* 114°C */

    {0xf4a8,115.0f},/* 115°C */

    {0xf4f0,116.0f},/* 116°C */

    {0xf536,117.0f},/* 117°C */

    {0xf579,118.0f},/* 118°C */

    {0xf5bb,119.0f},/* 119°C */

    {0xf5fb,120.0f},/* 120°C */

    {0xf639,121.0f},/* 121°C */

    {0xf675,122.0f},/* 122°C */

    {0xf6b0,123.0f},/* 123°C */

    {0xf6e9,124.0f},/* 124°C */

    {0xf720,125.0f},/* 125°C */

    {0xf756,126.0f},/* 126°C */

    {0xf78b,127.0f},/* 127°C */

    {0xf7bd,128.0f},/* 128°C */

    {0xf7ef,129.0f},/* 129°C */

    {0xf81f,130.0f},/* 130°C */

    {0xf84e,131.0f},/* 131°C */

    {0xf87b,132.0f},/* 132°C */

    {0xf8a8,133.0f},/* 133°C */

    {0xf8d3,134.0f},/* 134°C */

    {0xf8fd,135.0f},/* 135°C */

    {0xf926,136.0f},/* 136°C */

    {0xf94d,137.0f},/* 137°C */

    {0xf974,138.0f},/* 138°C */

    {0xf99a,139.0f},/* 139°C */

    {0xf9be,140.0f},/* 140°C */

    {0xf9e2,141.0f},/* 141°C */

    {0xfa05,142.0f},/* 142°C */

    {0xfa26,143.0f},/* 143°C */

    {0xfa47,144.0f},/* 144°C */

    {0xfa67,145.0f},/* 145°C */

    {0xfa87,146.0f},/* 146°C */

    {0xfaa5,147.0f},/* 147°C */

    {0xfac3,148.0f},/* 148°C */

    {0xfadf,149.0f},/* 149°C */

    {0xfafc,150.0f}/* 150°C */

    };

    根据ADC值匹配对应的温度

    /* Temperature calculation function using lookup table */

    floatcalculate_temperature_from_adc(uint16_tadc_value)

    {

    /* Handle edge cases */

    if(adc_value<=temp_lookup_table[0].adc_value){

    returntemp_lookup_table[0].temperature;

    }

    if(adc_value>=temp_lookup_table[TEMP_LOOKUP_TABLE_SIZE-1].adc_value){

    returntemp_lookup_table[TEMP_LOOKUP_TABLE_SIZE-1].temperature;

    }

    /* Find the correct temperature range */

    for(inti=1;i<TEMP_LOOKUP_TABLE_SIZE;i++){

    if(adc_value<=temp_lookup_table[i].adc_value){

    /* Linear interpolation between two points */

    uint16_tadc_low=temp_lookup_table[i-1].adc_value;

    uint16_tadc_high=temp_lookup_table[i].adc_value;

    floattemp_low=temp_lookup_table[i-1].temperature;

    floattemp_high=temp_lookup_table[i].temperature;

    floatratio=(float)(adc_value-adc_low)/(float)(adc_high-adc_low);

    returntemp_low+ratio*(temp_high-temp_low);

    }

    }

    /* Should not reach here */

    returntemp_lookup_table[TEMP_LOOKUP_TABLE_SIZE-1].temperature;

    }

    周期性处理ADC温度值

    voidperiod_handler(void)

    {

    uint16_tresult;

    floattemperature;

    floatvoltage;

    adc16_get_prd_result(BOARD_APP_ADC16_BASE,BOARD_APP_ADC16_CH_1,&result);

    temperature=calculate_temperature_from_adc(result);

    voltage=(float)result/ADC_MAX_VALUE*V_REF;

    printf("ADC: 0x%04x, Voltage: %.3fV, Temperature: %.1f°C\n",

    result,voltage,temperature);

    /* Add 1 second delay for temperature monitoring */

    board_delay_ms(1000);

    }

    3.6 实验现象

    测到室温环境温度变化,室温大概在30℃

    9ef2c622-07c7-11f1-96ea-92fbcf53809c.gif

    万用表测量的温度

    9f2db0ca-07c7-11f1-96ea-92fbcf53809c.png

    3.7 总结

    原始的温度值和NTC阻值是由一个物理方程式(Steinhart-Hart方程)来确定的,通过查表的方法可以大大提高程序执行效率,避免Steinhart-Hart方程带来的巨大计算量,特别适合嵌入式板卡的数据处理,工业自动化一般用热电偶比较多,驱动原理都类似

    9f6dd81c-07c7-11f1-96ea-92fbcf53809c.png

    3.8 开源

    完整代码已上传Gitee:https://gitee.com/hywing/hpm5e00evk

    4、modbus数据采集和设备控制

    4.1 介绍

    modbus协议在物联网、工业控制领域应用广泛,常用于数据采集和设备控制;先楫HPM5E00EVK开发板集成了一路通用以太网接口,我们可以利用它来进行modbus tcp rtu通信

    4.2 原理图

    实验中我们使用RGMII网口进行modbus通信

    9f945104-07c7-11f1-96ea-92fbcf53809c.png

    RGMII网口是千兆以太网口,不同于EtherCAT通信网口,它靠近USB调试口

    9fcc71ba-07c7-11f1-96ea-92fbcf53809c.jpg

    4.3 以太网

    以太网配置:设置MAC地址、IP地址、服务器端口

    /* MAC ADDRESS */

    #defineMAC_ADDR00x98

    #defineMAC_ADDR10x2C

    #defineMAC_ADDR20xBC

    #defineMAC_ADDR30xB1

    #defineMAC_ADDR40x9F

    #defineMAC_ADDR50x17

    /* Static IP ADDRESS */

    #defineIP_ADDR0192

    #defineIP_ADDR1168

    #defineIP_ADDR255

    #ifdefMODBUS_TCP_SLAVE

    #defineIP_ADDR310

    #else

    #defineIP_ADDR311

    #endif

    /* NETMASK */

    #defineNETMASK_ADDR0255

    #defineNETMASK_ADDR1255

    #defineNETMASK_ADDR2255

    #defineNETMASK_ADDR30

    /* Gateway Address */

    #defineGW_ADDR0192

    #defineGW_ADDR1168

    #defineGW_ADDR255

    #defineGW_ADDR31

    /* Remote IP Address */

    #defineREMOTE_IP_ADDR0192

    #defineREMOTE_IP_ADDR1168

    #defineREMOTE_IP_ADDR255

    #defineREMOTE_IP_ADDR388

    #defineTCP_SERVER_PORT(5001U)

    #defineNETWORK_BUFFER_SIZE(4096U)

    以太网初始化:引脚、PHY、MAC及lwip

    hpm_stat_tnetwork_init(void)

    {

    hpm_stat_tsta=status_success;

    /* Initialize GPIOs */

    board_init_enet_pins(ENET);

    /* Reset an enet PHY */

    board_reset_enet_phy(ENET);

    #if__ENABLE_ENET_RECEIVE_INTERRUPT

    printf("This is an ethernet demo: modbus tcp(Interrupt Usage)\n");

    #else

    printf("This is an ethernet demo: modbus tcp (Polling Usage)\n");

    #endif

    printf("LwIP Version: %s\n",LWIP_VERSION_STRING);

    /* Set RGMII clock delay */

    #ifdefined(RGMII)&&RGMII

    board_init_enet_rgmii_clock_delay(ENET);

    #elifdefined(RMII)&&RMII

    /* Set RMII reference clock */

    board_init_enet_rmii_reference_clock(ENET,BOARD_ENET_RMII_INT_REF_CLK);

    printf("Reference Clock: %s\n",BOARD_ENET_RMII_INT_REF_CLK?"Internal Clock":"External Clock");

    #endif

    /* Start a board timer */

    board_timer_create(LWIP_APP_TIMER_INTERVAL,sys_timer_callback);

    /* Initialize MAC and DMA */

    sta=enet_init(ENET);

    if(sta==status_success){

    /* Initialize the Lwip stack */

    lwip_init();

    netif_config();

    user_notification(&gnetif);

    /* Start services */

    enet_services(&gnetif);

    }

    returnsta;

    }

    tcp服务器初始化

    if(network_tcp_server_init()!=status_success){

    printf("network tcp client init fail\n");

    while(1){

    };

    }

    4.4 modbus

    配置agile_modbus库

    agile_modbus_tcp_init(&ctx_tcp,ctx_send_buf,sizeof(ctx_send_buf),ctx_read_buf,sizeof(ctx_read_buf));

    agile_modbus_set_slave(ctx,1);

    寄存器定义:LED、温度、湿度、真空度、气压、转速 ......

    #defineREG_LED_CONTROL0/* LED控制寄存器 */

    #defineREG_TEMPERATURE1/* 温度寄存器 (0.1°C) */

    #defineREG_HUMIDITY2/* 湿度寄存器 (0.1%) */

    #defineREG_VACUUM_PRESSURE3/* 真空度寄存器 (0.01 Pa) */

    #defineREG_ATMOSPHERIC_PRESSURE4/* 气压寄存器 (0.1 hPa) */

    #defineREG_MOTOR_SPEED5/* 真实转速寄存器 (RPM) */

    #defineREG_MOTOR_POSITION6/* 电机位置寄存器 (0.1°) */

    #defineREG_SENSOR_STATUS7/* 传感器状态寄存器 */

    #defineREG_ALARM_STATUS8/* 报警状态寄存器 */

    #defineREG_SYSTEM_STATUS9/* 系统状态寄存器 */

    寄存器初始化数值

    staticuint16_t_tab_registers[REGISTER_COUNT]={

    0,/* LED控制: 0=关闭, 1=开启 */

    250,/* 温度: 25.0°C */

    650,/* 湿度: 65.0% */

    10132,/* 真空度: 1013.2 Pa (标准大气压) */

    1013,/* 气压: 1013.0 hPa */

    1500,/* 电机转速: 1500 RPM */

    1800,/* 电机位置: 180.0° */

    0x00FF,/* 传感器状态: 所有传感器正常 */

    0x0000,/* 报警状态: 无报警 */

    0x0001/* 系统状态: 系统运行正常 */

    };

    LED控制

    staticvoidled_control(uint16_tvalue)

    {

    if(value==0){

    board_led_write(BOARD_LED_OFF_LEVEL);/* 关闭LED */

    printf("LED turned OFF\n");

    }else{

    board_led_write(BOARD_LED_ON_LEVEL);/* 开启LED */

    printf("LED turned ON\n");

    }

    }

    模拟传感器数据更新函数,实际条件下可以通过模拟或者数字方式获取传感器数值

    staticvoidupdate_sensor_data(void)

    {

    update_counter++;

    /* 模拟温度变化 (20-30°C) */

    int16_ttemp_variation=(rand()%200)-100;/* -10.0°C 到 +10.0°C */

    int32_tnew_temp=250+temp_variation;/* 基准25.0°C */

    if(new_temp<200)new_temp=200;/* 最低20.0°C */

    if(new_temp>300)new_temp=300;/* 最高30.0°C */

    _tab_registers[REG_TEMPERATURE]=(uint16_t)new_temp;

    /* 模拟湿度变化 (50-80%) */

    int16_thum_variation=(rand()%300)-150;/* -15.0% 到 +15.0% */

    int32_tnew_hum=650+hum_variation;/* 基准65.0% */

    if(new_hum<500)new_hum=500;/* 最低50.0% */

    if(new_hum>800)new_hum=800;/* 最高80.0% */

    _tab_registers[REG_HUMIDITY]=(uint16_t)new_hum;

    /* 模拟真空度变化 (10100-10200 Pa) */

    int16_tvac_variation=(rand()%200)-100;/* ±100 Pa */

    int32_tnew_vac=10132+vac_variation;

    if(new_vac<10100)new_vac=10100;

    if(new_vac>10200)new_vac=10200;

    _tab_registers[REG_VACUUM_PRESSURE]=(uint16_t)new_vac;/* 存储为0.1 Pa单位 */

    /* 模拟气压变化 (1000-1030 hPa) */

    int16_tatm_variation=(rand()%300)-150;/* ±15 hPa */

    int32_tnew_atm=1013+atm_variation;

    if(new_atm<1000)new_atm=1000;

    if(new_atm>1030)new_atm=1030;

    _tab_registers[REG_ATMOSPHERIC_PRESSURE]=(uint16_t)new_atm;

    /* 模拟电机转速变化 (1400-1600 RPM) */

    int16_tspeed_variation=(rand()%200)-100;/* ±100 RPM */

    int32_tnew_speed=1500+speed_variation;

    if(new_speed<1400)new_speed=1400;

    if(new_speed>1600)new_speed=1600;

    _tab_registers[REG_MOTOR_SPEED]=(uint16_t)new_speed;

    /* 模拟电机位置变化 (0-360°) */

    _tab_registers[REG_MOTOR_POSITION]=(update_counter*10)%3600;/* 0.1°为单位 */

    /* 更新传感器状态 */

    _tab_registers[REG_SENSOR_STATUS]=0x00FF;/* 所有传感器正常 */

    /* 模拟报警状态 */

    if(_tab_registers[REG_TEMPERATURE]>280){/* 温度超过28°C */

    _tab_registers[REG_ALARM_STATUS]|=0x0001;/* 温度报警 */

    }else{

    _tab_registers[REG_ALARM_STATUS]&=~0x0001;

    }

    if(_tab_registers[REG_MOTOR_SPEED]>1550){/* 转速超过1550 RPM */

    _tab_registers[REG_ALARM_STATUS]|=0x0002;/* 转速报警 */

    }else{

    _tab_registers[REG_ALARM_STATUS]&=~0x0002;

    }

    /* 系统状态保持正常 */

    _tab_registers[REG_SYSTEM_STATUS]=0x0001;

    }

    寄存器映射

    staticintget_map_buf(void*buf,intbufsz)

    {

    (void)bufsz;

    uint16_t*ptr=(uint16_t*)buf;

    /* 更新传感器数据 */

    update_sensor_data();

    for(uint32_ti=0;i<sizeof(_tab_registers)/sizeof(_tab_registers[0]);i++){

    ptr[i]=_tab_registers[i];

    }

    return0;

    }

    staticintset_map_buf(intindex,intlen,void*buf,intbufsz)

    {

    (void)bufsz;

    uint16_t*ptr=(uint16_t*)buf;

    for(inti=0;i<len;i++){

    _tab_registers[index+i]=ptr[index+i];

    /* 如果修改的是LED控制寄存器(地址0),则控制LED */

    if((index+i)==REG_LED_CONTROL){

    led_control(_tab_registers[REG_LED_CONTROL]);

    }

    /* 打印寄存器写入信息 */

    printf("Register 0x%04X written: 0x%04X (%d)\n",

    index+i,_tab_registers[index+i],_tab_registers[index+i]);

    }

    return0;

    }

    constagile_modbus_slave_util_map_thold_register_maps[1]={

    {0x00,REGISTER_COUNT-1,get_map_buf,set_map_buf}};

    4.5 测试用例

    添加寄存器访问接口

    staticintmodbus_slave_process(uint8_t*status)

    {

    staticintrecv_len=0;

    volatileintsta=0,rc=0;

    staticuint8_tcount=0;

    switch(*status){

    caserecv_wait:

    recv_len=network_tcp_s_receive(ctx_tcp._ctx.read_buf,APP_MIN_RECV_LEN);

    if(recv_len<0){

    sta=-1;

    }elseif(recv_len>0){

    (*status)=recv_finsh;

    }

    break;

    caserecv_finsh:

    rc=-1;

    rc=agile_modbus_slave_handle(&ctx_tcp._ctx,recv_len,0,agile_modbus_slave_util_callback,&slave_util,NULL);

    if(rc>0){

    printf("parse ok, recv master msg len:%d\n",recv_len);

    network_tcp_s_send(ctx_tcp._ctx.send_buf,rc);

    (*status)=send_finsh;

    }else{

    printf("parse failed code:%d\n",rc);

    sta=-3;

    }

    break;

    casesend_finsh:

    count++;

    if(count>TIEMOUT_COUNT){

    count=0;

    (*status)=recv_wait;

    }

    break;

    default:

    break;

    }

    returnsta;

    }

    main函数添加modbus tcp rtu数据帧处理流程

    printf("modbus tcp slave example\n");

    printf("= 寄存器映射表 =\n");

    printf("0x0000: LED控制寄存器 (0=关闭, 1=开启)\n");

    printf("0x0001: 温度寄存器 (0.1°C, 250=25.0°C)\n");

    printf("0x0002: 湿度寄存器 (0.1%%, 650=65.0%%)\n");

    printf("0x0003: 真空度寄存器 (0.01 Pa, 101325=1013.25 Pa)\n");

    printf("0x0004: 气压寄存器 (0.1 hPa, 1013=1013.0 hPa)\n");

    printf("0x0005: 电机转速寄存器 (RPM, 1500=1500 RPM)\n");

    printf("0x0006: 电机位置寄存器 (0.1°, 1800=180.0°)\n");

    printf("0x0007: 传感器状态寄存器 (0x00FF=正常)\n");

    printf("0x0008: 报警状态寄存器 (0x0000=无报警)\n");

    printf("0x0009: 系统状态寄存器 (0x0001=正常)\n");

    printf("================\n");

    agile_modbus_tcp_init(&ctx_tcp,ctx_send_buf,sizeof(ctx_send_buf),ctx_read_buf,sizeof(ctx_read_buf));

    agile_modbus_set_slave(ctx,1);

    while(1){

    if(time_flag true){

    time_flag=false;

    if(modbus_slave_process((uint8_t*)&seria_status)<0){

    seria_status=recv_wait;

    }

    }

    enet_common_handler(&gnetif);

    }

    4.6 modpoll

    以LED控制为例,循环发送控制命令,让LED循环点亮

    9ffd1928-07c7-11f1-96ea-92fbcf53809c.png

    读取所有寄存器

    ./modpoll-m tcp-a1-r10-c1-t4-p5001192.168.55.10

    监控温度变化

    ./modpoll-m tcp-a1-r1-c1-t4-p5001-0-l2000192.168.55.10

    监控传感器数据

    ./modpoll-m tcp-a1-r6-c1-t4-p5001-0-l2000192.168.55.10

    4.7 实验现象

    基于保持寄存器控制LED循环点亮

    4.8 总结

    modbus设备控制和传感器数据寄存器映射表

    a02a05a0-07c7-11f1-96ea-92fbcf53809c.png

    4.9 开源

    完整代码已上传Gitee:https://gitee.com/hywing/hpm5e00evk

    5、motor电机步进控制

    5.1 介绍

    步进电机因其精确的位置控制、开环操作简单、响应快和低成本等特性,被广泛应用于需要精准运动控制的领域;先楫HPM5E00EVK开发板上有专用的电机控制引脚,它的电机控制系统包括:

    2 个 8 通道 PWM 模块 PWMV2, 16 路 PWM 输出调制精度可达 100ps,支持产生互补 PWM 输出,死区插入和故障保护

    2 个正交编码器输入 QEIV2

    2 个正交编码器输出 QEOV2

    1 个 Σ∆ 信号接收单元 SDM,内置 4 通道 SINC 数字滤波器

    1 个可编程逻辑模块 PLB

    1 个互联管理器 TRGM

    各模块支持通过互联管理器 TRGM 与电机控制系统内部或外部的模块交互

    1 个同步定时器 SYNT,用于电动控制系统同步

    5.2 原理图

    我们用MOTOR_IF排针的PD0和PD1,分别用于生成PWM和步进电机状态使能

    a0613b88-07c7-11f1-96ea-92fbcf53809c.png

    另外一个方向使能引脚PC8位于PPI插槽

    a09346be-07c7-11f1-96ea-92fbcf53809c.png

    5.3 步进电机

    PUL+连接PD0,DIR连接PC8,ENA+连接PD1

    a0d2dc2a-07c7-11f1-96ea-92fbcf53809c.png

    5.4 驱动程序

    PWM

    初始化PWM引脚:仅配置PD0

    voidinit_pwm_pins_custom(PWMV2_Type*ptr)

    {

    if(ptr==HPM_PWM0){

    HPM_IOC->PAD[IOC_PAD_PD00].FUNC_CTL=IOC_PD00_FUNC_CTL_PWM0_P_0;

    printf("PD0 configured as PWM0_P_0\n");

    }

    }

    配置PWM生成方波

    freq=clock_get_frequency(PWM_CLOCK_NAME);

    reload=freq/1000*PWM_PERIOD_IN_MS-1;

    // 生成1kHz方波,占空比50%

    generate_1khz_50_percent_duty_waveform();

    PWM参数配置:生成1kHz方波,占空比50%

    voidgenerate_1khz_50_percent_duty_waveform(void)

    {

    uint32_tfreq;

    uint32_treload_1khz;

    // 获取PWM时钟频率

    freq=clock_get_frequency(PWM_CLOCK_NAME);

    // 计算1kHz频率的重载值

    reload_1khz=freq/PWM_FREQ_1KHZ-1;

    printf("PWM Clock Frequency: %d Hz\n",freq);

    printf("1kHz PWM Reload Value: %d\n",reload_1khz);

    // 初始化PWM

    pwmv2_deinit(PWM);

    pwmv2_shadow_register_unlock(PWM);

    // 设置周期值(重载值)

    pwmv2_set_shadow_val(PWM,PWMV2_SHADOW_INDEX(0),reload_1khz,0,false);

    // 设置比较值(50%占空比)

    pwmv2_set_shadow_val(PWM,PWMV2_SHADOW_INDEX(1),reload_1khz/2,0,false);

    // 配置计数器

    pwmv2_counter_select_data_offset_from_shadow_value(PWM,pwm_counter_0,PWMV2_SHADOW_INDEX(0));

    pwmv2_counter_burst_disable(PWM,pwm_counter_0);

    pwmv2_set_reload_update_time(PWM,pwm_counter_0,pwm_reload_update_on_reload);

    // 配置比较器

    pwmv2_select_cmp_source(PWM,PWMV2_CMP_INDEX(0),cmp_value_from_shadow_val,PWMV2_SHADOW_INDEX(1));

    // 锁定阴影寄存器

    pwmv2_shadow_register_lock(PWM);

    // 禁用四比较器模式

    pwmv2_disable_four_cmp(PWM,BOARD_APP_PWM_OUT1);

    // 启用PWM通道0输出

    pwmv2_channel_enable_output(PWM,BOARD_APP_PWM_OUT1);

    // 启用计数器并开始PWM输出

    pwmv2_enable_counter(PWM,pwm_counter_0);

    pwmv2_start_pwm_output(PWM,pwm_counter_0);

    printf("1kHz PWM with 50%% duty cycle started on channel 0 (PD00)\n");

    printf("Waveform will continue running...\n");

    }

    控制引脚

    方向和使能引脚配置

    init_dir_pin();// 初始化PC8为GPIO输出,设置为高电平

    init_ena_pin();// 初始化PD1为GPIO输出,设置为高电平

    初始化PC8为GPIO输出,设置为高电平

    voidinit_dir_pin(void)

    {

    // 配置PC8为GPIO输出

    HPM_IOC->PAD[IOC_PAD_PC08].FUNC_CTL=IOC_PC08_FUNC_CTL_GPIO_C_08;

    // 设置PAD控制:上拉使能,上拉,施密特触发器使能

    uint32_tpad_ctl=IOC_PAD_PAD_CTL_PE_SET(1)|IOC_PAD_PAD_CTL_PS_SET(1)|IOC_PAD_PAD_CTL_HYS_SET(1);

    HPM_IOC->PAD[IOC_PAD_PC08].PAD_CTL=pad_ctl;

    // 设置为输出模式,初始电平为低

    HPM_GPIO0->OE[GPIO_DI_GPIOC].SET=(1UL<<8);

    HPM_GPIO0->DO[GPIO_DI_GPIOC].CLEAR=(1UL<<8);

    printf("PC8 configured as DIR pin, set to LOW level\n");

    }

    初始化PD1为GPIO输出,设置为高电平(ENA功能)

    voidinit_ena_pin(void)

    {

    // 配置PD1为GPIO输出

    HPM_IOC->PAD[IOC_PAD_PD01].FUNC_CTL=IOC_PD01_FUNC_CTL_GPIO_D_01;

    // 设置PAD控制:上拉使能,上拉,施密特触发器使能

    uint32_tpad_ctl=IOC_PAD_PAD_CTL_PE_SET(1)|IOC_PAD_PAD_CTL_PS_SET(1)|IOC_PAD_PAD_CTL_HYS_SET(1);

    HPM_IOC->PAD[IOC_PAD_PD01].PAD_CTL=pad_ctl;

    // 设置为输出模式,初始电平为低

    HPM_GPIO0->OE[GPIO_DI_GPIOD].SET=(1UL<<1);

    HPM_GPIO0->DO[GPIO_DI_GPIOD].CLEAR=(1UL<<1);

    printf("PD1 configured as ENA pin, set to LOW level\n");

    }

    5.5 状态显示

    初始化LED引脚

    voidboard_init_led_pins(void)

    {

    init_led_pins();

    gpio_set_pin_output_with_initial(BOARD_LED_GPIO_CTRL,BOARD_LED_GPIO_INDEX,BOARD_LED_GPIO_PIN,board_get_led_gpio_off_level());

    }

    LED亮灭:LED亮表示正转、反转LED灭

    board_led_write(BOARD_LED_ON_LEVEL);

    board_led_write(BOARD_LED_OFF_LEVEL);

    5.6 测试用例

    配置PWM,步进电机转10s,停下来,反转10s又停下来,循环往复

    intmain(void)

    {

    uint32_tfreq;

    board_init();

    init_pwm_pins_custom(PWM);

    init_pwm_fault_pins();

    init_dir_pin();// 初始化PC8为GPIO输出,设置为高电平

    init_ena_pin();// 初始化PD1为GPIO输出,设置为高电平

    board_init_led_pins();// 初始化LED引脚

    printf("PWM 1kHz 50%% Duty Cycle Example\n");

    printf("Using PWM Channel 0 (PD00)\n");

    printf("Using PC8 as DIR pin (LOW level)\n");

    printf("Using PD1 as ENA pin (LOW level)\n");

    printf("LED will blink every 0.5 seconds\n");

    freq=clock_get_frequency(PWM_CLOCK_NAME);

    reload=freq/1000*PWM_PERIOD_IN_MS-1;

    printf("PWM Clock Frequency: %d Hz\n",freq);

    printf("PWM Period: %d ms\n",PWM_PERIOD_IN_MS);

    // 生成1kHz方波,占空比50%

    generate_1khz_50_percent_duty_waveform();

    printf("PWM output started. Check PD00 pin with oscilloscope.\n");

    printf("Expected: 1kHz square wave with 50%% duty cycle\n");

    printf("PC8 DIR pin is set to LOW level\n");

    printf("PD1 ENA pin is set to LOW level\n");

    printf("LED blinking started...\n");

    // 保持程序运行

    while(1){

    // 第一阶段:电机正转10秒

    printf("= Phase 1: Motor forward rotation for 10 seconds =\n");

    printf("ENA: LOW (Enable), DIR: LOW (Forward), PWM: Running\n");

    printf("LED: ON (Forward direction)\n");

    // ENA置低使能,DIR置低正转

    HPM_GPIO0->DO[GPIO_DI_GPIOD].CLEAR=(1UL<<1);// ENA = LOW

    HPM_GPIO0->DO[GPIO_DI_GPIOC].CLEAR=(1UL<<8);// DIR = LOW

    // LED亮表示正转

    board_led_write(BOARD_LED_ON_LEVEL);// LED亮

    // 运行10秒

    for(inti=0;i<20;i++){// 20 * 500ms = 10秒

    board_delay_ms(500);

    printf("Forward rotation: %d/20 cycles, PWM running, LED ON\n",i+1);

    }

    // 第二阶段:停止3秒

    printf("= Phase 2: Motor stop for 3 seconds =\n");

    printf("ENA: HIGH (Disable), DIR: LOW, PWM: Running\n");

    printf("LED: OFF (Motor stopped)\n");

    // ENA置高停止,LED灭

    HPM_GPIO0->DO[GPIO_DI_GPIOD].SET=(1UL<<1);// ENA = HIGH

    board_led_write(BOARD_LED_OFF_LEVEL);// LED灭

    // 停止3秒

    for(inti=0;i<6;i++){// 6 * 500ms = 3秒

    board_delay_ms(500);

    printf("Motor stopped: %d/6 cycles, LED OFF\n",i+1);

    }

    // 第三阶段:电机反转10秒

    printf("= Phase 3: Motor reverse rotation for 10 seconds =\n");

    printf("ENA: LOW (Enable), DIR: HIGH (Reverse), PWM: Running\n");

    printf("LED: OFF (Reverse direction)\n");

    // ENA置低使能,DIR置高反转

    HPM_GPIO0->DO[GPIO_DI_GPIOD].CLEAR=(1UL<<1);// ENA = LOW

    HPM_GPIO0->DO[GPIO_DI_GPIOC].SET=(1UL<<8);// DIR = HIGH

    // LED灭表示反转

    board_led_write(BOARD_LED_OFF_LEVEL);// LED灭

    // 运行10秒

    for(inti=0;i<20;i++){// 20 * 500ms = 10秒

    board_delay_ms(500);

    printf("Reverse rotation: %d/20 cycles, PWM running, LED OFF\n",i+1);

    }

    // 第四阶段:停止3秒

    printf("= Phase 4: Motor stop for 3 seconds =\n");

    printf("ENA: HIGH (Disable), DIR: HIGH, PWM: Running\n");

    printf("LED: OFF (Motor stopped)\n");

    // ENA置高停止,LED保持灭

    HPM_GPIO0->DO[GPIO_DI_GPIOD].SET=(1UL<<1);// ENA = HIGH

    board_led_write(BOARD_LED_OFF_LEVEL);// LED灭

    // 停止3秒

    for(inti=0;i<6;i++){// 6 * 500ms = 3秒

    board_delay_ms(500);

    printf("Motor stopped: %d/6 cycles, LED OFF\n",i+1);

    }

    printf("= Complete cycle finished, starting next cycle =\n");

    }

    return0;

    }

    5.7 实验现象

    电机正转10s,停止,反转10s,停止;示波器显示1kHz PWM波形,直流稳压电源显示电机的负载情况

    5.8 总结

    工业自动化领域一般通过脉冲数控机械装置电动步进位置,同时利用编码器反馈调整实现闭环控制,来达到精确的位移控制;常见的自动化控制设备有:真空晶圆传送、上下取片放料装置、机械手、电动托盘、激光校准器等

    5.9 开源

    完整代码已上传Gitee:https://gitee.com/hywing/hpm5e00evk

    视频链接:

    https://www.bilibili.com/video/BV19Zb3z5E5A/?t=0.0&vd_source=a5f350afc9fad8cf1c9fb8dc14248456

    文章来源:EEFocus

    开发者ID:eefocus_4139406

    原文链接:

    https://www.eefocus.com/forum/home.php?mod=space&uid=380063&do=thread&view=me&from=space

    /

    /

    以上内容来自先楫开发者的原创分享。

    我们始终相信开发者共创的力量。先楫社区坚持开源共享、互惠互利,贴近每一个开发者,一步一个脚印,一点一滴积累,为成为更好的我们而不断努力。