分享一款嵌入式开源LED指示灯控制代码框架cotLed

news2024/9/20 8:47:12

一、工程简介

        cotLed是一款轻量级的LED控制软件框架,可以十分方便地控制及自定义LED的各种状态,移植方便,无需修改,只需要在初始化时实现单片机硬件GPIO初始化,同时为框架接口提供GPIO写函数即可。

        框架代码工程地址:https://gitee.com/cot_package/cot_led

        工程代码结构也很精简,只用两个文件cot_led.c和cot_led.h实现具体功能,对外提供API接口函数,适用于裸机和带操作系统的应用代码。

图片

        通过该软件框架API,可以实现的LED控制接口功能有:

        (1)、单个LED的亮灭、翻转、闪烁、呼吸灯、自定义(如多少秒快闪几次等)等多种功能;

        (2)、多个LED组合的跑马灯、流水灯等功能;

        (3)、上述各个功能模式功能实现的次数设置。


二、工程代码分析

        在头文件cot_led.h中包括:

        (1)定义了LED亮灭的枚举cotLedState_e;

        (2)定义了写入IO状态的函数指针cotLedCtrl_f;

        (3)定义了LED核心控制功能成员变量的结构体cotLedProc_t;

        (4)外部声明的LED控制接口函数。

/* Define to prevent recursive inclusion -----------------------------------------------------------------------------*/
#ifndef _COT_LED_H_
#define _COT_LED_H_

/* Includes ----------------------------------------------------------------------------------------------------------*/
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>

/* Exported types ----------------------------------------------------------------------------------------------------*/

// 状态bit位所使用的byte数,bit之间的控制时间为时间颗粒度
#define LED_STATE_BYTE_NUM       4

/**
  * @brief  LED亮灭枚举定义
  */
typedef enum{
    COT_LED_OFF = 0,                     /*!< (0)灯灭 */
    COT_LED_ON = !COT_LED_OFF            /*!< (1)灯亮 */
} cotLedState_e;

typedef void (*cotLedCtrl_f)(cotLedState_e state);
typedef uint16_t led_t;

typedef struct
{
    uint8_t validBits;                 /*!< 状态有效bit位 */
    uint8_t offset;                    /*!< 状态bit位偏移 */
    uint8_t count;                     /*!< 控制次数 */
    uint8_t isSetCount : 1;            /*!< 是否设置了控制次数 */
    uint8_t defState : 1;              /*!< 默认状态 */
    uint8_t curState : 1;              /*!< 当前状态 */
    uint8_t pwmDir : 1;                /*!< PWM增减方向 */
    uint8_t isPwm : 1;                 /*!< PWM模式 */
    uint16_t tic;                      /*!< 时间计数器 */
    uint16_t interval;                 /*!< 每次控制的时间颗粒度,单位为1ms */
    union
    {
        struct
        {
            uint16_t onTime;               /*!< 亮的时长 */
            uint16_t tic;                  /*!< PWM计时 */
        } pwm;
        uint8_t state[LED_STATE_BYTE_NUM];  /*!< 状态bit位 */
    } data;
} cotLedProc_t;

typedef struct
{
    cotLedProc_t proc;
    cotLedCtrl_f pfnLedCtrl;
} cotLedCfg_t;

/* Exported constants ------------------------------------------------------------------------------------------------*/
/* Exported macro ----------------------------------------------------------------------------------------------------*/
/* Exported functions ------------------------------------------------------------------------------------------------*/

extern int cotLed_Init(cotLedCfg_t pCfgTable[], size_t num);

extern int cotLed_SetState(led_t led, cotLedState_e state);
extern int cotLed_SetStateWithTime(led_t led, cotLedState_e state, uint16_t time);

extern int cotLed_ON(led_t led);
extern int cotLed_OFF(led_t led);
extern int cotLed_Toggle(led_t led);

extern int cotLed_Twinkle(led_t led, uint16_t time);
extern int cotLed_TwinkleWithCount(led_t led, uint16_t time, uint8_t count, cotLedState_e defState);

extern int cotLed_Breathe(led_t led, uint16_t period);
extern int cotLed_BreatheWithCount(led_t led, uint16_t period, uint8_t count, cotLedState_e defState);

extern int cotLed_Custom(led_t led, ...);
extern int cotLed_CustomWithCount(led_t led, uint8_t count, cotLedState_e defState, ...);

extern int cotLed_Marquee(led_t led[], uint8_t ledNum, int32_t time);
extern int cotLed_MarqueeWithCount(led_t led[], uint8_t ledNum, int32_t time, uint8_t count, cotLedState_e defState);
extern int cotLed_Waterfall(led_t led[], uint8_t ledNum, int32_t time);
extern int cotLed_WaterfallWithCount(led_t led[], uint8_t ledNum, int32_t time, uint8_t count, cotLedState_e defState);

extern int cotLed_Ctrl(uint32_t sysTime);

#endif

        由于cot_led.c中代码比较多,这里暂时就不贴出,可以参考具体文件中的函数定义和注释内容。

        在头文件cot_led.c中包括:

        (1)呼吸灯软件模拟PWM频率;

        (2)在LED控制接口中需要用到的一些宏定义;

        (3)各类型的LED控制接口。

图片


三、工程代码应用

        工程代码的使用可以参考工程代码包中的examples和readme文件。这里以在stm32平台上使用带有freertos的应用代码中进行各个LED接口函数的测试,测试时使用宏开关TEST_LED_MODE控制分别进行接口功能测试。在应用代码中进行了接口使用的注释。

        应用的流程顺序为:

        (1)初始化各个LED对应GPIO口驱动。

        (2)定义各个LED的IO状态控制函数,调用LED控制初始化函数cotLed_Init,入参为各个LED的IO状态控制函数和LED数量。

        (3)创建周期执行逻辑代码,在带操作系统的代码中创建周期任务;而在裸机系统代码中则创建周期计数逻辑或定时器逻辑。

        (4)在周期执行逻辑代码中周期调用LED控制函数cotLed_Ctrl,入参为当前时间戳ms级的系统时长。

        (5)条件触发调用执行对应的各个LED控制接口函数。

        应用时的一些注意点如下:

        (1)调用接口函数时,不要重复调用,重复调用设置函数相关信息会复位,使用触发式调用即达到某一触发条件时调用接口一次。在实际测试时LED翻转接口cotLed_Toggle需要在循环中重复调用。

        (2)在调用的带时间设置入参的LED控制接口时,该时间需要设置为任务调度周期的倍数;裸机调用时设置为定时器或计数器周期的倍数。比如调度周期或计数器周期为500ms,为该接口设置的时间参数就为500*N。

        (3)如果设置某个LED为呼吸灯模式,则需要保证cotLed_Ctrl调用周期为1ms(优先级需要最高,或者定时器调度效果最好)。

        (4)在操作系统下使用时非线程安全,最好可以使用读写锁,如果没有读写锁则至少使用互斥锁。

#include "sys.h"
#include "delay.h"
#include "FreeRTOS.h"
#include "task.h"
#include "cot_led.h"

//任务优先级
#define START_TASK_PRIO     1
//任务堆栈大小    
#define START_STK_SIZE      128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define LED0_TASK_PRIO      2
//任务堆栈大小    
#define LED0_STK_SIZE       50  
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);

//任务优先级
#define LED1_TASK_PRIO      3
//任务堆栈大小    
#define LED1_STK_SIZE       50  
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);

typedef enum
{
    LED_0 = 0,
    LED_1,
    /* 勿删除,用来统计LED的数目 */
    LED_MAX_NUM
} LedType_e;

//LED的IO驱动初始化
void LED_Init(void)
{       
  GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);  //使能GPIOF时钟
  //GPIOF9,F10初始化设置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;  //LED0和LED1对应IO口
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;      //普通输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;    //100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;      //上拉
  GPIO_Init(GPIOF, &GPIO_InitStructure);          //初始化GPIO
  GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);      //GPIOF9,F10设置高,灯灭

}

void CtrlLed0(cotLedState_e state) //LED0的IO状态控制函数
{
    state == COT_LED_ON ? GPIO_ResetBits(GPIOF, GPIO_Pin_9) :  GPIO_SetBits(GPIOF, GPIO_Pin_9);
}

void CtrlLed1(cotLedState_e state) //LED1的IO状态控制函数
{
    state == COT_LED_ON ? GPIO_ResetBits(GPIOF, GPIO_Pin_10) :  GPIO_SetBits(GPIOF, GPIO_Pin_10);
}

void FML_LED_Init(void)
{
    static cotLedCfg_t s_ledTable[LED_MAX_NUM] = {
        {.pfnLedCtrl = CtrlLed0},
        {.pfnLedCtrl = CtrlLed1},
    };

    LED_Init();             //初始化LED驱动IO端口
    cotLed_Init(s_ledTable, LED_MAX_NUM); //LED控制初始化
}

int main(void)
{ 
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
    delay_init(168);        //初始化延时函数
    FML_LED_Init();
    
    //创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

#define TEST_LED_MODE  1   //用于测试各个LED控制接口的宏开关

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
#if ((TEST_LED_MODE >= 1) && (TEST_LED_MODE <= 6))
    //创建LED0任务
    xTaskCreate((TaskFunction_t )led0_task,         
                (const char*    )"led0_task",       
                (uint16_t       )LED0_STK_SIZE, 
                (void*          )NULL,              
                (UBaseType_t    )LED0_TASK_PRIO,    
                (TaskHandle_t*  )&LED0Task_Handler);                        
    vTaskDelete(StartTask_Handler); //删除开始任务
#else                               
    //创建LED1任务
    xTaskCreate((TaskFunction_t )led1_task,     
                (const char*    )"led1_task",   
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED1_TASK_PRIO,
                (TaskHandle_t*  )&LED1Task_Handler);    
#endif                              
    taskEXIT_CRITICAL();            //退出临界区
}

//LED任务函数 
void led0_task(void *pvParameters)
{       
#if (TEST_LED_MODE == 1)
    cotLed_Twinkle(LED_0, 2000); //设置LED0每隔2s进行闪烁(亮灭间隔时间)
    cotLed_Twinkle(LED_1, 5000); //设置LED1每隔5s进行闪烁(亮灭间隔时间
#elif (TEST_LED_MODE == 2)
  //设置LED0每隔2s秒进行闪烁,共5次(一亮一灭为一次),次数完成后灯灭
    cotLed_TwinkleWithCount(LED_0, 2000, 5, COT_LED_ON);
  //设置LED1每隔1s秒进行闪烁,共4次(一亮一灭为一次),次数完成后点亮
    cotLed_TwinkleWithCount(LED_1, 1000, 4, COT_LED_OFF);
#elif (TEST_LED_MODE == 3)
    cotLed_SetStateWithTime(LED_0, COT_LED_ON, 5000);//设置LED0亮持续5秒后熄灭
    cotLed_SetStateWithTime(LED_1, COT_LED_OFF, 3000);//设置LED1灭持续5秒后点亮
#endif      
    while(1)
    {
#if (TEST_LED_MODE == 4)
        cotLed_Toggle(LED_0);//周期翻转LED0
        cotLed_Toggle(LED_1);//周期翻转LED1   
#elif (TEST_LED_MODE == 5)          
    if (xTaskGetTickCount() == 0) {
      cotLed_ON(LED_0);//点亮LED0
      cotLed_ON(LED_1);//点亮LED1
    } else if (xTaskGetTickCount() == 5000) {
      cotLed_OFF(LED_0);//熄灭LED0
      cotLed_OFF(LED_1);//熄灭LED1
    } else if(xTaskGetTickCount() == 10000) {
      cotLed_ON(LED_0);
      cotLed_ON(LED_1);
    }
#elif (TEST_LED_MODE == 6)              
    if (xTaskGetTickCount() == 0) {
        cotLed_SetState(LED_0, COT_LED_ON);//点亮LED0
      cotLed_SetState(LED_1, COT_LED_ON);//点亮LED1
    } else if(xTaskGetTickCount() == 5000) {
      cotLed_SetState(LED_0, COT_LED_OFF);//熄灭LED0
      cotLed_SetState(LED_1, COT_LED_OFF);//熄灭LED1
    } else if(xTaskGetTickCount() == 10000) {
      cotLed_SetState(LED_0, COT_LED_ON);
      cotLed_SetState(LED_1, COT_LED_ON);
    }
#endif
        //周期调用LED控制函数,入参为当前时间戳ms级的系统时长
        cotLed_Ctrl(xTaskGetTickCount());
        vTaskDelay(100);
    }
} 

//LED1任务函数 
void led1_task(void *pvParameters)
{
#if (TEST_LED_MODE == 7)
  //LED0在2秒内1次快闪,亮100ms,灭1900ms,无限制次数
    cotLed_Custom(LED_0, 100, -1900, 0);
  //LED1在3秒内1次快闪,亮200ms,灭2800ms,无限制次数
    cotLed_Custom(LED_1, 200, -2800, 0);
#elif (TEST_LED_MODE == 8)
    //LED0在2秒内3次快闪,总共5次,次数完成后灯灭  
    cotLed_CustomWithCount(LED_0, 5, COT_LED_OFF, 100, -100, 100, -100, 100, -100, -1400, 0);
  //LED1在3秒内2次快闪,总共8次,次数完成后灯亮  
    cotLed_CustomWithCount(LED_1, 8, COT_LED_ON, 200, -200, 200, -200, -2200, 0);
#elif (TEST_LED_MODE == 9)      
    cotLed_Breathe(LED_0, 2000);//LED0在2秒内完成一次呼吸亮灭操作,一直执行不限次数
    cotLed_Breathe(LED_1, 3000);//LED1在3秒内完成一次呼吸亮灭操作,一直执行不限次数
#elif (TEST_LED_MODE == 10)
  //LED0在2秒内完成一次呼吸亮灭操作,总共3次,次数完成后灯灭
    cotLed_BreatheWithCount(LED_0, 2000, 3, COT_LED_OFF);
  //LED1在3秒内完成一次呼吸亮灭操作,总共5次,次数完成后灯亮
    cotLed_BreatheWithCount(LED_1, 3000, 5, COT_LED_ON);
#elif (TEST_LED_MODE == 11)
    led_t led[] = {LED_0, LED_1};
    cotLed_Waterfall(led, 2, 300);//流水灯一直执行,LED之间亮间隔300ms
#elif (TEST_LED_MODE == 12)
    led_t led[] = {LED_0, LED_1};
    //流水灯执行8次,LED之间亮间隔300ms,执行完后熄灭
    cotLed_WaterfallWithCount(led, 2, 300, 8, COT_LED_OFF);
#elif (TEST_LED_MODE == 13) 
     led_t led[] = {LED_0, LED_1};
    cotLed_Marquee(led, 2, 500);//跑马灯一直执行,LED之间亮间隔500ms
#elif (TEST_LED_MODE == 14) 
    led_t led[] = {LED_0, LED_1};
    //跑马灯执行5次,LED之间亮间隔500ms,执行完后熄灭
    cotLed_MarqueeWithCount(led, 2, 500, 5, COT_LED_OFF);
#endif
    while(1)
    {
    //周期调用LED控制函数,入参为当前时间戳ms级的系统时长
        cotLed_Ctrl(xTaskGetTickCount());
        vTaskDelay(1);
    }
}

↓↓↓更多技术内容和书籍资料获取,入群技术交流敬请关注“明解嵌入式”↓↓↓ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1919444.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++:重定义

派生类和基类的同名成员问题 派生类中再实现一个基类中的方法会怎样 (1)代码实验&#xff1a;派生类和基类中各自实现一个内容不同但函数原型完全相同的方法&#xff0c;会怎么样 (2)结论&#xff1a;基类对象调用的是基类的方法&#xff0c;派生类对象调用执行的是派生类中重…

Vue3 + Echarts堆叠折线图的tooltip不显示问题

问题介绍 使用Echarts在Vue3Vite项目中绘制堆叠折线图的的时候&#xff0c;tooltip总是不显示&#xff0c;经过很长时间的排查和修改&#xff0c;最后发现是在使用上有错误导致的。 错误图片展示 问题原因 由于Vue3底层使用proxy代理创建示例&#xff0c;使用其创建出来的实…

甘蔗基因组--文献精读30

A chromosomal-scale genome assembly of modern cultivated hybrid sugarcane provides insights into origination and evolution 现代栽培杂交甘蔗的染色体级基因组组装提供了起源和进化的洞见&#xff0c;确实甘蔗好几个基因组了~ 摘要 甘蔗是一种具有重要经济和工业价值…

提升论文质量和说服力的高阶技巧:数据驱动

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 今天分享的内容是“如何利用数据驱动的高阶技巧提升学术论文的深度与说服力”。从高级统计分析到机器学习&#xff0c;再到数据可视化和模型验证&#xff0c;我们将提供了一整套策略&am…

在Linux上搭建服务器之综合实验(web,dns,防火墙,SELinux)

其实验简图如下&#xff1a; 解读&#xff1a; 本实验需要完成4部分内容&#xff0c;web服务器的搭建&#xff0c;主从dns服务器的搭建&#xff0c;防火墙的开启&#xff0c;以及SELinux设置为强制模式。 首先dns主服务器上配置web服务&#xff08;其中我本机的IP为192.168.5.…

基于Android平台开发,仿头条新闻app

相关视频教程在某站上面(&#x1f50d;浩宇软件开发) 1. 项目模块功能思维导图 2. 项目涉及到的技术点 数据来源&#xff1a;聚合数据API使用okhttp网络请求框架获取api数据使用gson库解析json数据使用RecyclerViewadapter实现新闻列表使用SQLite数据库实现用户登录&#xff0…

数学建模·Topsis优劣解距离法

Topsis优劣解 一种新的评价方法&#xff0c;特点就是利用原有数据&#xff0c;客观性强。相较于模糊评价和层次评价 更加客观&#xff0c;充分利用原有数据&#xff0c;精确反映方案差距基本原理 离最优解最近&#xff0c;离最劣解越远具体步骤 正向化 代码与原理与熵权法…

防火墙第一次综合实验

DMZ区内的服务器&#xff0c;办公区仅能在办公时间内(9:00-18:00)可以访问&#xff0c;生产区的设备全天可以访问。 办公区设备10.8.2.1不允许访问DMZ区的FTP服务器和HTTP服务器&#xff0c;仅能ping通10.0.3.10 1.先建立拒绝BG到DMZ区的安全策略 2.建立BG到DMZ区的icmp允许 3…

探索 Electron:窗口菜单以及生命周期和对话框讲解

Electron是一个开源的桌面应用程序开发框架&#xff0c;它允许开发者使用Web技术&#xff08;如 HTML、CSS 和 JavaScript&#xff09;构建跨平台的桌面应用程序&#xff0c;它的出现极大地简化了桌面应用程序的开发流程&#xff0c;让更多的开发者能够利用已有的 Web 开发技能…

Linux文件编程(标准C库)

目录 一、标准C库打开/创建文件&#xff0c;读写文件&#xff0c;光标移动 二、标准C库写入结构体到文件 三、其他函数补充 1.fputc函数 2.feof函数和fgetc函数 前面讲到的open函数都是基于linux内核的&#xff0c;也就是说在Windows系统上无法运行&#xff0c;移植性比较…

shein测试开发会问些啥?

&#x1f3c6;本文收录于《CSDN问答解惑-》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&…

【thingsbord源码编译】 显示node内存不足

编译thingsbord显示报错 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory问题原因分析 重新安装java版本 编译通过

论文阅读【时空+大模型】ST-LLM(MDM2024)

论文阅读【时空大模型】ST-LLM&#xff08;MDM2024&#xff09; 论文链接&#xff1a;Spatial-Temporal Large Language Model for Traffic Prediction 代码仓库&#xff1a;https://github.com/ChenxiLiu-HNU/ST-LLM 发表于MDM2024&#xff08;Mobile Data Management&#xf…

无人机之遥控器保养

一、使用存放 1、避免让遥控器受到强烈的震动或从高处跌落&#xff0c;以免影响内部结构的精度&#xff1b; 2、遥控器在使用完后&#xff0c;需要将天线收拢&#xff0c;避免折断&#xff0c;养成定期检查天线的习惯&#xff1b; 3、定期检查遥控器按键有无裂纹、畸变、松旷…

跨境电商API的全球视野:打破地域限制,连接全球消费者与商家

在全球化日益加深的今天&#xff0c;跨境电商已成为推动全球经济一体化的重要力量。它不仅为消费者提供了前所未有的购物体验&#xff0c;让世界各地的商品触手可及&#xff0c;更为商家开辟了全新的市场蓝海&#xff0c;实现了业务的全球化拓展。在这一进程中&#xff0c;跨境…

PyTorch | 加速模型训练的妙招

引言 提升机器学习模型的训练速度是每位机器学习工程师的共同追求。训练速度的提升意味着实验周期的缩短&#xff0c;进而加速产品的迭代过程。同时&#xff0c;这也表示在进行单一模型训练时&#xff0c;所需的资源将会减少。简而言之&#xff0c;我们追求的是效率。 熟悉 PyT…

Rust代码优化的九大技巧

一.使用 Cargo 内置的性能分析工具 描述&#xff1a;Cargo 是 Rust 的包管理器&#xff0c;带有内置工具来分析代码性能&#xff0c;以识别性能瓶颈。 解释&#xff1a; 发布模式&#xff1a;在发布模式下编译启用优化&#xff0c;可以显著提高性能。 cargo build --release基…

解决vue多层弹框时存在遮挡问题

本文给大家介绍vue多层弹框时存在遮挡问题&#xff0c;解决思路首先想到的是找到对应的遮挡层的css标签&#xff0c;然后修改z-index值&#xff0c;但是本思路只能解决首次问题&#xff0c;再次打开还会存在相同的问题&#xff0c;故该思路错误&#xff0c;下面给大家带来一种正…

【鸿蒙学习笔记】文件管理

官方文档&#xff1a;Core File Kit简介 目录标题 文件分类什么是应用沙箱&#xff1f; 文件分类 应用文件&#xff0c;比如应用的安装包&#xff0c;自己的资源文件等。用户文件&#xff0c;比如用户自己的照片&#xff0c;录制的音视频等。 什么是应用沙箱&#xff1f; 应…

完美解决:MySQL8报错:Public Key Retrieval is not allowed

在配置数据源的时候直接将属性allowPublicKeyRetrieval设置为true即可 &AutoReconnecttrue