RT-Thread studio W25Q64 SFUD-FAL分区-存储参数+虚拟文件-ulog-RTC存储日志到文件-wifi-导出文件到电脑

第一章节:w25q64使用fal组件进行外部flash分区,并完成相关测试

第二章节:w25q64挂载文件系统配置,进行相关测试

第三章节:将日志信息存储于文件中,文件以年月日进行命名,每天生成一个日志

第四章节:在第三章基础上增加easyflash软件包实现键值对形式的数据存储和读取(分区一挂载文件系统存储日志信息,分区二通过键值对形式实现参数的存储)

第五章节:在第四章基础上增加agile_ftp软件包结合FileZilla软件将开发板日志信息导出到电脑(该方式更好用)

第六章节:在第四章接基础上增加netutils软件包软件包结合Tftpd64软件将日志导出文件到电脑

文章目录

一、W25Q64+FAL进行W25Q64(外部flash)分区

SPI-W25Q64配置及使用SFUD组件进行数据读写测试参考下文

RT-Thread studio SPI-W25Q64 SPI+SFUD+W25Q64

021基础上修改021-spak-device-spi-w25q64

通过 RT-Thread 提供的 FAL 组件对 Flash 进行分区管理操作,通过调用

FAL 接口完成了对指定分区的测试工作,完成了对 Flash 读、写、擦除测试。

FAL接口官方文档

(一)FAL组件+Error解决

1、使能fal组件

打开"rtthread studio studio"按照如图所示使能FAL组件,并且修改FAL组件设备名字"W25Q64"

保存编译程序后发现有错误

3、解决第一个报错:头文件缺少路径-需要手动增加

打开头文件在左上角可以看到头文件路径,增加对应的头文件路径即可

重新编译报错减少

4、解决第二个报错,将内部flash相关部分删除掉

删除内部flash之后如下所示:

5、再次编译程序 "error" 已经解决完毕

(二)根据开发板修改FAL代码

1、修改fal_cfg.h头文件

(1)修改外部flash分区表(分区表的名字可以随意命名,和后续使用保持一致即可)

(2)宏定义名修改为W25Q64

(3)fal_cfg.h头文件修改完毕后最终代码

2、修改W25Q64初始化函数

SPI片选和探测W25Q64设备成功红后,增加FAL组件初始化函数fal_init();

代码如下所示

复制代码
#include"rtthread.h"
#include"rtdevice.h"
#include"drv_spi.h"
#include"spi_flash_sfud.h"
#include"fal.h"


int w25q_sfud_test()
{
    //片选挂载设备
    rt_hw_spi_device_attach("spi2", "spi20", GPIOB, GPIO_PIN_12);
    //探测设备
    //使用sfud探测spi20设备,并将spi20连接的flash初始化为块设备,名称w25q64
    if(rt_sfud_flash_probe("W25Q64", "spi20")==RT_NULL)
    {
        rt_kprintf("w25q64 prob failed...\n");
        return -RT_ERROR;
    }
    fal_init();//W25Q64分区增加的fal初始化
return RT_EOK;
}

MSH_CMD_EXPORT(w25q_sfud_test,sfub test);

编译程序下载程序:输入w25q_sfud_test 指令调用函数进行W25Q64初始化及分区初始化。

运行结果如下所示,可以看到W25Q64已经按照设置的分区表被分为两个区域:

(1)分区一"param",分区2"filesystm"

(2)后续操作中分区一按照地址进行读写,用于存储参数;分区二挂载文件系统存储日志信息。

3、MSH测试指令

注意:在使用 fal 测试命令的时候,请先使用 fal probe 命令选择一个 Flash 分区

(1)输入"fal probe"可以查看W25Q64的分区情况

(2)使用 fal probe dev_name\|part_name 命令探测指定的 Flash 设备或者 Flash 分区,当探测到指定的 Flash 设备或分区后,会显示其属性信息。

输入"fal probe 分区名"可以查看该该分区的分区名,属于的flsah设备,偏移地址,分区大小;

(3)使用擦除命令时, addr 为相应探测 Flash 分区的偏移地址, size 为不超过该分区的值;

从地址0开始擦除4096个字节数据:输入"fal erase 0 4096"擦除一个扇区4096字节数据,擦除成功后相关位置被写为0XFF

(4)读取和写入测试

输入 fal read ,后面跟着待读取数据的起始地址以及长度,从 0 地址开始读取 10字节数据。

输入"fal read 0 10"表示从地址0开始读取10个数据。因为执行了擦除操作,所以数据值均为0XFF

输入"fal write 0 1 2 3 10 11 12"从地址0开始依次写入数据

输入"fal read 0 10"查看从地址0开始读取10个数据均可以发现相关位置数据已经被重新写入

(三)W25Q64分区+读写测试程序

修改代码如下所示:在fal初始化后增加读写测试函数

复制代码
#include"rtthread.h"
#include"rtdevice.h"
#include"drv_spi.h"
#include"spi_flash_sfud.h"
#include"fal.h"

int w25q_sfud_test()
{
    //增加写入数据和读取缓冲数组
    const uint8_t test_data[]={0x0b,0x0c,0x12,0x13,0x0e,0x11,0x02};
    uint8_t read_data[50]={0};

    //片选挂载设备
    rt_hw_spi_device_attach("spi2", "spi20", GPIOB, GPIO_PIN_12);
    //探测设备
    //使用sfud探测spi20设备,并将spi20连接的flash初始化为块设备,名称w25q64
    if(rt_sfud_flash_probe("W25Q64", "spi20")==RT_NULL)
    {
        rt_kprintf("w25q64 prob failed...\n");
        return -RT_ERROR;
    }
    fal_init();//W25Q64分区增加的fal初始化

    //增加读写操作
    // 查找param分区-进行数据读写测试
    const struct fal_partition *part = fal_partition_find("param");
    if (part)
    {

       fal_partition_erase(part, 0, 4096);
        // 直接读写,无需创建块设备
       if( fal_partition_write(part, 0, test_data, sizeof(test_data))<0)
       {
           rt_kprintf("write error\n");
       }
       else {
           rt_kprintf("write success\n");
    }

    if(fal_partition_read(part, 0, read_data, sizeof(test_data))<0)
    {
               rt_kprintf("read error\n");
           }
           else {
               rt_kprintf("read success \n");
               for(int i=0;i<sizeof(test_data);i++)
                   rt_kprintf("%#x ",read_data[i]);
        }
     }

return RT_EOK;
}

MSH_CMD_EXPORT(w25q_sfud_test,sfub test);

测试效果如下所示:输入"w25q_sfud_test"调用函数

二、W25Q64-虚拟文件系统挂载文件

DFS虚拟文件系统

(一)开启虚拟文件系统-设置DFS

(二)修改W25Q64初始化程序+挂载文件系统

注意:挂载文件系统前必须创建为块设备,要不然挂载失败,踩雷点!!!

在fal初始化函数后增加创建块设备+挂载文件系统

复制代码
    fal_blk_device_create("filesystem");//创建块设备(挂载文件系统必备)
    //格式化设备
    dfs_mkfs("elm","filesystem");
   // 直接挂载到根目录 /,而非 /spi
    if (dfs_mount("filesystem", "/", "elm", 0, 0) == 0) 
    {
        rt_kprintf("W25Q64 filesystem mount to / successful!\n");
    }
    else {
        rt_kprintf("W25Q64 filesystem mount retry\n");
        //格式化设备
        dfs_mkfs("elm","filesystem");
        if (dfs_mount("filesystem", "/", "elm", 0, 0) == 0) 
            rt_kprintf("W25Q64 filesystem mount to / successful!\n");
        else 
            rt_kprintf("W25Q64 filesystem mount to / failed!\n");
    }

运行程序发现提示缺少RTC设备:开启模拟RTC即可

重新编译下载:filesystem分区文件系统挂载成功,且param分区数据读写测试成功

对于挂载的文件系统进行msh指令测试:

新建目录,输出数据到文件,查看文件内容,删除文件,查看当前目录下文件

三、开启ulog组件:将日志信息存储到挂载的文件系统

(一)开启ulog组件

(二)增加RTC代码

复制代码
#include"rtthread.h"
#include"rtdevice.h"

#include"time.h"
#include <rtdbg.h>
#define RTC_NAME "rtc"
#define time_set 1001
rt_device_t rtc_device=RT_NULL;
rt_thread_t rtc_th;
struct tm *p;
void rtc_thread_fun(void *parameter)
{
    rt_err_t ret;
//    time_t now_time;
    time_t timep;

    //1-查找设备
   rtc_device=rt_device_find(RTC_NAME);
   if(rtc_device==RT_NULL)
   {
       rt_kprintf("rtc device find failed..\n");
   }
   else {
    rt_kprintf("rtc device find success...\n");
   }
   //2-初始化RTC设备
   ret=rt_device_open(rtc_device, 0);
   if(ret!=RT_EOK)
   {
       rt_kprintf("rtc_device open failed..\n");
   }
   else {
       rt_kprintf("rtc device open success...\n");
   }

   if(time_set!=1000)
   {
       //3-设置日期
       ret=set_date(2024, 01, 31);
       if(ret!=RT_EOK)
       {
           rt_kprintf("RTC set date fail...\n");
       }
       else
       {
           rt_kprintf("RTC set date ok...\n");
       }

       //4-设置时间
       ret=set_time(13, 37, 13);
       if(ret!=RT_EOK)
       {
           rt_kprintf("RTC set time fail...\n");
       }
       else
       {
           rt_kprintf("RTC set time ok...\n");
       }
   }
    while(1)
    {
//
//        now_time=time(RT_NULL);
//       //        /* 打 印 输 出 时 间 信 息 */
//               rt_kprintf("%s ", ctime(&now_time));
                  time(&timep);
                  p = localtime(&timep);
//                  printf("%d-%d-%d %d:%d:%d\n", (1900 + p->tm_year), ( 1 + p->tm_mon), p->tm_mday,
//                                              (p->tm_hour+12 ), p->tm_min, p->tm_sec);
        rt_thread_mdelay(1000);//延时1s
    }
}

int rtc_thread_test(void)
{
    rt_err_t ret;
    rtc_th=rt_thread_create("RTC_thread",rtc_thread_fun,RT_NULL,1024,6,5);
    if(rtc_th==RT_NULL)
    {
        rt_kprintf("rtc_th create failed..\n");
    }
    else
    {
        rt_kprintf("rtc_th create success..\n");
    }
    ret=rt_thread_startup(rtc_th);//开启任务调度
    if(ret==RT_EOK)
    {
        rt_kprintf("rtc_th start success..\n");
    }
    return RT_EOK;
}

INIT_APP_EXPORT(rtc_thread_test);

(三)增加ulog存储日志到文件代码

复制代码
#include <rtthread.h>
#include <ulog_be.h>
#include <rtdevice.h>
#include <ulog_be.h>
#include "dfs_fs.h"
#include "ulog_def.h"


//新建一个注册表,把目录,文件名,大小,数量写入表内
// 后端注册表
struct _log_file
{
    const char *prefix;               // 文件前缀(替代原固定name)
    ulog_backend_t backend;
    struct ulog_file_be *file_be;
    const char *dir_path;             // 保存路径
    rt_size_t max_num;                // 保存最大文件数量
    rt_size_t max_size;               // 保存最大文件大小
    rt_size_t buf_size;               // 文件保存缓存大小
};

/*
* 文件后端标识
*/
typedef enum
{
    sys_id = 0,
    dht_id = 1,
}ulog_file_be_name;

#define ROOT_PATH "/day" // 设置保存的路径
#define FILE_SIZE 1024 * 1024   // 单个文件大小(可根据需求调整)
//BUFF_SIZE代表多少大小再进行一次写入,缓冲区太大,会很久才更改一次;
//太小会写入太频繁,导致ULOG异步线程堆栈不够溢出
#define BUFF_SIZE 20       // 设备缓存区大小

static struct ulog_backend sys_log_backend;
static struct ulog_file_be sys_log_file;

static struct ulog_backend dht_log_backend;
static struct ulog_file_be dht_log_file;

// 修改table:name改为prefix(文件前缀)
static struct _log_file table[] =
{
    {""      ,&sys_log_backend,&sys_log_file,ROOT_PATH,10,FILE_SIZE,BUFF_SIZE},
};

extern struct tm *p;//时间结构体

/**
 * @brief  获取当前日期并生成带日期的文件名
 * @param  prefix: 文件前缀(如sys、dht、ulog等)
 * @param  buf: 存储生成的文件名缓冲区
 * @param  buf_len: 缓冲区长度
 * @retval RT_EOK: 成功,-RT_ERROR: 失败
 */
//修正文件名生成函数20210225010305
static int get_date_filename(const char *prefix, char *buf, rt_size_t buf_len)
{
    rt_snprintf(buf, buf_len, "%s%04d%02d%02d%02d%02d%02d",
                prefix,
                1900 + p->tm_year, // tm_year 是 1900 年至今的年数
                p->tm_mon + 1,
                p->tm_mday,p->tm_hour,p->tm_min,p->tm_sec);
    return RT_EOK;
}

/**
 * @brief  初始化日志文件后端(带日期文件名)
 * @param  id: 日志标识(sys_id/dht_id)
 * @retval RT_EOK: 成功,其他: 失败
 */
static int log_file_backend_init_with_date(ulog_file_be_name id)
{
    char filename[18] = {0}; // 存储带日期的文件名
    struct _log_file *log_cfg = &table[id];
    struct ulog_file_be *file_be = log_cfg->file_be;
    int ret;
    // 生成带日期的文件名
    ret = get_date_filename(log_cfg->prefix, filename, sizeof(filename));
    if (ret != RT_EOK)
    {
        return ret;
    }

    // 设置父后端
    file_be->parent = *log_cfg->backend;
    // 初始化文件后端(使用动态生成的日期文件名)
    ulog_file_backend_init(file_be,
                           filename,          // 带日期的文件名
                           log_cfg->dir_path, // 保存路径
                           log_cfg->max_num,
                           log_cfg->max_size,
                           log_cfg->buf_size);
    // 使能文件后端(init后一定要enbale,不然没有运行)
    ulog_file_backend_enable(file_be);
    rt_kprintf("Log backend %s init success, file: %s/%s\n",
               log_cfg->prefix, log_cfg->dir_path, filename);
    return RT_EOK;
}

/**
  * @brief  系统日志文件后端初始化(带日期文件名)
  * @retval RT_EOK: 成功
  */
int sys_log(void)
{
    return log_file_backend_init_with_date(sys_id);
}
MSH_CMD_EXPORT(sys_log, sys log init with date filename)

//在重新初始化前,必须先销毁旧的日志后端,随后创建新的!!!!!
static void log_date_check_thread(void *param)
{
    rt_uint8_t last_day = p->tm_mday;
    while (1)
    {
        if (p->tm_mday != last_day)
        {
            rt_kprintf("Date changed, reinit log backend...\n");


            // 先销毁旧的后端(关键修复)
            ulog_file_backend_disable(&sys_log_file);
            ulog_file_backend_deinit(&sys_log_file);

            // 再初始化新的后端
            sys_log();
            last_day = p->tm_mday;
        }
        rt_thread_mdelay(4 * 1000); // 1分钟或更长时间检查一次更稳妥(为测试方便修改为4s检查一次)
    }
}

/**
 * @brief  启动日志日期检查线程(可选)--判断是否需要开启新的日志
 */
int log_date_check_init(void)
{
    rt_thread_t tid;

    tid = rt_thread_create("log_date",
                           log_date_check_thread,
                           RT_NULL,
                           1024, // 栈大小
                           25,   // 优先级
                           10);  // 时间片
    if (tid != RT_NULL)
    {
        rt_thread_startup(tid);
        rt_kprintf("Log date check thread start success!\n");
        return RT_EOK;
    }
    return -RT_ERROR;
}
// 自动初始化日志检查线程(可选,开机启动)
INIT_APP_EXPORT(log_date_check_init);

在main函数或者其他地方开启打印

(四)编译运行程序

1、输入指令调用初始化程序:w25q_sfud_test

2、ls查看当前路径并无文件

3、运行"sys_log"之后再"ls"查看当前目录(此时会将日志信息存储于文件中)

4、"cd day" 进入目录使用"ls指令查看",可以看到已经生成了日志文件

5、但是文件命名不太对!!!

经过多次修改排查发现是设置中对象名称最大长度默认为8导致的(踩雷点),将其改为20保存编译重新下载。

6、使用"date"日期时间 指令修改一个不一样的日期

(1)例如设置2022年2月5日一个时间点:

复制代码
date 2022 02 05 13 25 12

(2)再次修改为date 2022 02 05 23 59 50

设置一个2022年2月5日23:59:50的时间,使其自动跳转到新的一天,则会生成一个2022年2月6日的日志(代码中在重新初始化前,先销毁旧的日志后端,随后创建新的!!!!!)

(3)可以输入cat 20220206000001.log查看该天的日志信息

最终代码021-01-spak-device-spi-w25q64-addFAL-filesystem-logfile-ok

四、W25Q64-FAL-DFS-easyflash

在第二章基础上增加一个分区,用于easyflash软件包的操作,通过键值对的方式进行写入数据,读取数据,修改数据操作

(一)新增easyflash软件包

(二)修改easyflash代码

1、fal_cfg.h文件flash分区表中新增easyflash分区

2、easyflash软件包代码

easyflash软件包将ef_fal_port.c从ports文件夹中移动到src文件夹,并且修改宏定义"FAL_EF_PART_NAME"的值修改为"easyflash",宏定义值只要修改为分区表中的分区名一致

3、修改w25q64初始化函数代码

在fal初始化函数之后调用easyflash初始化函数:easyflash_init()

w25q64文件修改如下所示:

复制代码
#include"rtthread.h"
#include"rtdevice.h"
#include"drv_spi.h"
#include"spi_flash_sfud.h"
#include"fal.h"


#include"easyflash.h"

static void easyflash_test_env(void)
{
    uint32_t i_boot_times = 0;
    char *c_old_boot_times, c_new_boot_times[11] = {0},mode_valve[2]={0};

    /* 从环境变量中获取启动次数 */
    c_old_boot_times = ef_get_env("boot_times");
    /* 获取启动次数是否失败 */
    if (c_old_boot_times == RT_NULL)
        c_old_boot_times[0] = '0';

    i_boot_times = atol(c_old_boot_times);
    /* 启动次数加 1 */
    i_boot_times++;
    LOG_I("===============================================");
    LOG_I("The system now boot %ld times", i_boot_times);
    LOG_I("===============================================");
    /* 数字转字符串 */
    sprintf(c_new_boot_times, "%ld", i_boot_times);
    /* 保存开机次数的值 */
    ef_set_env("boot_times", c_new_boot_times);
    ef_save_env();



    /* 新增-保存mode_value数值*/
    sprintf(mode_valve, "%d", 2);
    ef_set_and_save_env("mode_valve", mode_valve);
    LOG_I("mode_valve=%d\n", atol(ef_get_env("mode_valve")));
    
    sprintf(mode_valve, "%d", 5);//修改数值重新打印
    ef_set_and_save_env("mode_valve", mode_valve);
    LOG_I("mode_valve=%d\n", atol(ef_get_env("mode_valve")));
    

}



int w25q_sfud_test()
{
/*************************SPI-W25Q64探测***********************************/
    //增加写入数据和读取缓冲数组
    const uint8_t test_data[]={0x0b,0x0c,0x12,0x13,0x0e,0x11,0x02};
    uint8_t read_data[50]={0};

    //片选挂载设备
    rt_hw_spi_device_attach("spi2", "spi20", GPIOB, GPIO_PIN_12);
    //探测设备
    //使用sfud探测spi20设备,并将spi20连接的flash初始化为块设备,名称w25q64
    if(rt_sfud_flash_probe("W25Q64", "spi20")==RT_NULL)
    {
        rt_kprintf("w25q64 prob failed...\n");
        return -RT_ERROR;
    }

/***************************W25Q64-FAL分区初始化***********************************/


    fal_init();//W25Q64分区增加的fal初始化

/******************FAL分区初始化后创建块设备-挂载文件系统***************************/

    fal_blk_device_create("filesystem");//创建块设备(挂载文件系统必备)
    //格式化设备
    dfs_mkfs("elm","filesystem");
   // 直接挂载到根目录 /,而非 /spi
    if (dfs_mount("filesystem", "/", "elm", 0, 0) == 0)
    {
        rt_kprintf("W25Q64 filesystem mount to / successful!\n");
    }
    else {
        rt_kprintf("W25Q64 filesystem mount retry\n");
        //格式化设备
        dfs_mkfs("elm","filesystem");
        if (dfs_mount("filesystem", "/", "elm", 0, 0) == 0)
            rt_kprintf("W25Q64 filesystem mount to / successful!\n");
        else
            rt_kprintf("W25Q64 filesystem mount to / failed!\n");
    }


/******************FAL分区初始化后查找分区,进行擦除读写测试--调用的FAL接口函数***********************************/

    //增加分区后的读写操作
    // 查找param分区-进行数据读写测试
    const struct fal_partition *part = fal_partition_find("param");
    if (part)
    {

       fal_partition_erase(part, 0 , 4096);
        // 直接读写,无需创建块设备
       if( fal_partition_write(part, 0 , test_data, sizeof(test_data))<0)
       {
           rt_kprintf("write error\n");
       }
       else {
           rt_kprintf("write success\n");
    }

    if(fal_partition_read(part, 0 , read_data, sizeof(test_data))<0)
    {
               rt_kprintf("read error\n");
           }
           else {
               rt_kprintf("read success \n");
               for(int i=0;i<sizeof(test_data);i++)
                   rt_kprintf("%#x ",read_data[i]);
        }
     }

/*****************************FAL分区后使用easyflash软件包根据键值对进行数据写入和读取********************************/
    if(easyflash_init()==EF_NO_ERR)
    {
        easyflash_test_env();//测试函数
    }

    return RT_EOK;
}

MSH_CMD_EXPORT(w25q_sfud_test,sfub test);

输入w25q_sfud_test调用函数:

五、agile_ftp软件包将开发板日志文件导出到电脑

021-03-spak-device-spi-w25q64-addFAL-filesystem-logfile-easyflashkey2-ftp

(一)rw007-wifi软件包(板子上自带的wifi模块)

(二)agile_ftp软件包

1、使能agile_ftp软件包

2、增加对应头文件+调用初始化函数

复制代码
#include "ftp.h"
ftp_init(2048, 27, 100);

3、代码中-默认端口:21

4、登录用户名和密码默认都是"loogg"

(三)方法一:电脑使用WIN+E进入 WINDOWS 资源管理器地址栏输入IP地址获取开发板日志文件

1、输入对应的IP地址回车

复制代码
输入:ftp://192.168.52.148回车
IP地址是开发板实际ip地址(WIFI连接手机开的热点后会自动分配一个IP),电脑连的手机热点,和开发板IP是同一个网段。

2、回车之后即可看到开发板中的目录文件,右击登录输入账号和密码,账号和密码都是"loogg"

3、找到需要查看的日志文件将其选中复制到电脑上即可

(四)方法二:使用FileZilla软件进行操作,将所需日志文件下载到电脑

FileZilla软件下载地址

1、打开软件输入账号和密码

打开软件输入主机服务器地址(开发板的IP地址),输入账号(默认"loogg")和密码默认"loogg"以及端口(默认21),点击连接即可连接成功,即可通过鼠标进行控制

2、将设备中的文件导出到电脑,在远程端选择要导出的文件右击"下载即可"

3、将电脑上的文件上传到设备目录,选中文件右击上传即可将其传送到设备目录中

在下方可以看到传输的进度,传输进度为100%即为传输完成

4、传输完毕之后在设备端可以看到该文件(传输完毕未显示可以右击刷新)

在开发板串口调试中也可以输入"ls"指令查看当前目录下是否有该文件

六、使用netutils软件包操作导入导出文件

该软件包可以实现功能如下:

1、将文件从开发板中导出到电脑

2、将电脑文件导入到开发板

3、需要Tftpd64软件协助进行(设置好参数后即可使用);串口貌似不支持linux一样输入指令主动推送文件(尝试多次未成功),总体而言不如"agile_ftp软件包+FileZilla软件"操作方便。

netutils软件包其他功能参考下面文章第二章部分

RT-Thread studio STM32F407+RW007模块/物联网软件包/阿里云

相关推荐
艺杯羹6 天前
公共 Wi-Fi 流量安全全解析:从明文监听、流量拦截到 HTTPS 降级攻防
网络·web安全·网络安全·wifi
W.W.H.7 天前
嵌入式 Linux外接USB/WIFI模块兼容5G频段实战
linux·运维·5g·wifi
欢乐熊嵌入式编程2 个月前
WIFI通信协议全解析18: ESP32 作为 AP 热点:打造自己的“微型路由器”(附完整实战代码)
物联网·wifi·esp32·蓝牙·wifi协议·ap热点
天南散修2 个月前
MT7916 BA流程
网络·驱动开发·wifi·802.11
天南散修2 个月前
MT7916驱动中802.11转换为802.3
linux·网络·驱动开发·wifi·802.11
阿钱真强道2 个月前
27 鸿蒙LiteOS RK2206 设备连路由器完整实战(配网+联网验证+网络连通性测试)
wifi·harmonyos·鸿蒙·rk·liteos·开源鸿蒙·瑞芯微
星恒讯工业路由器2 个月前
物联网网关天线:分类解析与信号质量认知误区
网络·物联网·wifi·信息与通信·wifi 天线·lora 天线·物联网关天线
星恒讯工业路由器2 个月前
WiFi 安全技术演进全解析:从 WEP 到 WPA3 的迭代与安全蜕变
网络·安全·wifi·信息与通信
桑榆肖物2 个月前
ImprovWifi 跨平台传输层设计:把协议层做薄,把宿主层做稳
嵌入式硬件·wifi·.net·ble