空耳-

  • 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]  

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

    lugl4313820 发表于 2025-3-1 21:09 RT-Thread给定了基本的配置,不需要重复造轮子,用起来还是非常方便的。再加上是操作系统,消抖也变得得非 ... 主要是我rtt,不太会用,用的不熟 想单击,双击,长按都是很好实现的,可以自己写一个简单的,也可以移植开源的按键驱动框架

  • 2025-03-01
  • 发表了主题帖: 【先楫HPM5361】01_GPIO篇(一)

    本帖最后由 空耳- 于 2025-3-2 03:55 编辑 按键输入 我们在上次的工程上新建一个文件夹命名为Key,在这个文件夹下进行读取按键输入功能的演示。 这里还是采用RT-Thread studio的开发环境。 PIN设备 首先了解一下PIN设备,这里官方文档有很详细的说明:PIN设备 以下为几个常用的函数接口:   按键功能实现 确定按键引脚和触发状态     通过原理图可以清晰的看到引脚为Pa09,低电平触发。 主要代码 #include <rtthread.h> #include "rtdevice.h" #include "rtt_board.h" #define THREAD_PRIORITY 25 // 线程优先级,数字越大优先级越高 #define THREAD_STACK_SIZE 512 // 线程堆栈大小,决定了线程可以使用的内存空间 #define THREAD_TIMESLICE 5 // 线程时间片,决定了线程在调度时能占用CPU的最长时间 static rt_thread_t key_thread = RT_NULL; // 声明线程控制块指针,用于后续创建和管理线程 static rt_base_t user_key_pin = RT_NULL; // 声明引脚编号变量,初始化为NULL,用于存储LED的引脚编号 // 线程入口函数,这是线程启动后执行的函数 static void key_thread_entry(void *parameter) { static rt_uint8_t key_up = 1; /* 按键松开标志 */ static rt_uint8_t led_flag = 1; app_init_led_pins(); user_key_pin = rt_pin_get("PA09"); // 获取名为"PA09"的引脚编号 if ((user_key_pin == -RT_EINVAL) || (user_key_pin == -RT_ENOSYS)) // 如果获取失败,打印错误信息并返回 { rt_kprintf("Failed to get the pin PA.09\n"); return; // 获取引脚失败,直接返回,避免后续操作导致错误 } /* 初始化按键 */ rt_pin_mode(user_key_pin, PIN_MODE_INPUT); while (1) { /* 检测按键是否按下 */ if (key_up && (rt_pin_read(user_key_pin) == PIN_LOW) ) { rt_thread_mdelay(50); /* 延时消抖*/ key_up = 0; if (rt_pin_read(user_key_pin) == PIN_LOW) { /* 按键KEY按下,按键按下处理*/ rt_kprintf("KEY pressed!\n"); (led_flag) ? app_led_write(0, APP_LED_OFF) : app_led_write(0, APP_LED_ON); led_flag ^= 1; } } else if((rt_pin_read(user_key_pin) == PIN_HIGH) ) { key_up = 1; /* 按键已松开 */ } rt_thread_mdelay(100); } } /* 线程初始化*/ int key_init(void) { /* 创建线程,名称是 key_thread,入口是 key_thread*/ key_thread = rt_thread_create("key_thread", key_thread_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); /* 如果获得线程控制块,启动这个线程 */ if (key_thread != RT_NULL) rt_thread_startup(key_thread); return 0; } //导出到 msh 命令列表中 MSH_CMD_EXPORT(key_init, key init); // 导出函数自动运行,在系统初始化时调用key_init函数 //INIT_APP_EXPORT(key_init); 单机双击长按的简单实现: 优点:简单 缺点:对于三连击和四连击,容易误触,而且多个按键时不方便管理 typedef struct{ uint8_t curKey; uint8_t keyDown; uint8_t keyUsed; uint16_t debance; uint16_t timer; } SW_KEY_T; #define KEY_DEBANCE_CNT 5 #define KEY_LONG_CNT 300 #define KEY_DUB_TIME 30//80 #define THREAD_PRIORITY 25 // 线程优先级,数字越大优先级越高 #define THREAD_STACK_SIZE 512 // 线程堆栈大小,决定了线程可以使用的内存空间 #define THREAD_TIMESLICE 5 // 线程时间片,决定了线程在调度时能占用CPU的最长时间 static rt_thread_t key_thread = RT_NULL; // 声明线程控制块指针,用于后续创建和管理线程 static rt_base_t user_key_pin = RT_NULL; // 声明引脚编号变量,初始化为NULL,用于存储LED的引脚编号 SW_KEY_T g_key; void Drv_key_10ms_scan(void) { if((rt_pin_read(user_key_pin) == PIN_LOW)) // key 按下 { g_key.debance++; if(g_key.debance >= KEY_DEBANCE_CNT) // 50ms g_key.keyDown = true; } else // key 松开 { if(g_key.keyDown && g_key.keyUsed == false) { g_key.curKey++; if(g_key.curKey == 1) { g_key.timer = KEY_DUB_TIME; } } g_key.debance = 0; g_key.keyDown = false; g_key.keyUsed = false; } if(g_key.debance >= KEY_LONG_CNT && g_key.keyUsed == false)// 长按 { //key_long(); rt_kprintf("key_long\n"); g_key.keyUsed = true; } if(g_key.timer) { g_key.timer--; if( g_key.timer == 0) { if(g_key.curKey == 1) { //key_short(); // 短按一次 rt_kprintf("key_short\n"); } else if(g_key.curKey == 2) { //key_double(); // 短按两次 rt_kprintf("key_double\n");//key_double(); // 短按两次 } g_key.curKey = 0; } } } // 线程入口函数,这是线程启动后执行的函数 static void key_thread_entry(void *parameter) { static rt_uint8_t key_up = 1; /* 按键松开标志 */ static rt_uint8_t led_flag = 1; app_init_led_pins(); user_key_pin = rt_pin_get("PA09"); // 获取名为"PA09"的引脚编号 if ((user_key_pin == -RT_EINVAL) || (user_key_pin == -RT_ENOSYS)) // 如果获取失败,打印错误信息并返回 { rt_kprintf("Failed to get the pin PA.09\n"); return; // 获取引脚失败,直接返回,避免后续操作导致错误 } /* 初始化按键 */ rt_pin_mode(user_key_pin, PIN_MODE_INPUT); while (1) { #if 0 /* 检测按键是否按下 */ if (key_up && (rt_pin_read(user_key_pin) == PIN_LOW) ) { rt_thread_mdelay(50); /* 延时消抖*/ key_up = 0; if (rt_pin_read(user_key_pin) == PIN_LOW) { /* 按键KEY按下,按键按下处理*/ rt_kprintf("KEY pressed!\n"); (led_flag) ? app_led_write(0, APP_LED_OFF) : app_led_write(0, APP_LED_ON); led_flag ^= 1; } } else if((rt_pin_read(user_key_pin) == PIN_HIGH) ) { key_up = 1; /* 按键已松开 */ } #else Drv_key_10ms_scan(); #endif rt_thread_mdelay(10); } } /* 线程初始化*/ int key_init(void) { /* 创建线程,名称是 key_thread,入口是 key_thread*/ key_thread = rt_thread_create("key_thread", key_thread_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); /* 如果获得线程控制块,启动这个线程 */ if (key_thread != RT_NULL) rt_thread_startup(key_thread); return 0; } //导出到 msh 命令列表中 MSH_CMD_EXPORT(key_init, key init); // 导出函数自动运行,在系统初始化时调用usr_led_run函数 //INIT_APP_EXPORT(key_init); 按键驱动框架的移植:https://github.com/0x1abin/MultiButton 详细的过程大家可以clone一下,我这里只列举了使用的方式 优点:真的优秀,非常方便移植,和多按键管理 /**************************************************按键驱动框架******************************************/ struct Button btn1; uint8_t Read_Button_State(uint8_t button_id) { return ((rt_pin_read(user_key_pin))) ; } void Btn1_Single_Click_Handler(void *i) { rt_kprintf("Btn1_Single_Click\n"); } void Btn1_Double_Click_Handler(void *i) { rt_kprintf("Btn1_Double_Click\n"); } void Btn1_Long_Press_Hold_Handler(void *i) { rt_kprintf("Btn1_Long_Press_Hold\n"); } void key_scan(void) { button_init(&btn1, Read_Button_State, 0, 1); button_attach(&btn1, SINGLE_CLICK, Btn1_Single_Click_Handler); button_attach(&btn1, DOUBLE_CLICK, Btn1_Double_Click_Handler); button_attach(&btn1, LONG_PRESS_HOLD, Btn1_Long_Press_Hold_Handler); button_start(&btn1); } // 线程入口函数,这是线程启动后执行的函数 static void key_thread_entry(void *parameter) { static rt_uint8_t key_up = 1; /* 按键松开标志 */ static rt_uint8_t led_flag = 1; app_init_led_pins(); user_key_pin = rt_pin_get("PA09"); // 获取名为"PA09"的引脚编号 if ((user_key_pin == -RT_EINVAL) || (user_key_pin == -RT_ENOSYS)) // 如果获取失败,打印错误信息并返回 { rt_kprintf("Failed to get the pin PA.09\n"); return; // 获取引脚失败,直接返回,避免后续操作导致错误 } /* 初始化按键 */ rt_pin_mode(user_key_pin, PIN_MODE_INPUT); key_scan(); while (1) { #if 0 /* 检测按键是否按下 */ if (key_up && (rt_pin_read(user_key_pin) == PIN_LOW) ) { rt_thread_mdelay(50); /* 延时消抖*/ key_up = 0; if (rt_pin_read(user_key_pin) == PIN_LOW) { /* 按键KEY按下,按键按下处理*/ rt_kprintf("KEY pressed!\n"); (led_flag) ? app_led_write(0, APP_LED_OFF) : app_led_write(0, APP_LED_ON); led_flag ^= 1; } } else if((rt_pin_read(user_key_pin) == PIN_HIGH) ) { key_up = 1; /* 按键已松开 */ } #else // Drv_key_10ms_scan(); button_ticks(); #endif rt_thread_mdelay(10); } } 两种方式都可以有效识别按键的长按,单机,双击 驱动框架的   普通简易方式的     效果展示 可以看到按键按一下led亮,再按一下led灭   [localvideo]c84664945ce5684a8b3ffa078801192b[/localvideo] 问题记录 新创建的文件,因为rt-thread studio 同步scons后文件不见 解决方法: 先让被排除构建的资源显示出来常见问题 文件添加构建 右键灰色的文件或者文件夹--->资源配置--->添加构建 该说不说,这种导出到 msh 命令列表的方式真的太舒服了,可以选择执行那个模块任务。

  • 2025-02-22
  • 回复了主题帖: 【先楫HPM5361】00_开箱及环境搭建(RT-Thread studio篇)

    lugl4313820 发表于 2025-2-19 07:56 不用造轮子,那是真方便,我想问一下,他这个板子自带下载调试器吗? 是的,自带了一个FT2232调试器

  • 2025-02-19
  • 发表了主题帖: 【先楫HPM5361】00_开箱及环境搭建(RT-Thread studio篇)

    【先楫HPM5361】环境搭建 1.安装驱动   这样证明驱动安装成功   2.下载RT-Thread studio rt-thread.org/studio.html 不要有中文路径,无脑安装就行   2.下载板级支持包和依赖 点击这个图标   下载板级支持包   下载编译工具链   下载调试工具        这样环境就搭建完了,接下来新建一个工程点个灯试试。 3.新建工程 点击右侧的创建RT-Thread项目,然后按图试操作即可   4.添加串口打印,就两个步骤,很简单 main.c添加头文件 #include <stdio.h> 循环中添加要打印的内容 printf("hello wrold !\n"); 5.开箱演示         6.问题和总结 本次的开箱和环境搭建就先到这里了,其实还是比较简单的。唯一需要注意的点就是:官方默认是使用外部JTAG的,所以5个跳线帽是断开的,我们这里使用的是板载DEBUG,所以需要短接    我在这里还是浪费了不少时间!

  • 2025-01-20
  • 回复了主题帖: 【测评入围名单(最后1批)】年终回炉:FPGA、AI、高性能MCU、书籍等65个测品邀你来~

    个人信息无误,可按时完成测评计划。

  • 2025-01-14
  • 回复了主题帖: 【STM32H7S78-DK测评】1.收到板卡,简单测评

    羡慕啊

  • 2025-01-13
  • 加入了学习《【Follow me第二季第4期】Arduino? Nano RP2040 Connect活动项目总结》,观看 【Follow me第二季第4期】Arduino Nano RP2040 Connect活动项目总结

  • 2025-01-11
  • 回复了主题帖: 【回顾2024,展望2025】新年抢楼活动来啦!

    (5)立一个新年flag,不要拖延,执行力强一点

最近访客

< 1/4 >

统计信息

已有58人来访过

  • 芯积分:300
  • 好友:2
  • 主题:15
  • 回复:49

留言

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


现在还没有留言