- 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,不要拖延,执行力强一点