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

1.2 原理图
HPM_I2C0对应的GPIO:PC8 -> SCL、PC9 -> SDA,这一组接口位于开发板PPI连接槽
1.3 硬件接口
HT30连接HPM_I2C0接口引脚示意图,注意VCC为3.3V,电源正负极不能接反

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){
}
}
}
读取接口(跟stm32、nxp、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芯片连接到上位机

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

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开发板串口数据波形效果

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

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

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

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℃

万用表测量的温度

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

3.8 开源
完整代码已上传Gitee:https://gitee.com/hywing/hpm5e00evk
4、modbus数据采集和设备控制
4.1 介绍
modbus协议在物联网、工业控制领域应用广泛,常用于数据采集和设备控制;先楫HPM5E00EVK开发板集成了一路通用以太网接口,我们可以利用它来进行modbus tcp rtu通信
4.2 原理图
实验中我们使用RGMII网口进行modbus通信

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

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

读取所有寄存器
./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设备控制和传感器数据寄存器映射表

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

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

5.3 步进电机
PUL+连接PD0,DIR连接PC8,ENA+连接PD1

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
/
完
/
以上内容来自先楫开发者的原创分享。
我们始终相信开发者共创的力量。先楫社区坚持开源共享、互惠互利,贴近每一个开发者,一步一个脚印,一点一滴积累,为成为更好的我们而不断努力。
