空耳-

  • 2025-03-26
  • 回复了主题帖: 【先楫HPM5361】ADC篇-获取温湿度

    这个不是专业仪器测的话,应该很难察觉出来吧

  • 2025-03-24
  • 发表了主题帖: 【先楫HPM5361】DAC篇

    本次dome主要是通过dac输出一个锯齿波,然后通过adc去采集,然后串口打印出来。 代码还是通过官方的sdk,进行修改,完成的。 本次使用到的管脚如下: dac_Pin:PB08 adc_pin:PB05 核心代码: /*先楫HPM5361EVK的16位ADC共支持四种采样模式,分别是读取转换模式、周期转换模式、序列转换模式和抢占转换模式。这次使用的是读取转换模式,读取 BUS_RESULTx 寄存器将触发对 ADC 输入 x 的一次转换。一旦转换完成,结果将直接返回。根据不同的 ADC 通道配置,返回转换结果的时间也会有所不同。*/ static void Digital_Ctrl(void) { printf("Set DAC to output data in direct mode\n"); printf("DAC is outputting a triangle waveform in direct mode\n"); init_oneshot_config(); while(1) { channel_result_out_of_threshold_handler(); /* triangle waveform */ for (int32_t j = 0; j < 5000; j++) { for (float i = 0; i <= 10000; i += 2.5) { /* NOLINT */ dac_set_direct_config(BOARD_DAC_BASE, (uint16_t)DAC_OUTPUT(i)); board_delay_us(1); oneshot_handler(); } for (float i = 10000; i >= 0; i -= 2.5) { /* NOLINT */ dac_set_direct_config(BOARD_DAC_BASE, (uint16_t)DAC_OUTPUT(i)); board_delay_us(1); oneshot_handler(); } } if (abort_handler(1)) { break; } } } int main(void) { uint8_t output_mode; /* Initialize Bsp */ board_init(); /* Set a log title */ printf("This is a DAC demo:\n"); /* Initialize a DAC clock */ board_init_dac_clock(BOARD_DAC_BASE, false); /* Initialize a DAC pin */ board_init_dac_pins(BOARD_DAC_BASE); /************************************空耳***********************************/ /* ADC pin initialization */ board_init_adc16_pins(); /* ADC clock initialization */ board_init_adc_clock(BOARD_APP_ADC16_BASE, true); printf("This is an ADC16 demo:\n"); /************************************空耳***********************************/ while (1) { /* Get the output mode from a console */ output_mode = get_dac_mode(); /* Initialize a DAC peripheral with a common config */ init_common_config(output_mode); /* ADC16 common initialization */ adc_init_common_config(output_mode); /* Enable the DAC IRQ */ intc_m_enable_irq(BOARD_DAC_IRQn); /* Enable DAC conversion */ dac_enable_conversion(BOARD_DAC_BASE, true); /* Set a specified DAC output config */ switch (output_mode) { case dac_mode_direct: //set_direct_mode_config(); //init_oneshot_config(); Digital_Ctrl(); break; case dac_mode_step: set_step_mode_config(); break; case dac_mode_buffer: set_buffer_mode_config(); break; case 4 : break; default: break; } } 结果演示:   总结 先辑的sdk是真的好用,还相当的齐全。其实基于这个还可以实现一个简易的数控电压源。 dac控制开关电源芯片的FB引脚的电压(可以添加一个电压跟随器,提高驱动能力),adc去采集输出电压,串口设置输出电压,pid闭环控制。

  • 2025-03-23
  • 发表了主题帖: 【先楫HPM5361】ADC篇-获取温湿度

    一,前言 本次实例为通过ADC获取温湿度,该说不说16位的ad我还是第一次用。本次使用的温湿度传感器为SHT30,这是一个 模拟温湿度传感器(±3%)。 原理图:   这里面最重要的是这两个对应关系:       本次使用到的adc引脚分别是PB0与PB11   ADC配置如下:   二,核心代码 /* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date           Author       Notes * 2025-03-03     Administrator       the first version */ #include <rtthread.h> #include <rtdevice.h> #include "rtt_board.h" #ifdef BSP_USING_ADC12 #include "hpm_adc12_drv.h" #endif #ifdef BSP_USING_ADC16 #include "hpm_adc16_drv.h" #endif #define ADC_DEV_NAME                "adc0"      /* ADC 设备名称 */ #define ADC_TEMP_CHANNEL            2           /* ADC 通道 */ #define ADC_HUMIDITY_CHANNEL        3           /* ADC 通道 */ #define REFER_VOLTAGE               330         /* 参考电压 3.3V,数据精度乘以100保留2位小数*/ #ifdef BSP_USING_ADC16 #define CONVERT_BITS        (1 << 16) /*16 bit*/ #endif #ifdef BSP_USING_ADC12 #define CONVERT_BITS        (1 << 12) /*162 bit*/ #endif static int adc_vol_sample(int argc, char *argv[]) {     rt_adc_device_t adc_temp,adc_humidity;     rt_uint32_t value_temp, vol_temp,value_humidity,vol_humidity;     float temp,humidity;     rt_err_t ret = RT_EOK;     /* 查找设备 */     adc_temp = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);     adc_humidity = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);     if (adc_temp == RT_NULL || adc_humidity == RT_NULL)     {         rt_kprintf("adc sample run failed! can't find %s device!\n", ADC_TEMP_CHANNEL);         return RT_ERROR;     }     /* 使能设备 */     ret = rt_adc_enable(adc_temp, ADC_TEMP_CHANNEL);     if (ret != RT_EOK) {         rt_kprintf("Failed to enable ADC temperature channel: %d\n", ret);         return ret; // 返回错误或适当处理     }     ret = rt_adc_enable(adc_humidity, ADC_HUMIDITY_CHANNEL);     if (ret != RT_EOK) {         rt_kprintf("Failed to enable ADC humidity channel: %d\n", ret);         return ret; // 返回错误或适当处理     }     while(1)     {         /* 读取采样值 */         value_temp = rt_adc_read(adc_temp, ADC_TEMP_CHANNEL);         value_humidity = rt_adc_read(adc_humidity, ADC_HUMIDITY_CHANNEL); //        rt_kprintf("the value is :%d \n", value);         /* 转换为对应电压值 */         vol_temp = value_temp * REFER_VOLTAGE / CONVERT_BITS;         vol_humidity = value_humidity * REFER_VOLTAGE / CONVERT_BITS; //        printf("%d,%d \n", vol_temp,vol_humidity);         humidity = -12.50 + 125.00*(vol_humidity/330.00);         temp = -66.875+218.75*(vol_temp / 330.00); //        humidity = -(10.0 / 0.8) + (100.0 / 0.8)*((float)vol_humidity/330.00); //        temp = -45.0-(31.5 / 0.8) + (315.0/ 0.8)*((float)vol_temp / 330.00);         printf("%.2f,%.2f \n", temp,humidity);         rt_thread_mdelay(500);     }     /* 关闭通道 */     ret = rt_adc_disable(adc_temp, ADC_TEMP_CHANNEL);     ret = rt_adc_disable(adc_humidity, ADC_HUMIDITY_CHANNEL);     return ret; } /* 导出到 msh 命令列表中 */ //MSH_CMD_EXPORT(adc_vol_sample, adc voltage convert sample); // 导出函数自动运行,在系统初始化时调用usr_led_run函数 INIT_APP_EXPORT(adc_vol_sample); 三,实例演示   将手放上去会有明显的湿度值上升:   四,问题记录 就这个问题,就是死活找不到定义,主要还是自己对目录结构不是很熟悉,在这里记录一下。                   右键资源配置----》添加构建。 最后还是推荐不要使用这种模拟量的温湿度传感器,可能因为走线或者焊接的问题,测量的会很不准确。

  • 2025-03-22
  • 回复了主题帖: 【2024 DigiKey大赛参与奖】开箱帖

    jobszheng5 发表于 2025-3-22 19:32 这个看着好漂亮,好cute啊 确实好看

  • 发表了主题帖: 【2024 DigiKey大赛参与奖】开箱帖

    非常荣幸参见本次【2024 DigiKey 创意大赛】,并在本次活动中获得参与奖。这一次购买的板卡是 M5STACK家的Roller 485。 还是熟悉的外包装:     好久都没见过纸箱包装了。 下面就是本次参与奖购买的板卡,相当的精致。      虽然无刷电机自己组装一个成本也就100多,但是这个是真的很精致。   最后再次感谢EEWorld和得捷电子。

  • 2025-03-19
  • 回复了主题帖: 大家都用上RISCV的芯片了吗

    还挺多,先辑的hpm5300,还有很多串口屏,都用的·risc-v的

  • 2025-03-18
  • 回复了主题帖: 【先楫HPM5361】CAN篇-汽车ODB数据获取及可视化显示

    确实

  • 2025-03-17
  • 发表了主题帖: 【先楫HPM5361】CAN篇-汽车ODB数据获取及可视化显示

    通过这几天的不屑努力,终于将CAN跑通了,接下来是一个简单的dome。读取汽车的ODB数据,并显示。 这一次使用的开发环境不是RT-Thread Studio了,而是SEGGER Embedded Studio ,这个软件上手还挺快,不过我就是找不到下载在那,每次都是把调试当作下载用。 这里SEGGER Embedded Studio开发环境就不再zhuisu了,和KEIKL还是比较接近的。 这里只获取的汽车的一些基础数据,车速,转速,水温,电压,之内的。主要采用的是IS0-15765协议,500k的速率,标准帧。 核心代码,是在官方sdk上修改了一下,官方的sdk还是给的相当的齐全的。 核心代码: void board_can_isr(void) {     MCAN_Type *base = BOARD_APP_CAN_BASE;     uint32_t flags = mcan_get_interrupt_flags(base);     /* New message is available in RXFIFO0 */     if ((flags & MCAN_INT_RXFIFO0_NEW_MSG) != 0) {         mcan_read_rxfifo(base, 0, (mcan_rx_message_t *) &s_can_rx_buf);         has_new_rcv_msg = true;         rxfifo0_event_occurred = true;     }     /* New message is available in RXFIFO1 */     if ((flags & MCAN_INT_RXFIFO1_NEW_MSG) != 0U) {         mcan_read_rxfifo(base, 1, (mcan_rx_message_t *) &s_can_rx_buf);         has_new_rcv_msg = true;         rxfifo1_event_occurred = true;     }     /* New message is available in RXBUF */     if ((flags & MCAN_INT_MSG_STORE_TO_RXBUF) != 0U) {         has_new_rcv_msg = true;         /* NOTE: Below code is for demonstration purpose, the performance is not optimized          *       Users should optimize the performance according to real use case.          */         for (uint32_t buf_index = 0; buf_index < MCAN_RXBUF_SIZE_CAN_DEFAULT; buf_index++) {             if (mcan_is_rxbuf_data_available(base, buf_index)) {                 mcan_read_rxbuf(base, buf_index, (mcan_rx_message_t *) &s_can_rx_buf);                 mcan_clear_rxbuf_data_available_flag(base, buf_index);             }         }     }     /* New TX Event occurred */     if ((flags & MCAN_INT_TX_EVT_FIFO_NEW_ENTRY) != 0) {         mcan_read_tx_evt_fifo(BOARD_APP_CAN_BASE, (mcan_tx_event_fifo_elem_t *) &s_can_tx_evt);         tx_event_occurred = true;     }     /* Transmit completed */     if ((flags & MCAN_EVENT_TRANSMIT) != 0U) {         has_sent_out = true;     }     /* Error happened */     if ((flags & MCAN_EVENT_ERROR) != 0) {         has_error = true;     }     if ((flags & MCAN_INT_TIMEOUT_OCCURRED) != 0) {         timeout_event_occurred = true;     }     mcan_clear_interrupt_flags(BOARD_APP_CAN_BASE, flags); } /******************************************************************************************/ #define PID_ENGINE_LOAD 0x04 #define PID_COOLANT_TEMP 0x05                           //表示发动机冷却液的温度,通常以摄氏度(°C)为单位,是判断发动机温度状态的重要参数。 #define PID_SHORT_TERM_FUEL_TRIM_1 0x06 #define PID_LONG_TERM_FUEL_TRIM_1 0x07 #define PID_SHORT_TERM_FUEL_TRIM_2 0x08 #define PID_LONG_TERM_FUEL_TRIM_2 0x09 #define PID_FUEL_PRESSURE 0x0A #define PID_INTAKE_MAP 0x0B #define PID_RPM 0x0C #define PID_SPEED 0x0D #define PID_TIMING_ADVANCE 0x0E #define PID_INTAKE_TEMP 0x0F #define PID_MAF_FLOW 0x10 #define PID_THROTTLE 0x11 #define PID_AUX_INPUT 0x1E #define PID_RUNTIME 0x1F #define PID_DISTANCE_WITH_MIL 0x21 #define PID_COMMANDED_EGR 0x2C #define PID_EGR_ERROR 0x2D #define PID_COMMANDED_EVAPORATIVE_PURGE 0x2E #define PID_FUEL_LEVEL 0x2F #define PID_WARMS_UPS 0x30 #define PID_DISTANCE 0x31 #define PID_EVAP_SYS_VAPOR_PRESSURE 0x32 #define PID_BAROMETRIC 0x33 #define PID_CATALYST_TEMP_B1S1 0x3C #define PID_CATALYST_TEMP_B2S1 0x3D #define PID_CATALYST_TEMP_B1S2 0x3E #define PID_CATALYST_TEMP_B2S2 0x3F #define PID_CONTROL_MODULE_VOLTAGE      0x42 #define PID_ABSOLUTE_ENGINE_LOAD 0x43 #define PID_AIR_FUEL_EQUIV_RATIO 0x44 #define PID_RELATIVE_THROTTLE_POS 0x45 #define PID_AMBIENT_TEMP 0x46 #define PID_ABSOLUTE_THROTTLE_POS_B 0x47 #define PID_ABSOLUTE_THROTTLE_POS_C 0x48 #define PID_ACC_PEDAL_POS_D 0x49 #define PID_ACC_PEDAL_POS_E 0x4A #define PID_ACC_PEDAL_POS_F 0x4B #define PID_COMMANDED_THROTTLE_ACTUATOR 0x4C #define PID_TIME_WITH_MIL 0x4D #define PID_TIME_SINCE_CODES_CLEARED 0x4E #define PID_ETHANOL_FUEL 0x52 #define PID_FUEL_RAIL_PRESSURE 0x59 #define PID_HYBRID_BATTERY_PERCENTAGE 0x5B #define PID_ENGINE_OIL_TEMP 0x5C #define PID_FUEL_INJECTION_TIMING 0x5D #define PID_ENGINE_FUEL_RATE    0x5E #define PID_ENGINE_TORQUE_DEMANDED 0x61 #define PID_ENGINE_TORQUE_PERCENTAGE 0x62 #define PID_ENGINE_REF_TORQUE 0x63 uint8_t PID[]={PID_RPM,PID_SPEED,PID_DISTANCE,PID_CONTROL_MODULE_VOLTAGE,PID_ENGINE_FUEL_RATE,PID_EVAP_SYS_VAPOR_PRESSURE,PID_COOLANT_TEMP}; typedef struct {     uint16_t FH;     uint16_t rpm;                        //表示发动机的转速(RPM),对于监控发动机性能至关重要。     uint16_t speed;                      //时速     short  coolant_temp;               //水温     uint8_t  Control_Module_Voltage;     //控制模块的电压     int Distance;                   //总里程     int Trip_Distance;              //小计里程     short map;                        //进气压力     uint16_t engine_fuel_rate;           //瞬时油耗     uint8_t CHECK_SUM; }ODB_info; ODB_info g_odb_message; void serialize_sensor_data( ODB_info *data, uint8_t *buffer, size_t buffer_size) {       if (buffer_size < sizeof(ODB_info)) {           // Handle error: buffer too small           return;       }       data->FH = 0x5AA5;     data->CHECK_SUM = ( data->rpm + data->speed + data->coolant_temp + data->Control_Module_Voltage + data->Distance + data->Trip_Distance + data->engine_fuel_rate) & 0xff;     memcpy(buffer, data, sizeof(ODB_info));       for(int i = 0;i< sizeof(ODB_info);i++)     {         printf("%c",buffer[i]);     } }   void ODB_Info_analysis(void) {     uint8_t pid,data1,data2;     uint8_t odb_data[sizeof(ODB_info)];     pid  =  s_can_rx_buf.data_8[2];     data1 = s_can_rx_buf.data_8[3];     data2 = s_can_rx_buf.data_8[4];     short value = 0;     switch (pid)     {           case PID_RPM://转速                   value = (data2 | data1 << 8) / 4;                   g_odb_message.rpm = value;                   //printf("PID_RPM %d \n",value);                   break;           case PID_SPEED://车速                   value = data1 ;                   //printf("PID_SPEED :%d\n",value);                   break;           case PID_DISTANCE: // km//表示总行驶里程                   value = (data2 | data1 << 8) ;                   g_odb_message.Distance = value;                   //printf("PID_DISTANCE :%d\n",value);                   break;           case PID_CONTROL_MODULE_VOLTAGE: // 表示控制模块的电压,通常用于监控电源状态。 v                   value = (data2 | data1 << 8) / 1000;                   g_odb_message.Control_Module_Voltage = value;                   //printf("PID_CONTROL_MODULE_VOLTAGE :%d\n",value);                   break;           case PID_ENGINE_FUEL_RATE: // L/h         //表示单位时间内的燃油消耗量(通常以升每小时或加仑每小时为单位)。                   value = (data2 | data1 << 8) / 20;                   g_odb_message.engine_fuel_rate = value;                   //printf("PID_ENGINE_FUEL_RATE :%d\n",value);                   break;           case PID_EVAP_SYS_VAPOR_PRESSURE: // kPa 表示蒸发系统内的气压,通常用于监测油箱的密封性。                   value = (data2 | data1 << 8) / 4;                   g_odb_message.map = value;                   //printf("PID_EVAP_SYS_VAPOR_PRESSURE :%d\n",value);                   break;           case PID_INTAKE_MAP: // 涡轮进气压力                   value =  data1 - 100;                   //printf("PID_EVAP_SYS_VAPOR_PRESSURE :%d\n",value);                   break;           case PID_COOLANT_TEMP: // 水温                   value =  data1 - 40;                   g_odb_message.coolant_temp = value;                   //printf("PID_COOLANT_TEMP :%d\n",value);                   break;           default:                   break;     }     serialize_sensor_data(&g_odb_message,odb_data,sizeof(ODB_info)); } void board_can_rxbuf_test(void) {     MCAN_Type *base = BOARD_APP_CAN_BASE;     mcan_config_t can_config;     mcan_filter_elem_t can_filters[16];     mcan_get_default_config(base, &can_config);     can_config.baudrate = 500000; /* 500kbps */     can_config.mode = mcan_mode_loopback_internal;     board_init_can(base);     uint32_t can_src_clk_freq = board_init_can_clock(base);     uint32_t interrupt_mask = MCAN_EVENT_RECEIVE;     /* NOTE: CAN RXBUF must be used with the CAN Filter together.      *      * In the example, only the message with ID 0x123 is allowed to be stored into RXBUF0, the other messages      *  get ignored by the CAN controller according to the CAN Filter setting      *      */     uint32_t rxbuf_index = 1;     can_filters[0].filter_type = MCAN_FILTER_TYPE_CLASSIC_FILTER;     can_filters[0].filter_config = MCAN_FILTER_ELEM_CFG_STORE_INTO_RX_BUFFER_OR_AS_DBG_MSG;     can_filters[0].can_id_type = MCAN_CAN_ID_TYPE_STANDARD;     can_filters[0].match_id = 0x123;     can_filters[0].offset = rxbuf_index;     can_filters[0].filter_event = 0;     can_filters[0].store_location = 0;     can_config.all_filters_config.ext_id_filter_list.mcan_filter_elem_count = 0;     can_config.all_filters_config.std_id_filter_list.filter_elem_list = &can_filters[0];     can_config.all_filters_config.std_id_filter_list.mcan_filter_elem_count = 1;     can_config.all_filters_config.global_filter_config.accept_non_matching_std_frame_option =         MCAN_ACCEPT_NON_MATCHING_FRAME_OPTION_REJECT;     can_config.all_filters_config.global_filter_config.accept_non_matching_ext_frame_option =         MCAN_ACCEPT_NON_MATCHING_FRAME_OPTION_REJECT;     can_config.all_filters_config.global_filter_config.reject_remote_std_frame = true;     can_config.all_filters_config.global_filter_config.reject_remote_ext_frame = true;     can_config.txbuf_trans_interrupt_mask = ~0UL;     can_config.interrupt_mask = interrupt_mask;     hpm_stat_t status = mcan_init(base, &can_config, can_src_clk_freq);     if (status != status_success) {         printf("CAN initialization failed, error code: %d\n", status);         return;     }     intc_m_enable_irq_with_priority(BOARD_APP_CAN_IRQn, 1);     mcan_tx_frame_t tx_buf;     memset(&tx_buf, 0, sizeof(tx_buf));     tx_buf.dlc = 8;     for (uint32_t i = 0; i < 8; i++) {         tx_buf.data_8[i] = (uint8_t) i | (i << 4);     }     for (uint32_t i = 0; i < 2048; i++) {         tx_buf.std_id = i;         /* Demonstrate Transmit via TXBUF in non-blocking way here          */         uint32_t buf_index = 2;         mcan_transmit_via_txbuf_nonblocking(base, buf_index, &tx_buf);         while (!mcan_is_transmit_occurred(base, buf_index)) {         }     }     bool test_pass = false;     /* If below condition was met, it means this test case passed */     if (has_new_rcv_msg) {         has_new_rcv_msg = false;         has_sent_out = false;         printf("New message received, ID=%08x\n", s_can_rx_buf.std_id);         if (s_can_rx_buf.std_id == 0x123) {             test_pass = true;         }     }     printf("%s %s\n", __func__, test_pass ? "PASSED" : "FAILED"); } 成果演示 [localvideo]fbbea330f25cfd97c56c3f00d36f55a7[/localvideo] [localvideo]6ef3fbf80b52471fd6749c34cdf4cd59[/localvideo] 简单的上位机界面   这个界面只写了,转速,其他的还没有往上添加。  

  • 2025-03-10
  • 回复了主题帖: 《Hello算法》哈希表学习之一——理论学习

    本质是空间换时间吗,我记得前段时间有一个电子档的

  • 2025-03-08
  • 回复了主题帖: 在STM32F103上移植MultiButton按键,但是没有实现

    用你代码注释的那部分可以,        button_attach(&button1, SINGLE_CLICK,     button_callback);                         button_attach(&button1, SINGLE_CLICK,     button_callback);,这两个都是同一个单机信号,你得换一个

  • 2025-03-06
  • 回复了主题帖: 先楫HPM5361 can通讯回环模式正常,正常模式死活不通,麻烦各位大佬帮我看一下

    lugl4313820 发表于 2025-3-6 17:07 这两个都不是。  《RISC-V 开放架构设计之道》- RISC-V开放架构设计简读 - 编程基础 - 电子工程 ... 好的,谢谢

  • 回复了主题帖: 先楫HPM5361 can通讯回环模式正常,正常模式死活不通,麻烦各位大佬帮我看一下

    lugl4313820 发表于 2025-3-6 16:20 你找一下@RSCN大佬,他是这方面的专家,专玩这个板子的。 佬是那一个呀  

  • 回复了主题帖: 先楫HPM5361 can通讯回环模式正常,正常模式死活不通,麻烦各位大佬帮我看一下

    lugl4313820 发表于 2025-3-6 16:20 你找一下@RSCN大佬,他是这方面的专家,专玩这个板子的。 好的,好的,我就知道版主最有办法了

  • 回复了主题帖: 先楫HPM5361 can通讯回环模式正常,正常模式死活不通,麻烦各位大佬帮我看一下

    damiaa 发表于 2025-3-6 10:25 回环模式是内部测试,正常模式要接外部can总线。   说明can总线接口方面还有问题吧。 总线应该是通的,好像没有数据出来,他数据没发送完就卡死了

  • 回复了主题帖: 先楫HPM5361 can通讯回环模式正常,正常模式死活不通,麻烦各位大佬帮我看一下

    Jacktang 发表于 2025-3-6 07:26 正常模式死活不通,这个与代码有关? 不知道啊,吧正常模式改为回环模式就又可以了, 我与另一个can设备之间没有接地 还有就是我是电脑usb供电的,不知道会不会供电不足  

  • 发表了主题帖: 先楫HPM5361 can通讯回环模式正常,正常模式死活不通,麻烦各位大佬帮我看一下

    本帖最后由 空耳- 于 2025-3-6 12:38 编辑 先楫HPM5361  can通讯回环模式正常,正常模式死活不通,麻烦各位大佬帮我看一下 /* * Copyright (c) 2022-2023 HPMicro * * SPDX-License-Identifier: BSD-3-Clause * * Change Logs: * Date Author Notes * 2022-05-08 hpmicro the first version * 2023-05-08 hpmicro Adapt the RT-Thread V5.0.0 */ /***************************************************************************************** * * CAN Example * * This demo works in internal loopback mode. It demonstrates following functions: * * 1. CAN transmission and reception with standard ID * 2. CAN transmission and reception with extended ID * 3. CAN transmission and reception with CAN filter enabled * 4. CANFD transmission and reception * * *****************************************************************************************/ #include <rtthread.h> #include <rtdevice.h> #include "rtt_board.h" #define CAN_BAUD (500000) #define CANFD_BAUD (2000000) #define CAN_FILTERMODE_IDMASK (0x00000000U) /*!< Identifier mask mode */ static struct rt_semaphore rx_sem; /* Semaphore fore CAN receive */ static rt_device_t can_dev; /* CAN device handler */ static uint8_t can_get_data_bytes_from_dlc(uint32_t dlc); static void can_rx_thread(void *parameter); #define PID_ENGINE_LOAD 0x04 #define PID_COOLANT_TEMP 0x05 //表示发动机冷却液的温度,通常以摄氏度(°C)为单位,是判断发动机温度状态的重要参数。 #define PID_SHORT_TERM_FUEL_TRIM_1 0x06 #define PID_LONG_TERM_FUEL_TRIM_1 0x07 #define PID_SHORT_TERM_FUEL_TRIM_2 0x08 #define PID_LONG_TERM_FUEL_TRIM_2 0x09 #define PID_FUEL_PRESSURE 0x0A #define PID_INTAKE_MAP 0x0B #define PID_RPM 0x0C #define PID_SPEED 0x0D #define PID_TIMING_ADVANCE 0x0E #define PID_INTAKE_TEMP 0x0F #define PID_MAF_FLOW 0x10 #define PID_THROTTLE 0x11 #define PID_AUX_INPUT 0x1E #define PID_RUNTIME 0x1F #define PID_DISTANCE_WITH_MIL 0x21 #define PID_COMMANDED_EGR 0x2C #define PID_EGR_ERROR 0x2D #define PID_COMMANDED_EVAPORATIVE_PURGE 0x2E #define PID_FUEL_LEVEL 0x2F #define PID_WARMS_UPS 0x30 #define PID_DISTANCE 0x31 #define PID_EVAP_SYS_VAPOR_PRESSURE 0x32 #define PID_BAROMETRIC 0x33 #define PID_CATALYST_TEMP_B1S1 0x3C #define PID_CATALYST_TEMP_B2S1 0x3D #define PID_CATALYST_TEMP_B1S2 0x3E #define PID_CATALYST_TEMP_B2S2 0x3F #define PID_CONTROL_MODULE_VOLTAGE 0x42 #define PID_ABSOLUTE_ENGINE_LOAD 0x43 #define PID_AIR_FUEL_EQUIV_RATIO 0x44 #define PID_RELATIVE_THROTTLE_POS 0x45 #define PID_AMBIENT_TEMP 0x46 #define PID_ABSOLUTE_THROTTLE_POS_B 0x47 #define PID_ABSOLUTE_THROTTLE_POS_C 0x48 #define PID_ACC_PEDAL_POS_D 0x49 #define PID_ACC_PEDAL_POS_E 0x4A #define PID_ACC_PEDAL_POS_F 0x4B #define PID_COMMANDED_THROTTLE_ACTUATOR 0x4C #define PID_TIME_WITH_MIL 0x4D #define PID_TIME_SINCE_CODES_CLEARED 0x4E #define PID_ETHANOL_FUEL 0x52 #define PID_FUEL_RAIL_PRESSURE 0x59 #define PID_HYBRID_BATTERY_PERCENTAGE 0x5B #define PID_ENGINE_OIL_TEMP 0x5C #define PID_FUEL_INJECTION_TIMING 0x5D #define PID_ENGINE_FUEL_RATE 0x5E #define PID_ENGINE_TORQUE_DEMANDED 0x61 #define PID_ENGINE_TORQUE_PERCENTAGE 0x62 #define PID_ENGINE_REF_TORQUE 0x63 typedef struct { uint16_t rpm; //表示发动机的转速(RPM),对于监控发动机性能至关重要。 uint16_t speed; //时速 short coolant_temp; //水温 uint8_t Control_Module_Voltage; //控制模块的电压 int Distance; //总里程 int Trip_Distance; //小计里程 short map; //进气压力 uint16_t engine_fuel_rate; //瞬时油耗 }ODB_info; int main(void) { /* Search CAN device */ can_dev = rt_device_find(BOARD_CAN_NAME); if (!can_dev) { rt_kprintf("find %s failed!\n", BOARD_CAN_NAME); return RT_ERROR; } /* Initialize semaphore for CAN receive */ rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO); /* OPEN CAN device using interrupt transmit and receive mode */ rt_err_t res = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX); rt_device_control(can_dev, RT_CAN_CMD_SET_MODE, (void*) RT_CAN_MODE_NORMAL); rt_device_control(can_dev, RT_CAN_CMD_SET_BAUD, (void*)CAN_BAUD); RT_ASSERT(res == RT_EOK); /* Create receive thread */ rt_thread_t thread = rt_thread_create("can_rx", can_rx_thread, RT_NULL, 1024, 25, 10); if (thread != RT_NULL) { rt_thread_startup(thread); } else { rt_kprintf("create can_rx thread failed!\n"); } } /* Callback for receive */ static rt_err_t can_rx_call(rt_device_t dev, rt_size_t size) { rt_sem_release(&rx_sem); return RT_EOK; } static void can_rx_thread(void *parameter) { int i; rt_err_t res; struct rt_can_msg rxmsg = {0}; /* Set callback for receive */ rt_device_set_rx_indicate(can_dev, can_rx_call); while (1) { rt_sem_take(&rx_sem, RT_WAITING_FOREVER); /* Get one CAN frame */ #ifdef RT_CAN_USING_HDR /* when RT_CAN_USING_HDR is turned on, read data from hdr for rtt can driver */ rxmsg.hdr_index = BOARD_CAN_HWFILTER_INDEX; #endif rt_device_read(can_dev, 0, &rxmsg, sizeof(rxmsg)); /* Display CAN frame content */ rt_kprintf("ID: %x ", rxmsg.id); uint32_t msg_len = can_get_data_bytes_from_dlc(rxmsg.len); for (i = 0; i < msg_len; i++) { rt_kprintf("%02x", rxmsg.data[i]); } rt_kprintf("\n"); } } static uint8_t can_get_data_bytes_from_dlc(uint32_t dlc) { uint32_t data_bytes = 0; dlc &= 0xFU; if (dlc <= 8U) { data_bytes = dlc; } else { switch (dlc) { case 9: data_bytes = 12U; break; case 10: data_bytes = 16U; break; case 11: data_bytes = 20U; break; case 12: data_bytes = 24U; break; case 13: data_bytes = 32U; break; case 14: data_bytes = 48U; break; case 15: data_bytes = 64U; break; default: /* Code should never touch here */ break; } } return data_bytes; } uint8_t PID[]={PID_RPM,PID_SPEED,PID_DISTANCE,PID_CONTROL_MODULE_VOLTAGE,PID_ENGINE_FUEL_RATE,PID_EVAP_SYS_VAPOR_PRESSURE,PID_COOLANT_TEMP}; int can_sample(int argc, char *argv[]) { struct rt_can_msg msg = {0}; rt_err_t res; rt_size_t size; rt_thread_t thread; char can_name[RT_NAME_MAX]; if (argc == 2) { rt_strncpy(can_name, argv[1], RT_NAME_MAX); } else { rt_strncpy(can_name, BOARD_CAN_NAME, RT_NAME_MAX); } /* Search CAN device */ can_dev = rt_device_find(can_name); if (!can_dev) { rt_kprintf("find %s failed!\n", can_name); return RT_ERROR; } /* Restore CAN filters to default state */ rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, NULL); msg.id = 0x07DF; /* ID 为 0x78 */ msg.ide = RT_CAN_STDID; /* Standard ID */ msg.rtr = RT_CAN_DTR; /* Data Frame */ msg.len = CAN_MSG_8BYTES; /* Data size: 8 */ /* Data to be sent */ msg.data[0] = 0x02; msg.data[1] = 0x01; msg.data[2] = 0x00; msg.data[3] = 0x00; msg.data[4] = 0x00; msg.data[5] = 0x00; msg.data[6] = 0x00; msg.data[7] = 0x00; rt_kprintf("Sent out messages with standard id\n"); // while(1) { for (uint32_t i = 0; i < sizeof(PID); i++) { // msg.id = i; msg.data[2] = PID[i]; size = rt_device_write(can_dev, 0, &msg, sizeof(msg)); if (size == 0) { rt_kprintf("can dev write data failed!\n"); } rt_kprintf("can!\n"); rt_thread_mdelay(2000); /* delay a while, give the receive thread enough time to print out received message */ } } // rt_kprintf("Sent out messages with extended id\n"); // msg.ide = RT_CAN_EXTID; /* Extend ID */ // for (uint32_t i = 0; i < 10; i++) // { // msg.id = i + 0x10000; // size = rt_device_write(can_dev, 0, &msg, sizeof(msg)); // if (size == 0) // { // rt_kprintf("can dev write data failed!\n"); // } // rt_thread_mdelay(5); /* delay a while, give the receive thread enough time to print out received message */ // } /**************************************************************************** * * Configure the CAN Filters * ****************************************************************************/ struct rt_can_filter_item items[5] = { #ifdef RT_CAN_USING_HDR /* std, match ID:0x100~0x1ff, default filter list */ RT_CAN_FILTER_ITEM_INIT(0x100, 0, 0, CAN_FILTERMODE_IDMASK, 0x700, RT_NULL, RT_NULL), /* std, match ID:0x300~0x3ff*/ RT_CAN_FILTER_ITEM_INIT(0x300, 0, 0, CAN_FILTERMODE_IDMASK, 0x700, RT_NULL, RT_NULL), /* std,match ID:0x211*/ RT_CAN_FILTER_ITEM_INIT(0x211, 0, 0, CAN_FILTERMODE_IDMASK, 0x7FF, RT_NULL, RT_NULL), /* std,match ID:0x486*/ RT_CAN_FILTER_STD_INIT(0x486, RT_NULL, RT_NULL), /* std, match ID: 0x55, specify the filter number : 7 */ {0x555, 0, 0, CAN_FILTERMODE_IDMASK, 0x7ff, 7,} #else /* std, match ID:0x100~0x1ff, default filter list */ RT_CAN_FILTER_ITEM_INIT(0x100, 0, 0, CAN_FILTERMODE_IDMASK, 0x700), /* std, match ID:0x300~0x3ff*/ RT_CAN_FILTER_ITEM_INIT(0x300, 0, 0, CAN_FILTERMODE_IDMASK, 0x700), /* std,match ID:0x211*/ RT_CAN_FILTER_ITEM_INIT(0x211, 0, 0, CAN_FILTERMODE_IDMASK, 0x7FF), /* std,match ID:0x486*/ RT_CAN_FILTER_STD_INIT(0x486), /* std, match ID: 0x55, specify the filter number : 7 */ {0x555, 0, 0, CAN_FILTERMODE_IDMASK, 0x7ff, 7,} #endif }; // struct rt_can_filter_config cfg = {5, 1, items}; // res = rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg); // RT_ASSERT(res == RT_EOK); // // rt_kprintf("Sent out messages with filter enabled\n"); // msg.ide = RT_CAN_STDID; // for (uint32_t i = 0; i < 0x7ff; i++) // { // msg.id = i; // size = rt_device_write(can_dev, 0, &msg, sizeof(msg)); // if (size == 0) // { // rt_kprintf("can dev write data failed!\n"); // } // rt_thread_mdelay(5); /* delay a while, give the receive thread enough time to print out received message */ // } #ifdef RT_CAN_USING_CANFD /* Restore CAN filters to default state */ rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, NULL); /* Re-Configure CAN baud rate */ rt_device_control(can_dev, RT_CAN_CMD_SET_BAUD, (void*)CAN_BAUD); /* Configure CAN-FD baud rate */ rt_device_control(can_dev, RT_CAN_CMD_SET_BAUD_FD, (void*)CANFD_BAUD); /** * NOTE: In real CAN-FD use case, user may need to use the similar codes as below * to get the same bit-timing configuration in the whole CANFD network * * Assume the CAN clock is 80MHz, below is the bit timing for CANFD * */ #if 0 struct rt_can_bit_timing timing_items[2] = { /* prescaler, num_seg1, num_seg2, num_sjw, num_sspoff */ {2, 32, 8, 8, 0}, /* 1Mbit/s */ {2, 16, 4, 4, 16}, /* 2Mbit/s */ }; struct rt_can_bit_timing_config timing_config = { 2, &timing_items[0]; }; rt_device_control(can_dev, RT_CAN_CMD_SET_BITTIMING, &timing_config); #endif /* Enable CANFD */ rt_device_control(can_dev, RT_CAN_CMD_SET_CANFD, (void*)1); msg.len = CAN_MSG_16BYTES; msg.brs = 1; msg.fd_frame = 1; for (uint32_t i=8; i<16; i++) { msg.data[i] = (uint8_t)(i | (i<<4)); } rt_kprintf("Sent out CANFD messages with standard id\n"); for (uint32_t i = 0; i < 10; i++) { msg.id = i; size = rt_device_write(can_dev, 0, &msg, sizeof(msg)); if (size == 0) { rt_kprintf("can dev write data failed!\n"); } rt_thread_mdelay(10); /* delay a while, give the receive thread enough time to print out received message */ } rt_kprintf("Sent out CANFD messages with extended id\n"); msg.ide = RT_CAN_EXTID; /* Extend ID */ for (uint32_t i = 0; i < 10; i++) { msg.id = i + 0x10000; size = rt_device_write(can_dev, 0, &msg, sizeof(msg)); if (size == 0) { rt_kprintf("can dev write data failed!\n"); } rt_thread_mdelay(10); /* delay a while, give the receive thread enough time to print out received message */ } #endif return RT_EOK; } /* EXPORT to msh command list */ MSH_CMD_EXPORT(can_sample, can device sample); 这个程序最终卡在这里:   size = rt_device_write(can_dev, 0, &msg, sizeof(msg)); //后面都没有执行到 if (size == 0) { rt_kprintf("can dev write data failed!\n"); } rt_kprintf("can!\n"); 一帧数据都没有发送完,就卡死在这了  

  • 2025-03-03
  • 回复了主题帖: 【先楫HPM5361】USART篇-读取GPS数据

    lugl4313820 发表于 2025-3-3 16:09 浮点打印,要以IDE的编译选项里进行设置,设置可打印浮点的开关。 学到了

  • 2025-03-02
  • 发表了主题帖: 【先楫HPM5361】USART篇-读取GPS数据

    本帖最后由 空耳- 于 2025-3-3 11:40 编辑 读取GPS数据 串口实现的主要步骤: 首先查找串口设备获取设备句柄。 初始化回调函数发送使用的信号量,然后以读写及中断接收方式打开串口设备。 设置串口设备的接收回调函数,之后发送字符串,并创建读取数据线程。 读取数据线程会尝试读取一个字符数据,如果没有数据则会挂起并等待信号量,当串口设备接收到一个数据时会触发中断并调用接收回调函数,此函数会发送信号量唤醒线程,此时线程会马上读取接收到的数据。 运行序列图如下图所示:     这里我们使用USART2(使用的是PB08和PB09),对其进行简单配置       NMEA0183协议解析 我这里使用到的GNSS模组为:ATGM332D-5N31 GPS数据包类型:(GP :GPS;BD:北斗;GN:多星联合定位;GL:GLONASS) GPGSV:可见卫星信息 GPGLL:地理定位信息 GPRMC:推荐最小定位信息 GPVTG:地面速度信息 GPGGA:GPS定位信息 GPGSA:当前卫星信息 我这里主要使用的是GNGGA,其标准协议为: 标准格式:$GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,M,<10>,M,<11>,<12>*hh<CR><LF> 格式解析: <1> UTC 时间,hhmmss(时分秒)格式 <2> 纬度ddmm.mmmm(度分)格式(前面的0 也将被传输) <3> 纬度半球N(北半球)或S(南半球 <4> 经度dddmm.mmmm(度分)格式(前面的0 也将被传输) <5> 经度半球E(东经)或W(西经) <6> GPS 状态:0=未定位,1=非差分定位,2=差分定位,6=正在估算 <7> 正在使用解算位置的卫星数量(00~12)(前面的0 也将被传输) <8> HDOP 水平精度因子(0.5~99.9) <9> 海拔高度(-9999.9~99999.9) <10> 地球椭球面相对大地水准面的高度 <11> 差分时间(从最近一次接收到差分信号开始的秒数,如果不是差分定位将为空) <12> 差分站ID 号0000~1023(前面的0 也将被传输,如果不是差分定位将为空 核心代码 /* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2025-03-02 Administrator the first version */ #include <rtthread.h> #include <rtdevice.h> #include <board.h> /*******************************************************************************************/ #include "gps.h" #include <string.h> nmea_msg gpsx; /** * [url=home.php?mod=space&uid=159083]@brief[/url] NMEA_Comma_pos 从buf里面得到第n个逗号所在的位置 * @argument 数组 * @argument 地n个',' * [url=home.php?mod=space&uid=784970]@return[/url] 逗号的位置 */ #if 1 u8 NMEA_Comma_Pos(u8 *buf,u8 n) { u8 *ptr = buf; while(n) { //遇到'*'或者非法字符,则不存在第cx个逗号 if(*ptr == '*' || *ptr < ' '|| *ptr > 'z')return 0xFF; if(*ptr == ',')n--; ptr++; } return ptr-buf; } /** * @brief NMEA_Pow m^n次方 */ u32 NMEA_Pow(u8 m,u8 n) { u32 result =1; while(n--)result *= m; return result; } /** * @brief 字符串转数字 */ int NMEA_Str2Num(u8 *buf,u8*dx) { u8 *p = buf; u32 ires=0,fres=0; u8 ilen = 0,flen =0,i; u8 mask=0; int res; /*********得到个位十位***************/ while(1) { if(*p == '-') { mask |= 0x02; p++; } if(*p == ',' || *p == '*')break; if(*p == '.') { mask |= 0x01; p++; } else if((*p >'9') || (*p < '0')) { ilen=0; flen=0; break; } if(mask & 0x01)flen++;//小数 else ilen++;//整数 p++; } if(mask&0x02)buf++; for(i = 0;i<ilen;i++) { ires += NMEA_Pow(10,ilen-1-i)*(buf[i]-'0'); //LTPrintf("buf[%d]:%d ,ires:%d\n",i,(buf[i]-'0'),ires); } if(flen>5)flen = 5; *dx = flen; for(i=0;i<flen;i++) { fres += NMEA_Pow(10,flen-1-i)*(buf[ilen+1+i]-'0'); } //LTPrintf("ilen:%d flen%d ires:%d fres:%d\n",ilen,flen,ires,fres); res = ires*NMEA_Pow(10,flen)+fres; if(mask&0x02)res = -res; return res; } /** * @brief 分析GPGSV信息信息 * @argument nmea信息结构体 * @argument buf接收数据的缓存区地址 * @return 无・ * @note GSV表示可视的GNSS卫星。本语句包含可视的卫星数、卫星标识号、仰角、方位角和信噪比。每次传送, * 一个GSV语句只能包含最多4颗卫星的数据, * 因此可能需要多个语句才能获得完整的信息。由于GSV包含的卫星不用于定位解决方案,所以GSV语句指示的卫星可能比GGA多。 * @note “GN”标识符不可用于该语句。如果可以多个卫星系统可视,则设备输出多条GSV语句,用不同的发送设备标识符表示相应的卫星。 **/ void NMEA_GPGSV_Analysis(nmea_msg *gpsx,u8 *buf) { u8 *p,*p1,dx; u8 len,i,j,slx = 0; u8 posx; p = buf; p1 = (u8*)strstr((const char*)p,"$GPGSV"); len = p1[7] - '0'; //得到GPGSV的条数语句总数。范围:1~9。 posx=NMEA_Comma_Pos(p1,3); //得到可见卫星总数 if(len!=0xFF)gpsx->svnum = NMEA_Str2Num(p1+posx,&dx); for(i=0;i<len;i++) { p1 = (u8*)strstr((const char*)p,"$GPGSV"); for(j=0;j<4;j++) { posx=NMEA_Comma_Pos(p1,4+j*4); if(posx!=0xFF)gpsx->slmsg[slx].num = NMEA_Str2Num(p1+posx,&dx);//得到卫星编号 else break; posx=NMEA_Comma_Pos(p1,5+j*4); if(posx!=0xFF)gpsx->slmsg[slx].eledeg= NMEA_Str2Num(p1+posx,&dx);//得到卫星仰角 else break; posx=NMEA_Comma_Pos(p1,6+j*4); if(posx!=0xFF)gpsx->slmsg[slx].azideg = NMEA_Str2Num(p1+posx,&dx);//得到卫星方位角 else break; posx=NMEA_Comma_Pos(p1,7+j*4); if(posx!=0xFF)gpsx->slmsg[slx].sn = NMEA_Str2Num(p1+posx,&dx);//得到卫星信噪比 else break; slx++; } } } /** * @brief 分析GPGGA信息 * @argument nmea信息结构体 * @argument buf接收数据的缓存区地址 * @return 无・ * @note GGA提供全球定位系统定位数据。本语句包含GNSS接收机提供的时间、位置和定位相关数据 * 1.QZSS和GPS星系配置下<TalkerID>均为GP;有关卫星标识符的详情,请参考表16:GNSS标识符。 * 2. NMEA 0183协议指示GGA消息为GPS系统特有;但当接收器配置为多星系时,GGA消息的内容将从多星系解决方案中生成。 * 3. 1) NMEA 0183协议定义的使用中卫星数量范围为00~12,然而,在多星系解决方案中,使用的卫星数量可能超过12颗。 **/ void NMEA_GPGGA_Analysis(nmea_msg *gpsx,u8 *buf) { u8 *p1,dx; u8 posx; p1 = (u8*)strstr((const char*)buf,"$GNGGA"); posx = NMEA_Comma_Pos(p1,1);//得到日期 if(posx != 0xFF) { int temp = NMEA_Str2Num(p1+posx,&dx); gpsx->utc.sec = (temp / 1000) % 10; gpsx->utc.min = (temp / 100000) %100; gpsx->utc.hour = temp / 10000000; } posx = NMEA_Comma_Pos(p1,2);NMEA_Str2Num; if(posx!=0xFF) { int temp = NMEA_Str2Num(p1+posx,&dx); //LTPrintf("temp:%d dx:%d\n",temp,dx); gpsx->latitude = temp / NMEA_Pow(10,dx+2);//得到° float rs = temp%NMEA_Pow(10,dx+2); //得到' gpsx->latitude=gpsx->latitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为° } posx = NMEA_Comma_Pos(p1,3);//南纬还是北纬 if(posx!=0xFF)gpsx->nshemi = *(p1+posx); posx = NMEA_Comma_Pos(p1,4);//得到经度 if(posx!=0xFF) { int temp = NMEA_Str2Num(p1+posx,&dx); gpsx->longitude = temp / NMEA_Pow(10,dx+2); float rs = temp%NMEA_Pow(10,dx+2); gpsx->longitude = gpsx->longitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60; } posx = NMEA_Comma_Pos(p1,5);//东经还是西经 if(posx!=0xFF)gpsx->ewhemi = *(p1+posx); posx = NMEA_Comma_Pos(p1,6); //GPS状态:0,未定位;1,非差分定位;2,差分定位;6,正在估算. if(posx!=0XFF)gpsx->gpssta = NMEA_Str2Num(p1+posx,&dx); posx = NMEA_Comma_Pos(p1,7); //使用的卫星数。 if(posx!=0xFF)gpsx->posslnum = NMEA_Str2Num(p1+posx,&dx); posx = NMEA_Comma_Pos(p1,9); //得到海拔高度 if(posx != 0xFF)gpsx->altitude = NMEA_Str2Num(p1+posx,&dx); } /** * @brief 分析GPGSA信息 * @argument nmea信息结构体 * @argument buf接收数据的缓存区地址 * @return 无・ * @note GSA表示GNSS精度因子(DOP)与有效卫星。本语句包含GNSS接收机工作模式,GGA或GNS 语句报告的导航解算中用到的卫星以及精度因子的值。 **/ void NMEA_GPGSA_Analysis(nmea_msg *gpsx,u8 *buf) { u8 *p1,dx; u8 posx,i; //LTPrintf("===============NMEA_GPGSA_Analysis=======================\r\n"); p1 = (u8*)strstr((const char*)buf,"$GNGSA"); //LTPrintf("p1:%p buf:%p\r\n",p1,buf); posx = NMEA_Comma_Pos(p1,2); //得到定位类型 //LTPrintf("posx:%d\r\n",posx); if(posx!=0xFF)gpsx->fixmode = NMEA_Str2Num(p1+posx,&dx); for(i=0;i<12;i++)//得到定位卫星编号 { posx = NMEA_Comma_Pos(p1,3+i); if(posx!=0xFF)gpsx->possl[i] = NMEA_Str2Num(p1+posx,&dx); else break; } posx = NMEA_Comma_Pos(p1,15);//位置精度因子 if(posx != 0xFF)gpsx->pdop = NMEA_Str2Num(p1+posx,&dx); posx = NMEA_Comma_Pos(p1,16);//水平精度因子 if(posx != 0xFF)gpsx->hdop = NMEA_Str2Num(p1+posx,&dx); posx = NMEA_Comma_Pos(p1,17);//垂直精度因子 if(posx != 0xFF)gpsx->vdop = NMEA_Str2Num(p1+posx,&dx); } /** * @brief 分析GPRMC信息 * @argument nmea信息结构体 * @argument buf接收数据的缓存区地址 * @return 无・ * @note RMC表示推荐的最少专用GNSS数据。本语句包含GNSS接收机提供的时间、日期、位置、航迹向 和速度数据。 **/ void NMEA_GPRMC_Analysis(nmea_msg *gpsx,u8 *buf) { u8 *p1,dx,posx; u32 temp; float rs; p1 = (u8 *)strstr((const char*)buf,"GNRMC");//"$GPRMC",经常有&和GPRMC分开的情况,故只判断GPRMC. //LTPrintf("buf:%p p1:%p\n",buf,p1); //LTPrintf("buf:%s p1:%s\n",buf,p1); posx = NMEA_Comma_Pos(p1,1);//获取时间,不要ms //LTPrintf("posx:%d ",posx); if(posx!=0xFF) { temp = NMEA_Str2Num(p1+posx,&dx) / NMEA_Pow(10,dx); //LTPrintf("temp:%d dx:%d\n",temp,dx); gpsx->utc.hour = temp/10000; gpsx->utc.min = (temp /100) %100; gpsx->utc.sec = temp % 100; } posx = NMEA_Comma_Pos(p1,3);NMEA_Str2Num; if(posx!=0xFF) { temp = NMEA_Str2Num(p1+posx,&dx); //LTPrintf("temp:%d dx:%d\n",temp,dx); gpsx->latitude = temp / NMEA_Pow(10,dx+2);//得到° rs = temp%NMEA_Pow(10,dx+2); //得到' gpsx->latitude=gpsx->latitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60;//转换为° } posx = NMEA_Comma_Pos(p1,4);//南纬还是北纬 if(posx!=0xFF)gpsx->nshemi = *(p1+posx); posx = NMEA_Comma_Pos(p1,5);//得到经度 if(posx!=0xFF) { temp = NMEA_Str2Num(p1+posx,&dx); gpsx->longitude = temp / NMEA_Pow(10,dx+2); rs = temp%NMEA_Pow(10,dx+2); gpsx->longitude = gpsx->longitude*NMEA_Pow(10,5)+(rs*NMEA_Pow(10,5-dx))/60; } posx = NMEA_Comma_Pos(p1,6);//东经还是西经 if(posx!=0xFF)gpsx->ewhemi = *(p1+posx); posx = NMEA_Comma_Pos(p1,9);//得到日期 if(posx != 0xFF) { temp = NMEA_Str2Num(p1+posx,&dx); gpsx->utc.date = temp / 10000; gpsx->utc.month = (temp / 100) %100; gpsx->utc.year = 2000 + temp % 100; } } /** * @brief 分析GPVTG信息 * @argument nmea信息结构体 * @argument buf接收数据的缓存区地址 * @return 无・ * @note VTG语句包含相对于地面的实际航向和速度 **/ void NMEA_GPVTG_Analysis(nmea_msg *gpsx,u8 *buf) { u8 *p1,dx,posx; p1 = (u8*)strstr((const char*)buf,"$GNVTG"); posx = NMEA_Comma_Pos(p1,1);//<COGT 对地航向(真北) if(posx!=0xFF)gpsx->cogt = NMEA_Str2Num(p1+posx,&dx) / NMEA_Pow(10,dx); posx = NMEA_Comma_Pos(p1,5);//对地速度 节 if(posx!=0xFF) { gpsx->SOGN = NMEA_Str2Num(p1+posx,&dx) / NMEA_Pow(10,dx); } posx = NMEA_Comma_Pos(p1,7);//对地速度 km/h gpsx->SOGK = NMEA_Str2Num(p1+posx,&dx) / NMEA_Pow(10,dx); } /** * @brief 提取NMEA-0183信息 * @argument nmea信息结构体 * @argument buf接收数据的缓存区地址 * @return 无・ * @note **/ void GPS_Analysis(nmea_msg *gpsx,u8 *buf) { NMEA_GPGSV_Analysis(gpsx,buf); //GPGSV解析 NMEA_GPGGA_Analysis(gpsx,buf); //GPGGA解析 NMEA_GPGSA_Analysis(gpsx,buf); //GPGSA解析 NMEA_GPRMC_Analysis(gpsx,buf); //GPRMC解析 NMEA_GPVTG_Analysis(gpsx,buf); //GPVTG解析 } /** * @brief GPS校验和计算 * @argument * @argument * @return 无・ * @note **/ void GPS_CheckSum(u8*buf,u8*checksum) { *checksum = 0; u8 *ptr = buf; const char *start = strchr(ptr,'$'); const char *end = strchr(ptr,'*'); if(start == NULL || end == NULL || (start >= end))return; for(ptr = (u8 *)(start + 1);ptr < (u8 *)end;ptr++) { *checksum ^= *ptr; } } /*-----------------------------------------PCAS_---------------------------*/ /** * @brief 配置NMEA串口波特率 * @argument * @argument * @return 无・ * @note **/ //void PCAS01(u8 *buf,u8 baud) //{ // u8 *ptr = "$PCAS01,1*"; // u8 len = strlen(ptr); // memncpy(ptr,buf,len-1); // //} #endif /****************************************************************************************/ #define SAMPLE_UART_NAME "uart2" /* 串口设备名称 */ struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; /* 初始化配置参数 */ /* 用于接收消息的信号量 */ static struct rt_semaphore rx_sem; static rt_device_t serial; u16 RXD_index; u8 RX_BUF[800]; //接收数据回调函数 static rt_err_t uart_input(rt_device_t dev, rt_size_t size) { /* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */ if (size > 0) { rt_sem_release(&rx_sem); } return RT_EOK; } // 串口接收线程入口函数 static void serial_thread_entry(void *parameter) { char ch; while (1) { rt_sem_take(&rx_sem, RT_WAITING_FOREVER); // 等待信号量 // 循环读取可用数据 while (rt_device_read(serial, 0, &ch, 1) == 1) { // 存储接收到的数据 if (RXD_index < (sizeof(RX_BUF) - 1)) // 确保不会超出缓冲区大小 { RX_BUF[RXD_index++] = ch; } else { GPS_Analysis(&gpsx, RX_BUF); RX_BUF[RXD_index++] = '\0'; for(int i = 0;i<RXD_index;i++) rt_kprintf("%c",RX_BUF[i]); // rt_kprintf("%s",RX_BUF); rt_kprintf("\n-----------------------------------------------\n"); rt_kprintf("GPS Analysis Result:\n"); rt_kprintf("Year: %d\n", gpsx.utc.date); rt_kprintf("Month: %d\n", gpsx.utc.month); rt_kprintf("Date: %d\n", gpsx.utc.date); rt_kprintf("hour: %d\n", gpsx.utc.hour); rt_kprintf("min: %d\n", gpsx.utc.min); rt_kprintf("sec: %d\n", gpsx.utc.sec); // rt_kprintf("Longitude: %.6f\n", (float)gpsx.longitude / 100000.0); // rt_kprintf("Latitude: %.6f\n", (float)gpsx.latitude / 100000.0); printf("Longitude: %.6f\n", (float)gpsx.longitude / 100000.0); printf("Latitude: %.6f\n", (float)gpsx.latitude / 100000.0); RXD_index = 0; // 处理完后重置索引 } } } } /** * @brief thread_serial * @param None * @retval ret */ int GPS_serial(void) { rt_err_t ret = RT_EOK; char uart_name[RT_NAME_MAX]; char str[] = "hello RT-Thread!\\r\\n"; rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX); /* 查找系统中的串口设备 */ serial = rt_device_find(uart_name); if (!serial) { rt_kprintf("find %s failed!\\n", uart_name); return RT_ERROR; } /* 修改串口配置参数 */ config.baud_rate = BAUD_RATE_9600; //修改波特率为 115200 config.data_bits = DATA_BITS_8; //数据位 8 config.stop_bits = STOP_BITS_1; //停止位 1 config.parity = PARITY_NONE; //无奇偶校验位 /* 控制串口设备。通过控制接口传入命令控制字,与控制参数 */ rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config); /* 初始化信号量 */ rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO); /* 以中断接收及轮询发送模式打开串口设备 */ rt_device_open(serial, RT_DEVICE_FLAG_INT_RX); /* 设置接收回调函数 */ rt_device_set_rx_indicate(serial, uart_input); /* 发送字符串 */ rt_device_write(serial, 0, str, (sizeof(str) - 1)); /* 创建 serial 线程 */ rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10); /* 创建成功则启动线程 */ if (thread != RT_NULL) { rt_thread_startup(thread); } else { ret = RT_ERROR; } return ret; } /* 导出到 msh 命令列表中 */ //MSH_CMD_EXPORT(GPS_serial,GPS uart device sample); // 导出函数自动运行,在系统初始化时调用usr_led_run函数 INIT_APP_EXPORT(GPS_serial); 实例演示     [localvideo]8975e76985a678613336616dbb411edb[/localvideo]   遇到的问题 不知道为什么这个接口没法实现浮点数的打印rt_kprintf // rt_kprintf("Longitude: %.6f\n", (float)gpsx.longitude / 100000.0); // rt_kprintf("Latitude: %.6f\n", (float)gpsx.latitude / 100000.0); printf("Longitude: %.6f\n", (float)gpsx.longitude / 100000.0); printf("Latitude: %.6f\n", (float)gpsx.latitude / 100000.0);  

  • 回复了主题帖: 【先楫HPM5361】01_GPIO篇(二)

    lugl4313820 发表于 2025-3-2 17:33 按键好象不那灵呀,这个是不是可以用pwm的占空比,或者SPI来控制是不是更方便一些呢? 不是按键不灵,是io模拟这个时序,容易出错

  • 发表了主题帖: 【先楫HPM5361】01_GPIO篇(二)

    WS2812 我们在上次的工程上新建一个文件夹命名为ws2812,在这个文件夹下进行点亮WS2812B功能的演示。 这里还是采用RT-Thread studio的开发环境。 WS2812介绍 WS2812B是一种广泛使用的智能LED灯带,主要用于各种装饰和显示应用。 WS2812的数据协议采用单线归零码的通讯方式,支持串行级联接口,能通过一根信号线完成数据的接收与解码。每个灯就是一个像素点,每个像素点的三基色颜色可实现256级亮度显示,完成16777216种颜色的全真色彩显示。 像素点在上电复位以后,DIN端接受从控制器传输过来的数据,首先送过来的24bit数据被第一个像素点提取后,送到像素点内部的数据锁存器,剩余的数据经过内部整形处理电路整 形放大后通过DO端口开始转发输出给下一个级联的像素点,每经过一个像素点的传输,信号减少24bit。像素点采用自动整形转发技术,使得该像素点的级联个数不受信号传送的限制,仅受限信号传输速度要求。 控制方式     时序要求     核心代码 其实这里不推荐采用gpio的方式驱动的,可以采用spi来驱动,这样更稳定。 /* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2025-03-01 Administrator the first version */ #include <rtthread.h> #include <rtdevice.h> #include "hpm_common.h" #include "hpm_soc.h" #include <drv_gpio.h> #include "ws2812.h" #define RGB_PIN GET_PIN(B, 8) #define RGBLED_1 rt_pin_write(RGB_PIN , 1); #define RGBLED_0 rt_pin_write(RGB_PIN , 0); uint8_t led_array[WS2812_SUM * 3]; //定义颜色数据存储数组 uint32_t led_count = WS2812_SUM; //定义实际彩灯默认个数 uint8_t led_bytes = WS2812_SUM*3; //定义实际彩灯颜色数据个数 #define MTIMER_CORE_CLOCK 24000000 /** * [url=home.php?mod=space&uid=2666770][url=home.php?mod=space&uid=159083]@brief[/url] [/url] read machine timer low value * [url=home.php?mod=space&uid=3142012]@param[/url] none. * @retval none. */ uint32_t mtime_lo(void) { return ((HPM_MCHTMR->MTIME) & 0xFFFFFFFF) ; } /** * @brief read machine timer high value * @param none. * @retval none. */ uint32_t mtime_hi(void) { return (((HPM_MCHTMR->MTIME) >> 32) & 0xFFFFFFFF); } /** * @brief get machine timer value * @param none. * @retval none. */ uint64_t get_timer_value(void) { uint64_t value = 0; uint64_t hi; uint64_t lo; while(1) { hi = mtime_hi(); lo = mtime_lo(); if(hi == mtime_hi()) { value = ((uint64_t)((hi << 32)) | lo); break; } } return value; } /** * @brief delay a time in microsecond * @param nus: count in microsecond * @retval none */ void delay_us(uint32_t nus) { uint64_t start_mtime; uint64_t delta_mtime; /* get current timer value */ uint64_t tmp = 0; tmp = get_timer_value(); do { start_mtime = get_timer_value(); }while(start_mtime == tmp); /* continue counting until the delay time is reached */ do { delta_mtime = get_timer_value() - start_mtime; }while(delta_mtime <(MTIMER_CORE_CLOCK / 1000000 * nus)); } void rt_hw_us_delay(rt_uint32_t us) { delay_us(us); } void rgb_SetColor(uint8_t lde_id, unsigned long color) { if( lde_id > WS2812_SUM ) { return; } led_array[lde_id * 3] = (color>>8)&0xff; led_array[lde_id * 3 + 1] = (color>>16)&0xff; led_array[lde_id * 3 + 2] = (color>>0)&0xff; } void rgb_SetRGB(uint8_t lde_id, uint8_t red, uint8_t green, uint8_t blue) { uint32_t color=red<<16|green<<8|blue; rgb_SetColor(lde_id,color); } void Ws2812b_WriteByte(uint8_t byte) { int i = 0, j = 0; for(i = 0; i < 8; i++ ) { if( byte & (0x80 >> i) )//当前位为1 { RGBLED_1; delay_us(1); RGBLED_0; } else//当前位为0 { RGBLED_1; RGBLED_0; delay_us(1);//0.833us } } } void rgb_sendarray(void) { unsigned int i; //发送数据 for(i=0; i<led_bytes; i++) Ws2812b_WriteByte(led_array[i]); } void RGB_LED_Reset(void) { RGBLED_0; delay_us(281); } void rgb_Init(void) { rt_pin_mode(RGB_PIN, PIN_MODE_OUTPUT); } uint32_t color_buff[WS2812_SUM] = {WHITE,GREEN,WHITE,GREEN,WHITE,GREEN,WHITE,GREEN,WHITE,GREEN,WHITE,GREEN}; static void rgbled_entry(void *param) { rgb_Init(); RGBLED_0; memset(color_buff,GREEN,sizeof(color_buff)); rt_kprintf("ws2812 test -------------\n"); while (1) { for( int i = 0; i < WS2812_SUM; i++ ) { rgb_SetColor(i,color_buff[i]); rgb_sendarray(); } rt_thread_mdelay(1000); } } /*线程创建函数*/ int ws2812_test(void) { rt_thread_t tid1; /*创建线程控制块指针来接收线程创建函数的返回值,目的是通过返回值判断线程是否创建ok*/ /* 创建线程 1,名称是 rgbled_test,入口是 rgbled_entry*/ tid1 = rt_thread_create("ws2812_test", /*线程名称,系统打印线程时会显示这个线程的名字*/ rgbled_entry, /*线程入口函数,入口函数函数名*/ RT_NULL, /*入口参数*/ 500, /*设置内存堆栈大小*/ 9, /*设置优先级*/ 500); /*时间片参数,时间片是在有多个相同优先级线程时,这个线程每次被执行多少个时间片*/ /* 如果获得线程控制块,启动这个线程 */ if (tid1 != RT_NULL) rt_thread_startup(tid1); return RT_EOK; } //导出到 msh 命令列表中 MSH_CMD_EXPORT(ws2812_test, ws2812 test); 配合上一期按键的内容,可以实现 单机--》红色 双击--》绿色 长按--》白色 DOME演示 [localvideo]6fdc77726700f213b861380bc19f8cb3[/localvideo]  

最近访客

< 1/4 >

统计信息

已有65人来访过

  • 芯积分:365
  • 好友:2
  • 主题:19
  • 回复:53

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言