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

先楫半导体HPMicro 2026-02-12 12:00

概要

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,为工业、农业、物联网应用提供了高效、便捷的数字量采集方式(下图是我手工焊接的模块)

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

 

1.2 原理图

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

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

 

1.3 硬件接口

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

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

 

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_t config;
hpm_stat_t stat;
uint32_t freq;

     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){
}
}
}

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

hpm_stat_ti2c_master_read(I2C_Type *ptr,constuint16_t device_address,
uint8_t*buf,constuint32_t size)

写入接口

hpm_stat_ti2c_master_write(I2C_Type *ptr,constuint16_t device_address,
uint8_t*buf,constuint32_t size)

HT30

读取HT30数据

staticht30_error_tht30_read_data(uint8_t*data,uint8_t len)
{
if(status_success !=i2c_master_read(TEST_I2C, HT30_I2C_ADDR, data, len)){
printf("HT30 read data failed\n");
return HT30_ERROR_READ;
}

return HT30_OK;
}

发送命令到HT30

staticht30_error_tht30_send_command(uint16_t command)
{
uint8_t cmd_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);
return HT30_ERROR_MEASURE;
}

return HT30_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");
return HT30_ERROR_INIT;
}

// 等待复位完成
board_delay_ms(10);

printf("HT30 sensor initialized successfully\n");
return HT30_OK;
}

读取HT30温湿度数据

ht30_error_tht30_read_temperature_humidity(ht30_data_t*data)
{
uint8_t rx_data[6];
uint16_t temp_raw, hum_raw;
uint8_t temp_crc, hum_crc;

// 发送测量命令(高精度)
if(ht30_send_command(HT30_CMD_MEASURE_HIGH)!= HT30_OK){
printf("HT30 measurement command failed\n");
return HT30_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");
return HT30_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]);
return HT30_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]);
return HT30_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);

return HT30_OK;
}

读取HT30状态寄存器

ht30_error_tht30_read_status(uint8_t*status)
{
uint8_t rx_data[3];

// 发送状态读取命令
if(ht30_send_command(HT30_CMD_STATUS)!= HT30_OK){
printf("HT30 status command failed\n");
return HT30_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");
return HT30_ERROR_READ;
}

// 验证CRC
uint8_t status_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]);
return HT30_ERROR_CHECKSUM;
}

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

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

staticuint8_tht30_calculate_crc(uint8_t*data,uint8_t len)
{
uint8_t crc =0xFF;
uint8_t i, j;

for(=0; i < len; i++){
         crc = data[i];
for(=0; j <8; j++){
if(crc &0x80){
                 crc =(crc <<1)0x31;
}else{
                 crc = crc <<1;
}
}
}
return crc;
}

检查HT30传感器是否响应

ht30_error_tht30_check_connection(void)
{
uint8_t status;
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_t status;
if(ht30_read_status(&status)== HT30_OK){
printf("HT30 Status: 0x%02X\n", status);
}

ht30_data_t sensor_data;
uint32_t measurement_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 实验现象

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

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

 

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芯片连接到上位机

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

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

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

 

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_t config ={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_t irq_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_t i =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,...)
{
char buffer[128];
     va_list args;
va_start(args, format);
int len =vsnprintf(buffer,sizeof(buffer), format, args);
va_end(args);

if(len >0&& len <sizeof(buffer)){
printf_interrupt_send(buffer, len);
}
}

printf中断发送函数

uint8_t buff_index;
uint8_t data_count;
 ATTR_PLACE_AT_NONCACHEABLE uint8_t data_buff[TEST_UART_MAX_BUFFER_SIZE];

// printf中断发送相关变量
volatile bool printf_busy = false;
volatileuint16_t printf_write_index =0;
volatileuint16_t printf_read_index =0;
 ATTR_PLACE_AT_NONCACHEABLE uint8_t printf_buffer[PRINTF_BUFFER_SIZE];

// printf中断发送函数
voidprintf_interrupt_send(constchar*str,int len)
{
for(int i =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)
{
staticfloat angle =0.0f;
float sinVal, 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开发板串口数据波形效果

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

 

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

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

 

3.3 热敏电阻

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

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

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

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

 

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_t freq =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);
}

return freq;
}

读取ADC值

uint16_t result;
float temperature;
float voltage;

adc16_get_prd_result(BOARD_APP_ADC16_BASE, BOARD_APP_ADC16_CH_1,&result);

 

3.5 测试用例

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

staticconsttemp_lookup_entry_t temp_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_t adc_value)
{
/* Handle edge cases */
if(adc_value <= temp_lookup_table[0].adc_value){
return temp_lookup_table[0].temperature;
}
if(adc_value >= temp_lookup_table[TEMP_LOOKUP_TABLE_SIZE -1].adc_value){
return temp_lookup_table[TEMP_LOOKUP_TABLE_SIZE -1].temperature;
}

/* Find the correct temperature range */
for(int i =1; i < TEMP_LOOKUP_TABLE_SIZE; i++){
if(adc_value <= temp_lookup_table[i].adc_value){
/* Linear interpolation between two points */
uint16_t adc_low = temp_lookup_table[i-1].adc_value;
uint16_t adc_high = temp_lookup_table[i].adc_value;
float temp_low = temp_lookup_table[i-1].temperature;
float temp_high = temp_lookup_table[i].temperature;

float ratio =(float)(adc_value - adc_low)/(float)(adc_high - adc_low);
return temp_low + ratio *(temp_high - temp_low);
}
}

/* Should not reach here */
return temp_lookup_table[TEMP_LOOKUP_TABLE_SIZE -1].temperature;
}

周期性处理ADC温度值

voidperiod_handler(void)
{
uint16_t result;
float temperature;
float voltage;

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℃

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

万用表测量的温度

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

 

3.7 总结

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

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

 

3.8 开源

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

 

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

 

4.1 介绍

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

 

4.2 原理图

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

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

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

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

 

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_t sta = 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);
}
return sta;
}

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_t value)
{
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_t temp_variation =(rand()%200)-100;/* -10.0°C 到 +10.0°C */
int32_t new_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_t hum_variation =(rand()%300)-150;/* -15.0% 到 +15.0% */
int32_t new_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_t vac_variation =(rand()%200)-100;/* ±100 Pa */
int32_t new_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_t atm_variation =(rand()%300)-150;/* ±15 hPa */
int32_t new_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_t speed_variation =(rand()%200)-100;/* ±100 RPM */
int32_t new_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,int bufsz)
{
(void)bufsz;
uint16_t*ptr =(uint16_t*)buf;

/* 更新传感器数据 */
update_sensor_data();

for(uint32_t i =0; i <sizeof(_tab_registers)/sizeof(_tab_registers[0]); i++){
         ptr[i]= _tab_registers[i];
}
return0;
}

staticintset_map_buf(int index,int len,void*buf,int bufsz)
{
(void)bufsz;
uint16_t*ptr =(uint16_t*)buf;
for(int i =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_t hold_register_maps[1]={
{0x00, REGISTER_COUNT -1, get_map_buf, set_map_buf}};

 

4.5 测试用例

添加寄存器访问接口

staticintmodbus_slave_process(uint8_t*status)
{
staticint recv_len =0;
volatileint sta =0, rc =0;
staticuint8_t count =0;
switch(*status){
case recv_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;
case recv_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;
case send_finsh:
         count++;
if(count > TIEMOUT_COUNT){
             count =0;
(*status)= recv_wait;
}
break;
default:
break;
}
return sta;
}

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循环点亮

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

读取所有寄存器

./modpoll -m tcp -1-10-1-4-5001192.168.55.10

监控温度变化

./modpoll -m tcp -1-1-1-4-5001-0-2000192.168.55.10

监控传感器数据

./modpoll -m tcp -1-6-1-4-5001-0-2000192.168.55.10

 

4.7 实验现象

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

 

4.8 总结

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

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

 

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和步进电机状态使能

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

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

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

 

5.3 步进电机

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

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

 

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_t freq;
uint32_t reload_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_t pad_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_t pad_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_t freq;
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(int i =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(int i =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(int i =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(int i =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

 

 

/

/

声明:内容取材于网络,仅代表作者观点,如有内容违规问题,请联系处理。 
控制系统
more
2025年汽车电子控制系统发展趋势及市场规模
民航局发布《基于计算机的民用无人驾驶航空器运行控制系统申请和批准(征求意见稿)》
实现宇树G1连续106次乒乓球对打!UC伯克利Sastry教授团队提出分层规划控制系统HITTER
重磅!华经产业研究院发布《2025年中国激光振镜控制系统行业市场深度研究报告》
严厉打击黑飞!公安部公布3起非法破解无人机飞行控制系统黑客违法犯罪典型案例
华成工控:深耕具身小脑技术,构建具身智能控制系统框架
《计测技术》推荐文章|航空工业计量所王江萍:高精度激光自聚焦控制系统的设计与实现
永磁无刷高速电机、控制系统及电池包扩能建设项目可行性研究报告
开发者分享 | 构建完整控制系统:HPM5E00 外设综合应用
发布 | 飞机环境控制系统市场与供应链分析报告(2025版)
Copyright © 2025 成都区角科技有限公司
蜀ICP备2025143415号-1
  
川公网安备51015602001305号