嵌入式协程AlarmProtothread

AlarmProtothread 在完整保留 Protothread

原有 API 语义的前提下,对其时间模型进行了修改。
Protothread 采用固定 1 ms tick 的协作式调度机制,本质上是一种低时间分辨率的时间片模型,难以准确描述或调度 µs 级事件。

AlarmProtothread 引入基于硬件 Alarm的精确定时机制,使协程调度不再依赖统一的毫秒节拍,而是由事件触发驱动,从而在保持轻量协程模型的同时,支持更高时间精度的异步控制逻辑。

例子

例1: 产生us精度的pwm

如果AlarmProtothread的Run函数不能一次执行完,则它不允许有栈变量

c 复制代码
/**
 * LedPt0的Run完整执行周期是80us
 * 大多数情况,遇到PT_开头的函数Run函数会直接返回
 * LedPt0执行流程
 * 20us
 * 翻转
 * 60us
 * 翻转
 */
class LedPt0 : public AlarmProtothread {
    void Init() {
        AT_println("LedPt0 init");
        ledp0cnt=0;
    }

    bool Run() {
        static uint32_t last_us=mi_hw_alarm_get_tick_us();
        static uint32_t now = mi_hw_alarm_get_tick_us();
        WHILE(1){
                PT_DELAY(20);
                gpio_xor_mask(1u << LED_PIN);
                PT_DELAY(60);
                gpio_xor_mask(1u << LED_PIN);
                now = mi_hw_alarm_get_tick_us();
                dif_led0_us=now-last_us;
                last_us=now;
            }
        PT_END();
    }
};

例2: 协程间通讯

在通信机制上,AlarmProtothread 同时提供了 两种相互独立的通信路径:

一是协程内部的 m_indata / m_outdata 数据通道,二是基于外部 EventBus 的事件通信机制。

m_indata / m_outdata 属于 协程本体的最小通信接口,用于描述单个协程在一次调度周期内的输入与输出状态,其生命周期与协程实例严格绑定。

EventBus 则是 完全独立于协程实现之外的事件分发系统,用于在不同协程或外部模块之间建立松耦合的消息传递关系。两者在语义、作用域与生命周期上彼此独立,可单独使用。

外部EventBus通讯

订阅发布---1ms发布一条消息

c 复制代码
#include "pico/stdlib.h"
#include "./lib/AtShell.h"
#include "./user/user_atshell.h"
#include "AlarmProtothread.h"
#include "./common/EventBus.h"

using namespace Common;

class LedPt0 : public AlarmProtothread {
    void Init() {
        AT_println("LedPt0 init");
    }
    bool Run() {
        static uint32_t cnt = 0;
        WHILE(1){
        			//延时1000us
                PT_DELAY(1000);
                cnt++;
                //发布消息
                EventPublish(EventType::PUSH_DATA, &cnt);
            }
        PT_END();
    }
};

class LedPt1 : public AlarmProtothread {
    void Init() {
        AT_println("LedPt1 init");
        //订阅
        EventSubscribe(EventType::PUSH_DATA, this, &LedPt1::OnRecvEvent);
    }

    void OnRecvEvent(EventType eventType, void* data){
        if(eventType==EventType::PUSH_DATA){
            uint32_t cnt = *(uint32_t*)data;
            //实际应是写队列,在主循环中处理
            AT_println("LedPt0 OnRecvEvent cnt=%d,%d",cnt, to_ms_since_boot(get_absolute_time()));
            return;
        }
    }

    bool Run() {
        PtOsDelay(20000);
        return true;
    }

};

LedPt0 ledPt0;
LedPt1 ledPt1;


static int ptTest(int argc, char** argv) {
    //下次进定时器中断的时间(us)
    AT_println("M_next_tick=%u", AlarmProtothread::M_next_tick);
    return 0;
}


/* ================= main ================= */
int main() {
    stdio_init_all();
    AT_SHELL_EXPORT(test02, "ptTest", ptTest);
    user_atShell_init();
    AlarmProtothread::PtInit();
    AlarmProtothread::AllStart();
    /* 主循环:统一发送 Shell 输出 */
    while (true) {
        user_atShell_loop();
        tight_loop_contents();
    }
}

m_indata/m_outdata 通讯

ledPt0 每隔1s发送一条消息给 ledPt1

c 复制代码
#include "pico/stdlib.h"
#include "./lib/AtShell.h"
#include "./user/user_atshell.h"
#include "AlarmProtothread.h"

class LedPt0 : public AlarmProtothread {
    void Init() {
        AT_println("LedPt0 init");
    }
    bool Run() {
        static uint32_t cnt = 0;
        WHILE(1){
                PT_DELAY(1000000);
                cnt++;
                //1s 给LedPt1发一条消息
                //协程索引的顺序就是协程变量声明的顺序
                AlarmProtothread::PushIndata(AlarmProtothread::M_pts[1], cnt);
            }
        PT_END();
    }
};

class LedPt1 : public AlarmProtothread {
    void Init() {
        AT_println("LedPt1 init");
    }
    bool Run() {
        WHILE(1) {
                //等待LedPt0发消息,m_indata为0,则直接返回
                PT_WAIT_UNTIL(GetInData());
                //PopInData()会清零m_indata
                AT_println("ledPt1 get data %d\n",PopInData());
                PT_DELAY(100);
            }
        PT_END();
    }


};

LedPt0 ledPt0;
LedPt1 ledPt1;


static int ptTest(int argc, char** argv) {
    //下次进定时器中断的时间(us)
    AT_println("M_next_tick=%u", AlarmProtothread::M_next_tick);
    return 0;
}


/* ================= main ================= */
int main() {
    stdio_init_all();
    AT_SHELL_EXPORT(test02, "ptTest", ptTest);
    user_atShell_init();
    AlarmProtothread::PtInit();
    AlarmProtothread::AllStart();
    /* 主循环:统一发送 Shell 输出 */
    while (true) {
        user_atShell_loop();
        tight_loop_contents();
    }
}

目录结构

bash 复制代码
PS D:\workspace\gitee\2\ming_picoman> tree /F
卷 新加卷 的文件夹 PATH 列表
卷序列号为 1E8A-2CFF
D:.
│  .gitignore
│  CMakeLists.txt
│  copy_uf2.bat
│  diagram.json
│  pico_sdk_import.cmake
│  README.md
│  wokwi.toml
│
└─src
    │  AlarmProtothread.cpp
    │  AlarmProtothread.h
    │  main.cpp
    │  mi_hw_alarm.cpp
    │  mi_hw_alarm.h
    │
    ├─common
    │      LogUtils.cpp
    │      LogUtils.h
    │      EventBus.h
    |      EventBus.cpp
    │
    ├─lib
    │      AtShell.cpp
    │      AtShell.h
    │
    └─user
            user_atshell.cpp
            user_atshell.h

源文件

main.cpp

c 复制代码
#include "pico/stdlib.h"
#include "./lib/AtShell.h"
#include "./user/user_atshell.h"
#include "AlarmProtothread.h"
#include "mi_hw_alarm.h"

static uint32_t ledp0cnt=0;
static uint32_t dif_led0_us=0;
static uint32_t ledp1cnt=0;

const uint LED_PIN = 21;

/**
 * LedPt0 20us执行一次
 * LedPt1 2ms执行一次
 */
class LedPt0 : public AlarmProtothread {
    void Init() {
        AT_println("LedPt0 init");
        ledp0cnt=0;
    }
    bool Run() {
        static uint32_t last_us=mi_hw_alarm_get_tick_us();
        uint32_t now = mi_hw_alarm_get_tick_us();
       // PtOsDelayMs(1);
        //每隔20us执行一次
        PtOsDelay(20);
        ledp0cnt++;
        // 翻转 LED 电平
        gpio_xor_mask(1u << LED_PIN);
        dif_led0_us=now-last_us;
        last_us=now;
        return true;
    }
};

class LedPt1 : public AlarmProtothread {
    void Init() {
        AT_println("LedPt1 init");
        ledp1cnt=0;
    }
    bool Run() {
        //每隔2ms执行一次
        PtOsDelay(2000);
        ledp1cnt=ledp1cnt+1;
        return true;
    }
};

LedPt0 ledPt0;
LedPt1 ledPt1;


static int ptTest(int argc, char** argv) {
    AT_println("ptTest");
    //协程数量
    AT_println("M_nspt=%u",AlarmProtothread::M_nspt);
    //定时器us计时器
    AT_println("tick_us=%u",mi_hw_alarm_get_tick_us());
    //协程任务执行次数
    AT_println("ledp0cnt=%u",ledp0cnt);
    AT_println("dif_led0_us=%u",dif_led0_us);

    AT_println("ledp1cnt=%u",ledp1cnt);
    //进中断次数
    AT_println("M_int_count=%u",AlarmProtothread::M_int_count);
    //下次进定时器中断的时间(us)
    AT_println("M_next_tick=%u", AlarmProtothread::M_next_tick);
    AT_println("M_run_count=%u", AlarmProtothread::M_run_count);
    AT_println("M_unrun_count=%u", AlarmProtothread::M_unrun_count);
    return 0;
}


/* ================= main ================= */
int main() {
    stdio_init_all();
    gpio_init(LED_PIN);
    gpio_set_dir(LED_PIN, GPIO_OUT);
    gpio_put(LED_PIN, 0);   // 初始为低电平
    AT_SHELL_EXPORT(test02, "ptTest", ptTest);
    user_atShell_init();
    AlarmProtothread::PtInit();
    AlarmProtothread::AllStart();
    /* 主循环:统一发送 Shell 输出 */
    while (true) {
        user_atShell_loop();
        tight_loop_contents();
    }
}

AlarmProtothread.h

c 复制代码
#ifndef __AlarmProtothread_H__
#define __AlarmProtothread_H__
#include "stdint.h"
#define  PT_MAX_THREAD_NUM      5
#define  PT_THREAD_TICK_MS      10
/**
class LEDFlasher : public AlarmProtothread
{
    void Init(){
        AT_println("dd");
    }
    bool Run() {
        WHILE(1){
            AT_println("dd");
            PT_DELAY_MS(1000);
        }
        PT_END();
    }

    bool _Run() {
        AT_println("d33d");
        PtOsDelayMs(1000);
        return true;
    }
};

 void let_test(AlarmProtothread * pt) {
    LED_TOGGLE();
    PT_OS_DELAY_MS(10);
}

AlarmProtothread::Create(let_test);
LEDFlasher ledFlasher;
ledFlasher.Start();
**/


typedef struct {
    uint32_t	 code;
    uint32_t	 ms;
    void *       args;
} AlarmProtothreadNotifyEvent;


class AlarmProtothread;
typedef  void (*PtRunFun)(AlarmProtothread * pt);
class AlarmProtothread
{
public:
    static AlarmProtothread* M_pts[PT_MAX_THREAD_NUM];
    //for start
    static int M_nspt;
    static int M_npt;
    //for cycle
    static uint32_t M_now_tick;
    static uint32_t M_next_tick;
    //中断次数
    static uint32_t M_int_count;
    static uint32_t M_run_count;
    static uint32_t M_unrun_count;
    AlarmProtothread();
    virtual ~AlarmProtothread() { }
    virtual const char* Name(void) 	{ return("");}
    void Restart() { _ptLine = 0; }
    void Stop() { _ptLine = LineNumberInvalid; }
    bool IsRunning() { return _ptLine != LineNumberInvalid; }
    void PtOsDelay(uint32_t tick);
    void PtOsDelayMs(uint32_t ms);
    void PtOsDelayResume();
    virtual void Init();
    virtual bool Run();
    virtual bool IsContinueRun();
    virtual unsigned int Start();
    virtual void   Notify(AlarmProtothread * target,AlarmProtothreadNotifyEvent evt);
    virtual void   OnRecvNotify(AlarmProtothread * source,AlarmProtothreadNotifyEvent evt);
    static  void   OnRecvNotify(AlarmProtothread * source, AlarmProtothread * target,AlarmProtothreadNotifyEvent evt);
    static void  AllStart();
    static void  PtInit();
    static AlarmProtothread* Create(PtRunFun run);
protected:
    PtRunFun m_run;
    unsigned int  m_id;
    //下次运行时刻
    uint32_t m_delay;
    typedef unsigned short LineNumber;
    static const LineNumber LineNumberInvalid = (LineNumber)(-1);
    LineNumber _ptLine;
public:
    uint32_t  m_state;
    uint32_t  m_indata;
    uint32_t  m_outdata;
    uint32_t GetDelay();
    uint32_t GetInData();
    void SetOutData(uint32_t outdata);
    uint32_t PopInData();
    bool  PushOutData(uint32_t outdata);
    static void SetInData(AlarmProtothread * pt,uint32_t indata);
    static bool PushIndata(AlarmProtothread * pt,uint32_t indata);
    static uint32_t GetOutData(AlarmProtothread * pt);
    static uint32_t  PopOutData(AlarmProtothread * pt);
};

#define PT_BEGIN() bool ptYielded = true; (void) ptYielded; switch (_ptLine) { case 0:
#define PT_END() default: ; } Stop(); return false;
#define PT_WAIT_UNTIL(condition) \
    do { _ptLine = __LINE__; case __LINE__: \
    if (!(condition)) return true; } while (0)
#define PT_WAIT_WHILE(condition) PT_WAIT_UNTIL(!(condition))
#define PT_WAIT_THREAD(child) PT_WAIT_WHILE((child).Run())
#define PT_SPAWN(child) \
    do { (child).Restart(); PT_WAIT_THREAD(child); } while (0)

#define PT_RESTART() do { Restart(); return true; } while (0)

#define PT_EXIT() do { Stop(); return false; } while (0)

#define PT_YIELD() \
    do { ptYielded = false; _ptLine = __LINE__; case __LINE__: \
    if (!ptYielded) return true; } while (0)

#define PT_YIELD_UNTIL(condition) \
    do { ptYielded = false; _ptLine = __LINE__; case __LINE__: \
    if (!ptYielded || !(condition)) return true; } while (0)

#define PT_DELAY(v)				\
  do {						\
    PtOsDelay(v);			\
    PT_WAIT_UNTIL(IsContinueRun());		\
  } while(0)

#define PT_DELAY_MS(v)				\
  do {						\
    PtOsDelayMs(v);       \
    PT_WAIT_UNTIL(IsContinueRun());	\
  } while(0)

#define WHILE(a)   PT_BEGIN(); \
  while(1)

#define PT_OS_DELAY(tick)        pt->PtOsDelay(tick)
#define PT_OS_DELAY_MS(ms)       pt->PtOsDelayMs(ms)
//free time slice
#define PT_FREE_TIME_SLICE(tsMs)   PT_THREAD_TICK_MS-(tsMs-AlarmProtothread::M_ms_tick)
#endif

AlarmProtothread.cpp

c 复制代码
#include "AlarmProtothread.h"
#include "mi_hw_alarm.h"

int  AlarmProtothread::M_nspt = 0;
int  AlarmProtothread::M_npt = 0;
//中断执行次数
uint32_t  AlarmProtothread::M_int_count=0;
uint32_t  AlarmProtothread::M_now_tick = 0;
uint32_t  AlarmProtothread::M_next_tick =0;
uint32_t AlarmProtothread::M_run_count=0;
uint32_t AlarmProtothread::M_unrun_count=0;

AlarmProtothread * AlarmProtothread::M_pts[PT_MAX_THREAD_NUM];

static mi_hw_alarm_t g_alarm;

void alarm_cb(void *arg)
{
    uint32_t now = mi_hw_alarm_get_tick_us();
    uint32_t minDelay = UINT32_MAX;
    AlarmProtothread::M_now_tick = now;
    for (int i = 0; i < AlarmProtothread::M_npt; i++) {
        AlarmProtothread *pt = AlarmProtothread::M_pts[i];
        /* 到期就运行
         * Run()重新设置自己的m_delay
         */
        AlarmProtothread::M_run_count=0;
        if ((int32_t)(now - pt->GetDelay()) >= 0) {
            pt->Run();
        }
        /* 使用 Run() 之后的 delay 参与下一次调度 */
        uint32_t d = pt->GetDelay();
        if (d < minDelay) {
            minDelay = d;
        }
    }
    constexpr uint32_t MIN_IRQ_GAP_US=1;// 安全最小间隔
    uint32_t next;
    if (minDelay == UINT32_MAX) {
        /* 没有任何线程在等待 */
        next = now + MIN_IRQ_GAP_US;
    }
    else if (minDelay <= now + MIN_IRQ_GAP_US) {
        /* 防止 schedule 到 now 或极近未来 */
        next = now + MIN_IRQ_GAP_US;
    }
    else {
        next = minDelay;
    }
    AlarmProtothread::M_int_count++;
    AlarmProtothread::M_next_tick=next;
    mi_hw_alarm_schedule_at(next);
}

AlarmProtothread::AlarmProtothread(): _ptLine(0){
    m_delay = 0;
    m_indata=0;
    m_outdata=0;
    m_state=0;
    m_run=0;
    M_pts[AlarmProtothread::M_nspt++]= this;
}

void AlarmProtothread::PtOsDelay(uint32_t tick) {
    m_delay=mi_hw_alarm_get_tick_us()+ tick;
}

void AlarmProtothread::PtOsDelayMs(uint32_t ms) {
    m_delay = mi_hw_alarm_get_tick_us() + ms * 1000;
}

bool AlarmProtothread::IsContinueRun(){
   if( (int32_t)(AlarmProtothread::M_now_tick  - m_delay) >= 0) {
       return true;
   }
   return false;
}
void AlarmProtothread::PtOsDelayResume() {
    m_delay=0;
}
void AlarmProtothread::PtInit(){
    mi_hw_alarm_init(&g_alarm);
}
void AlarmProtothread::Init() {

}

uint32_t AlarmProtothread::GetDelay(){
    return m_delay;
}

uint32_t AlarmProtothread::GetInData(){
    return m_indata;
}
void AlarmProtothread::SetInData(AlarmProtothread * pt,uint32_t indata){
    pt->m_indata=indata;
}

uint32_t AlarmProtothread::PopInData(){
    uint32_t v = m_indata;
    m_indata = 0;
    return v;
}

bool AlarmProtothread::PushIndata(AlarmProtothread * pt,uint32_t indata){
    if (pt->m_indata == 0) {
        pt->m_indata = indata;
        return true;
    }
    return false;
}

uint32_t AlarmProtothread::GetOutData(AlarmProtothread * pt){
    return pt->m_outdata;
}

void AlarmProtothread::SetOutData(uint32_t outdata){
    m_outdata=outdata;
}

uint32_t  AlarmProtothread::PopOutData(AlarmProtothread * pt){
    uint32_t v=pt->m_outdata;
    pt->m_outdata=0;
    return v;
}

bool  AlarmProtothread::PushOutData(uint32_t outdata){
    if(m_outdata==0) {
        m_outdata=outdata;
        return true;
    }
    return false;
}

unsigned int AlarmProtothread::Start() {
    this->Init();
    m_id=AlarmProtothread::M_npt++;
    M_pts[m_id]= this;
    return m_id;
}

bool AlarmProtothread::Run() {
    if(m_run!=0){
        m_run(this);
    }
    return true;
}

AlarmProtothread* AlarmProtothread::Create(PtRunFun run) {
    AlarmProtothread* pt= new AlarmProtothread();
    pt->m_run=run;
    return pt;
}



void AlarmProtothread::AllStart() {
    for (int i=0;i<M_nspt;i++){
        M_pts[i]->Start();
    }
    /* 第一次触发 */
    mi_hw_alarm_attach(&g_alarm, alarm_cb, 0);
    mi_hw_alarm_enable(&g_alarm);
    mi_hw_alarm_schedule_after_us(1000);
}




void  AlarmProtothread::Notify(AlarmProtothread * target,AlarmProtothreadNotifyEvent evt){
    AlarmProtothread::OnRecvNotify(this,target,evt);
    if(target== nullptr){
        for(int i=0;i<AlarmProtothread::M_npt;i++){
            AlarmProtothread::M_pts[i]->OnRecvNotify(target,evt);
        }
        return;
    } else{
        target->OnRecvNotify(target,evt);
    }
}


void  AlarmProtothread::OnRecvNotify(AlarmProtothread * source,AlarmProtothreadNotifyEvent evt){
    // nothing
}

void  AlarmProtothread::OnRecvNotify(AlarmProtothread * source, AlarmProtothread * target,AlarmProtothreadNotifyEvent evt){
    // nothing
}

mi_hw_alarm.h

c 复制代码
#pragma once
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef void (*mi_hw_alarm_cb_t)(void *user);

typedef struct {
    mi_hw_alarm_cb_t cb;
    void *user_data;
} mi_hw_alarm_t;

void mi_hw_alarm_init(mi_hw_alarm_t *alarm);
void mi_hw_alarm_attach(mi_hw_alarm_t *alarm,
                        mi_hw_alarm_cb_t cb,
                        void *user_data);
void mi_hw_alarm_enable(mi_hw_alarm_t *alarm);
void mi_hw_alarm_disable(void);

void mi_hw_alarm_schedule_at(uint32_t abs_us);
void mi_hw_alarm_schedule_after_us(uint32_t delta_us);

uint32_t mi_hw_alarm_get_tick_us(void);

#ifdef __cplusplus
}
#endif

mi_hw_alarm.cpp

c 复制代码
#include "mi_hw_alarm.h"
#include "hardware/timer.h"
#include "hardware/irq.h"
#define ALARM_NUM   0
#define ALARM_MASK  (1u << ALARM_NUM)

/* 只维护一个 alarm 实例 */
static mi_hw_alarm_t *s_alarm = NULL;

/* ============================================================
 * IRQ Handler
 * ============================================================ */
static void __isr timer_irq_handler(void)
{
    /* 清 pending */
    hw_clear_bits(&timer_hw->intr, ALARM_MASK);

    /* 调用用户回调 */
    if (s_alarm && s_alarm->cb) {
        s_alarm->cb(s_alarm->user_data);
    }
}

/* ============================================================
 * 初始化
 * ============================================================ */
void mi_hw_alarm_init(mi_hw_alarm_t *alarm)
{
    if (!alarm) return;

    s_alarm = alarm;
    alarm->cb        = NULL;
    alarm->user_data = NULL;
}

/* ============================================================
 * 绑定回调
 * ============================================================ */
void mi_hw_alarm_attach(mi_hw_alarm_t *alarm,
                        mi_hw_alarm_cb_t cb,
                        void *user_data)
{
    if (!alarm) return;

    alarm->cb        = cb;
    alarm->user_data = user_data;
}

/* ============================================================
 * 启用 alarm
 * ============================================================ */
void mi_hw_alarm_enable(mi_hw_alarm_t *alarm)
{
    (void)alarm;

    /* 先关 IRQ,防止配置过程触发 */
    irq_set_enabled(TIMER_IRQ_0, false);

    /* 清 pending,避免"立刻进中断" */
    hw_clear_bits(&timer_hw->intr, ALARM_MASK);

    /* 独占 IRQ */
    irq_set_exclusive_handler(TIMER_IRQ_0, timer_irq_handler);

    /* 只使能指定 alarm */
    timer_hw->inte |= ALARM_MASK;

    irq_set_enabled(TIMER_IRQ_0, true);
}

/* ============================================================
 * 关闭 alarm
 * ============================================================ */
void mi_hw_alarm_disable(void)
{
    irq_set_enabled(TIMER_IRQ_0, false);

    timer_hw->inte &= ~ALARM_MASK;

    /* 可选:清一次 pending,确保干净 */
    hw_clear_bits(&timer_hw->intr, ALARM_MASK);
}


static inline bool time_after_eq_u32(uint32_t a, uint32_t b)
{
    return (int32_t)(a - b) >= 0;
}

/* ============================================================
 * 设置绝对时间(μs since boot)
 * RP2040 alarm 只有 32bit
 * ============================================================ */
void mi_hw_alarm_schedule_at(uint32_t abs_us)
{
    uint32_t now = timer_hw->timerawl;
    if (time_after_eq_u32(now + 1, abs_us)) {
        abs_us = now + 1;
    }
    timer_hw->alarm[ALARM_NUM] = abs_us;
}

/* ============================================================
 * 相对延时
 * ============================================================ */
void mi_hw_alarm_schedule_after_us(uint32_t delta_us)
{
    uint32_t now = timer_hw->timerawl;
    /* 防止 0 或极小延迟造成 IRQ 风暴 */
    if (delta_us < 10)
        delta_us = 10;

    timer_hw->alarm[ALARM_NUM] = now + delta_us;
}

/* ============================================================
 * 获取当前 tick(μs)
 * ============================================================ */
uint32_t mi_hw_alarm_get_tick_us(void)
{
    return timer_hw->timerawl;
}

user_atshell.h

c 复制代码
//
// Created by PC on 2025/12/25.
//

#ifndef PIPO_PROJECT_USER_ATSHELL_H
#define PIPO_PROJECT_USER_ATSHELL_H


void user_atShell_init();
void user_atShell_loop();

#endif //PIPO_PROJECT_USER_ATSHELL_H

user_atshell.cpp

c 复制代码
#include "pico/stdlib.h"
#include "hardware/uart.h"
#include "hardware/irq.h"
#include "../lib/AtShell.h"
#include <cstdint>

/* ================= UART 配置 ================= */
#define UART_ID        uart0
#define UART_BAUDRATE  115200
#define UART_TX_PIN    0
#define UART_RX_PIN    1

/* ================= TX 缓冲区类 ================= */
class UartTxBuffer {
public:
    explicit UartTxBuffer(size_t size)
            : size_(size), head_(0), tail_(0)
    {
        buf_ = new uint8_t[size_];
    }

    ~UartTxBuffer() {
        delete[] buf_;
    }

    // 写入缓冲区(返回写入字节数)
    size_t write(const uint8_t* data, size_t len) {
        size_t written = 0;
        for (size_t i = 0; i < len; i++) {
            if (isFull()) break;
            buf_[head_] = data[i];
            head_ = (head_ + 1) % size_;
            written++;
        }
        return written;
    }

    // 从缓冲区读取一个字节
    bool read(uint8_t& data) {
        if (isEmpty()) return false;
        data = buf_[tail_];
        tail_ = (tail_ + 1) % size_;
        return true;
    }

    bool isEmpty() const { return head_ == tail_; }
    bool isFull()  const { return (head_ + 1) % size_ == tail_; }

private:
    uint8_t* buf_;
    size_t size_;
    volatile size_t head_;
    volatile size_t tail_;
};

/* ================= 全局对象 ================= */
UartTxBuffer shellTxBuffer(512);

/* ================= BSP 写接口(这个方法是正常是在中断里用的) ================= */
int Bsp_shell_write(uint8_t* buf, uint32_t len, uint32_t timeout) {
    (void)timeout;
    return shellTxBuffer.write(buf, len);
}



/* ================= UART RX 中断 ================= */
void on_uart_rx() {
    while (uart_is_readable(UART_ID)) {
        uint8_t ch = uart_getc(UART_ID);
        at_import(&ch, 1, 0);
    }
}

void user_atShell_init(){
    /* UART 初始化 */
    uart_init(UART_ID, UART_BAUDRATE);
    gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);
    gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);
    uart_set_hw_flow(UART_ID, false, false);
    uart_set_format(UART_ID, 8, 1, UART_PARITY_NONE);
    uart_set_fifo_enabled(UART_ID, true);

    /* UART RX 中断 */
    irq_set_exclusive_handler(UART0_IRQ, on_uart_rx);
    irq_set_enabled(UART0_IRQ, true);
    uart_set_irq_enables(UART_ID, true, false);
    /* AtShell 初始化 */
    at_init(Bsp_shell_write);
    at_show_version();
}

/* ================= main ================= */
void user_atShell_loop() {
    uint8_t ch;
    while (!shellTxBuffer.isEmpty() && uart_is_writable(UART_ID)) {
        if (shellTxBuffer.read(ch))
            uart_putc_raw(UART_ID, ch);
    }
    tight_loop_contents();
}

AtShell.h

c 复制代码
/*
 CON_AT_MSH=0: AT模式: 配合 Xmodem1K 更新固件
=>: AT+fun(a,b,c)\r\n
CON_AT_MSH=1: MSH模式: 调试
=>: fun a b \n
**/

#ifndef _AT_SHELL_H
#define _AT_SHELL_H

#include "stdint.h"
#include "string.h"
#include "stdbool.h"

//模式  0:AT    1:MSH
#define  CON_AT_MSH 1
//机器Hex通讯 AT+c(55,01 02)
#define  CON_AT_USE_CALLBACK 1
//数据监控
#define  CON_AT_USE_CycleMonitorData 1
//监控总数量
#define CON_CYCLE_MONITOR_DATA_PACK_NUM  10

//方法数
#define  CON_AT_METHOD_NUM     10//  10
#define  FINSH_CMD_SIZE       20  //20             //最长命令尺寸
#define  RT_FINSH_ARG_MAX      3// 6        //参数个数
#define  FINSH_HISTORY_LINES    3       //历史命令条数
#define  CON_AT_R_SUCCESS   0    // 成功
#define  CON_AT_R_ERR_ARG   1    // 参数错误
#define  CON_AT_R_ERR_NO_CMD   2  //无此命令
#define  CON_AT_R_ERR_EXEC_FAIL 3  //执行失败
#define  CON_AT_WRITE_TIMEOUT   100
//一种异步发送的实现
#define  CON_AT_USE_EXPORT    0
#define  CON_AT_OUT_BUF_SIZE     200
#define CON_METHOD_NAME_SIZE 8
#define CON_HELP_INFO_SIZE  20


typedef int (*ATServerFun)(int argc, char **argv);
typedef int (*ATWriteFun)(uint8_t *buf, uint32_t len,uint32_t timeout);

#if CON_AT_USE_CALLBACK == 1
typedef void (*ATCallBackFun)(uint32_t code, uint8_t *buf, uint32_t len);
#endif

typedef struct {
    char methodName[CON_METHOD_NAME_SIZE];
    char helpInfo[CON_HELP_INFO_SIZE];
    ATServerFun atFun;
    void *userData;
} AT_CMD_ENTRY_TypeDef;


typedef struct {
    char     tag[CON_CYCLE_MONITOR_DATA_PACK_NUM][10];
    uint8_t  buffer[CON_CYCLE_MONITOR_DATA_PACK_NUM][100];
    uint8_t  bufferLen[CON_CYCLE_MONITOR_DATA_PACK_NUM];
    uint8_t  curInx;
} AT_CycleMonitorDataTypeDef;
#ifdef __cplusplus


class AtShell {
private:
    AT_CMD_ENTRY_TypeDef *m_cmdList;
    uint32_t m_cmdSize;
    int m_cmdNum;
    char m_buf[FINSH_CMD_SIZE];
    uint32_t m_bufLen;
    uint32_t m_importMs;
    uint32_t m_lock;
    int m_argc;
    char m_method[CON_METHOD_NAME_SIZE];
    char *m_argv[RT_FINSH_ARG_MAX];
    ATWriteFun m_initWriteFun;
    ATWriteFun m_writeFun;
    virtual bool Parse(char *str);

public:
    AtShell();
    virtual ~AtShell();
    AT_CMD_ENTRY_TypeDef *ctx;
#if CON_AT_USE_CALLBACK == 1
    ATCallBackFun m_atCallBackFun;
#endif
#if CON_AT_USE_EXPORT == 1
    char m_out_buf[CON_AT_OUT_BUF_SIZE];
    uint32_t m_out_bufLen;
    virtual void Export();
#endif

    virtual void Init(ATWriteFun writeFun);
    virtual void SetWriteFun(ATWriteFun writeFun);
    virtual void ResetWriteFun();
    virtual char *GetBuf() { return m_buf; };
    virtual int GetCmdNum() { return m_cmdNum; };
    virtual bool Regist(AT_CMD_ENTRY_TypeDef cmd);
    virtual bool Regist(AT_CMD_ENTRY_TypeDef *cmdList, int cmdLen);
    virtual bool Exec(char *str);
    virtual int Import(uint8_t *buf, uint32_t len, uint32_t ms = 0);
    virtual int ImportForAt(uint8_t *buf, uint32_t len, uint32_t ms = 0);
    virtual int  AsyncPrintf(const char *format, ...);
    virtual int  Printf(const char *format, ...);
    virtual int Output(long nLevel, const char *pszFileName, int nLineNo, const char *pszFmt, ...);
    virtual int PrintfBs(uint8_t *buf, uint32_t len);
    virtual int Write(uint8_t *buf, uint32_t len,uint32_t timeout);
    virtual int Write(uint8_t data);
    virtual void AtCall(uint32_t code, uint8_t *buf, uint32_t len);
    virtual int Reply(uint8_t errCode);
    virtual void ShowVersion();
#if CON_AT_USE_CycleMonitorData==1
    AT_CycleMonitorDataTypeDef m_monitorData;
    virtual int MonitorDataPush(const char *tag, uint8_t * buffer, int len);
    virtual int MonitorDataViewInfo();
#endif

#if CON_AT_MSH == 1
    uint16_t m_currentHistory;
    uint16_t m_historyCount;
    char m_cmdHistory[FINSH_HISTORY_LINES][FINSH_CMD_SIZE];
    int m_stat;
    char m_line[FINSH_CMD_SIZE];
    uint8_t m_linePosition;
    uint8_t m_lineCurpos;

    virtual int ImportForMsh(uint8_t *buf, uint32_t len, uint32_t ms = 0);
    virtual void MshAddChar(char ch);
    virtual void ShellPushHistory();
    virtual void ShellAutoComplete(char *prefix);
    virtual void MshAutoComplete(char *prefix);
    virtual bool ShellHandleHistory();
#endif
};

extern AtShell g_atShell;

enum AT_LOG_LEVEL {
    AT_LOG_LEVEL_TRACE,
    AT_LOG_LEVEL_DEBUG,
    AT_LOG_LEVEL_INFO,
    AT_LOG_LEVEL_WARNING,
    AT_LOG_LEVEL_ERROR
};

extern AtShell g_atShell;

#define AT_m_buf  g_atShell.GetBuf()

#if CON_AT_MSH == 1
#define AT_FUN(fun)    #fun
#else
#define AT_FUN(fun)    "AT+"#fun
#endif
#define CONCAT(a, b) a ## b
#define AT_FILE_NAME(x)   strrchr(x,'\\')?strrchr(x,'\\')+1:x
#define AT_info(...)      g_atShell.Output(AT_LOG_LEVEL_INFO,__func__, __LINE__,__VA_ARGS__)
#define AT_debug(...)     g_atShell.Output(AT_LOG_LEVEL_DEBUG,AT_FILE_NAME(__FILE__), __LINE__,__VA_ARGS__)
#define AT_debug1(...)    g_atShell.Output(AT_LOG_LEVEL_DEBUG,__func__,__LINE__,__VA_ARGS__)
#define AT_error(...)     g_atShell.Output(AT_LOG_LEVEL_ERROR,__PRETTY_FUNCTION__,__LINE__,__VA_ARGS__)
#define AT_printf(format, ...)  g_atShell.Printf(format,##__VA_ARGS__)
#define AT_println(format, ...)  g_atShell.Printf(format,##__VA_ARGS__);AT_printf("\r\n")
#define AT_aprintf(format, ...)    g_atShell.AsyncPrintf(format,##__VA_ARGS__)
#define AT_aprintln(format, ...)  g_atShell.AsyncPrintf(format,##__VA_ARGS__);AT_aprintf("\r\n")

#define AT_printfBs(buf, len)  g_atShell.PrintfBs(buf,len)
#define rt_kprintf                  AT_printf
#define ATX_info(w, ...)             g_atShell.SetWriteFun(w);AT_info(__VA_ARGS__);g_atShell.ResetWriteFun();
#define ATX_debug(w, ...)            g_atShell.SetWriteFun(w);AT_debug(__VA_ARGS__);g_atShell.ResetWriteFun();
#define ATX_error(w, ...)            g_atShell.SetWriteFun(w);AT_error(__VA_ARGS__);g_atShell.ResetWriteFun();
#define ATX_printf(w, ...)           g_atShell.SetWriteFun(w);AT_printf(__VA_ARGS__);g_atShell.ResetWriteFun();
#define ATX_printfBs(w, buf, len)     g_atShell.SetWriteFun(w);g_atShell.PrintfBs(buf,len);g_atShell.ResetWriteFun();
#endif

#if CON_AT_USE_CALLBACK == 1
#define AT_SET_CALL_BACK(fun)          g_atShell.m_atCallBackFun=fun
#endif


#define AT_SHELL_EXPORT(cmdName, desc, fun, ...)  AT_CMD_ENTRY_TypeDef fun##entrycmd={AT_FUN(cmdName),#desc,fun,__VA_ARGS__}; at_register(fun##entrycmd)
#define AT_EXEC(cmd)    g_atShell.Exec(cmd)


#ifdef __cplusplus
extern "C" {
#endif
void at_init(ATWriteFun writeFun);
int  at_import(uint8_t *buf, uint32_t len, uint32_t ms);
#if CON_AT_USE_EXPORT == 1
void  at_export();
#endif
#if CON_AT_USE_CycleMonitorData == 1
void  at_monitor_init();
void  at_monitor_push(const char *tag, uint8_t * buffer, int len);
void  at_monitor_viewInfo();
#endif
bool at_try_import(uint8_t *buf, uint32_t len, uint32_t ms);
bool at_register(AT_CMD_ENTRY_TypeDef cmd);
bool at_register_many(AT_CMD_ENTRY_TypeDef *cmdList, int cmdLen);
int  at_write(uint8_t *buf, uint32_t len,uint32_t timeout);
int  at_awrite(uint8_t *buf, uint32_t len);
int  at_printf(const char *format, ...);
int  at_aprintf(const char *format, ...);
int  at_reply(uint8_t errCode);
int  at_hexStringToByteArray(const char *hexStr, unsigned char *bs);
long at_str_to_int(char* str);
void at_show_version();
#ifdef __cplusplus
}
#endif

#endif





/*
*
#include <iostream>
#include "AtShell.h"
#include "stdio.h"
int shell_write(uint8_t* buf, uint32_t len,uint32_t timeout) {
	return  printf("%s", buf);
}
static int test01(int argc, char** argv) {
	AT_printf("argc %d:\r\n", argc);
	return 0;
}
int main() {
	at_init(shell_write);
	AT_SHELL_EXPORT(test01, "", test01);
	while (1) {
		char a = getchar();
		uint8_t bs[1] = { a };
		at_import(bs, 1, 0);
	}
	return 0;
}
**/

AtShell.cpp

c 复制代码
#include "AtShell.h"
#include "stdio.h"
#include "stdlib.h"
#include "stdarg.h"
#include "string.h"
#include <ctype.h>
#include <limits.h>

#define CON_AT_DELIMITER   "(,)"
#define CON_AT_DELIMITER_BANK   " "
#define FINSH_PROMPT        "$:"

static int __help(int argc, char **argv);


AtShell g_atShell;


#if CON_AT_MSH == 1

static int __clean(int argc, char **argv) {
    AT_printf("\033c");
    return 0;
}

#else
static int __at(int argc, char** argv) {
    at_reply(CON_AT_R_SUCCESS);
    return 0;
}
#endif


#if CON_AT_USE_CALLBACK == 1
static int __call(int argc, char **argv) {
    if (argc <= 2) {
        return 0;
    }
    char *hexStr = argv[2];
    uint32_t code = strtol(argv[1], NULL, 16);
    char *bs = g_atShell.GetBuf();
    int len = at_hexStringToByteArray(hexStr, (unsigned char *) bs);
    if (g_atShell.m_atCallBackFun != NULL) {
        g_atShell.m_atCallBackFun(code, (uint8_t *) bs, len);
    }
    return 0;
}

#endif

static AT_CMD_ENTRY_TypeDef s_ap_cmd_entry[CON_AT_METHOD_NUM] = {
        {AT_FUN(help), "list cmd", __help, 0},
#if CON_AT_USE_CALLBACK == 1
        {AT_FUN(c), "(55,01 02)", __call, 0},
#endif
#if CON_AT_MSH == 1
        {"clean", "clean screen", __clean, 0},
#else
        { "AT","",__at, 0 },
#endif
};

static int __help(int argc, char **argv) {
    int cmdInx = g_atShell.GetCmdNum();
    AT_printf("AtShell commands:\r\n");
    for (int i = 0; i < cmdInx; i++) {
        AT_printf("%2d.%-20s - %s \r\n", i, s_ap_cmd_entry[i].methodName, s_ap_cmd_entry[i].helpInfo);
    }
    return 0;
}


AtShell::AtShell() {
    m_cmdList = s_ap_cmd_entry;
    m_cmdSize = CON_AT_METHOD_NUM;
    m_cmdNum = 0;
    m_writeFun = NULL;
    m_bufLen = 0;
    m_importMs = 0;
#if CON_AT_USE_EXPORT == 1
    m_out_bufLen=0;
#endif
    for (size_t i = 0; i < m_cmdSize; i++) {
        if (m_cmdList[i].atFun == NULL) {
            m_cmdNum = i;
            break;
        }
    }
}


AtShell::~AtShell() {

}


bool AtShell::Regist(AT_CMD_ENTRY_TypeDef cmd) {
    if (m_cmdNum >= CON_AT_METHOD_NUM) {
        return false;
    }
    m_cmdList[m_cmdNum] = cmd;
    m_cmdNum++;
    return true;
}

bool AtShell::Regist(AT_CMD_ENTRY_TypeDef *cmdList, int cmdLen) {
    if (m_cmdNum + cmdLen > CON_AT_METHOD_NUM) {
        return false;
    }
    for (int i = 0; i < cmdLen; i++) {
        AtShell::Regist(cmdList[i]);
    }
    return true;
}

void AtShell::Init(ATWriteFun writeFun) {
    m_writeFun = writeFun;
    m_initWriteFun = writeFun;
}


void AtShell::SetWriteFun(ATWriteFun writeFun) {
    m_writeFun = writeFun;
}

void AtShell::ResetWriteFun() {
    m_writeFun = m_initWriteFun;
}

bool AtShell::Parse(char *str) {
    if (m_buf != str) {
        strcpy(m_buf, str);
    }
    m_argv[0] = m_buf;
    char *token;
    char delimiter[16] = CON_AT_DELIMITER;
    if (strstr((char *) str, "AT") != (char *) str) {
        sprintf(delimiter, CON_AT_DELIMITER_BANK);
    }
    token = strtok(m_buf, delimiter);
    int i = 0;
    while (token != NULL) {
        if (i == 0) {
            snprintf(m_method, sizeof(m_method), "%s", token);
            snprintf(m_argv[0], sizeof(m_method), "%s", token);
            m_argv[1] = &m_buf[strlen(m_argv[0]) + 1];
        } else if (i < RT_FINSH_ARG_MAX) {
            sprintf(m_argv[i], "%s", token);
            m_argv[i + 1] = m_argv[i] + strlen(m_argv[i]) + 1;
        }
        token = strtok(NULL, delimiter);
        i++;
        m_argc = i;
    }
    return true;
}


bool AtShell::Exec(char *str) {
    m_lock = 1;
    bool r = Parse(str);
    if (r == false) {
        m_lock = 0;
        return r;
    }
    bool cmd_ret = false;
    //methodName match
    for (int i = 0; i < m_cmdNum; i++) {
        if (strcmp(m_cmdList[i].methodName, m_method) == 0) {
            this->ctx = &m_cmdList[i];
            m_cmdList[i].atFun(m_argc, m_argv);
            cmd_ret = true;
            break;
        }
    }
    //inx match
    if (!cmd_ret) {
        int inx = strtol(m_method, NULL, 10);
        if ('0' <= m_method[0] && m_method[0] <= '9' && inx < m_cmdNum) {
            this->ctx = &m_cmdList[inx];
            m_cmdList[inx].atFun(m_argc, m_argv);
            cmd_ret = true;
        }
    }
    if (!cmd_ret) {
        char *tcmd;
        tcmd = str;
        while (*tcmd != ' ' && *tcmd != '\0') {
            tcmd++;
        }
        *tcmd = '\0';
        this->Printf("%s: command not found.\n", str);
    }
    m_bufLen = 0;
    m_lock = 0;
    return cmd_ret;
}

int AtShell::Import(uint8_t *buf, uint32_t len, uint32_t ms) {
#if CON_AT_MSH == 1
    return this->ImportForMsh(buf, len, ms);
#else
    return  this->ImportForAt(buf, len, ms);
#endif
}



int AtShell::ImportForAt(uint8_t *buf, uint32_t len, uint32_t ms) {
    if (m_lock == 1 || len == 0 || len > sizeof(m_buf)) {
        return 0;
    }
    if (buf == NULL || ms - m_importMs > 10 || m_bufLen + len >= sizeof(m_buf)) {
        m_bufLen = 0;
    }
    if (buf != NULL) {
        memcpy(m_buf + m_bufLen, buf, len);
    }
    m_bufLen = m_bufLen + len;
    m_importMs = ms;
    if (m_bufLen < 3) {
        return 0;
    }
    if (m_buf[m_bufLen - 2] == 0x0d && m_buf[m_bufLen - 1] == 0x0a) {
        if (strstr((char *) m_buf, "AT") == (char *) m_buf) {
            m_buf[m_bufLen - 1] = 0;
            m_buf[m_bufLen - 2] = 0;
        } else {
            m_buf[m_bufLen - 1] = 0;
            m_buf[m_bufLen - 2] = 0;
        }
        this->Exec(m_buf);
        return m_bufLen;
    }
    return 0;
}


int AtShell::Printf(const char *format, ...) {
    char log_buf[256];
    va_list args;
    va_start(args, format);
    vsprintf(log_buf, format, args);
    uint32_t len = strlen(log_buf);
    m_writeFun((uint8_t *) log_buf, len,CON_AT_WRITE_TIMEOUT);
    va_end(args);
    return len;
}


int AtShell::Output(long nLevel, const char *pszFileName, int nLineNo, const char *format, ...) {
    char log_buf[256];
    snprintf(log_buf, sizeof(log_buf), "[%s:%d]", pszFileName, nLineNo);
    uint32_t len = strlen(log_buf);
    m_writeFun((uint8_t *) log_buf, len,CON_AT_WRITE_TIMEOUT);
    va_list args;
    va_start(args, format);
    vsprintf(log_buf, format, args);
    len = strlen(log_buf);
    m_writeFun((uint8_t *) log_buf, len,CON_AT_WRITE_TIMEOUT);
    va_end(args);
    return len;
}

int AtShell::PrintfBs(uint8_t *buf, uint32_t len) {
    char log_buf[256];
    if (len == 0 || len > 80) {
        return 0;
    }
    for (size_t i = 0; i < len; i++) {
        sprintf(&log_buf[3 * i], "%02X ", buf[i]);
    }
    log_buf[3 * len - 1] = '\n';
    this->Write((uint8_t *) log_buf, 3 * len,CON_AT_WRITE_TIMEOUT);
    return 3 * len;
}


int AtShell::Write(uint8_t *buf, uint32_t len,uint32_t timeout) {
    return m_writeFun(buf, len,timeout);
}

int AtShell::Write(uint8_t data) {
    uint8_t temp[1] = {data};
    return m_writeFun(temp, 1,CON_AT_WRITE_TIMEOUT);
}


void AtShell::AtCall(uint32_t code, uint8_t *buf, uint32_t len) {
    char log_buf[256];
    if (len == 0 || len > 80) {
        return;
    }
    for (size_t i = 0; i < len; i++) {
        sprintf(&log_buf[3 * i], "%02X ", buf[i]);
    }
    log_buf[3 * len - 1] = 0;
    this->Printf("AT+c(%d,%s)\r\n", code, log_buf);
}


int AtShell::Reply(uint8_t errCode) {
    if (errCode) {
        this->Printf("NG:%d\r\n", errCode);
        return errCode;
    }
    this->Printf("OK\r\n");
    return errCode;
}


void AtShell::ShowVersion() {
    this->Printf(" \\ | /\r\n");
    this->Printf("- AT_SHELL - %s  msh:%d \r\n", __DATE__, CON_AT_MSH);
    this->Printf(" / | \\\r\n");
#if CON_AT_MSH == 1
    char s[1] = {""};
    ShellAutoComplete(s);
#endif
}


#if CON_AT_MSH == 1
#if 0
/*
void msh_split_test(){
    char* argv[RT_FINSH_ARG_MAX];
    char test_cmd[100] = { "funcname arg0 \"arg1 arg1\"" };
    msh_split(test_cmd, sizeof(test_cmd), argv);
}
*/
static int msh_split(char* cmd, uint32_t length, char* argv[RT_FINSH_ARG_MAX])
{
    char* ptr;
    uint32_t position;
    uint32_t argc;
    uint32_t i;
    /* strim the beginning of command */
    while (*cmd == ' ' || *cmd == '\t')
    {
        cmd++;
        length--;
    }
    if (length == 0)
        return 0;
    ptr = cmd;
    position = 0; argc = 0;
    while (position < length)
    {
        /* strip bank and tab */
        while ((*ptr == ' ' || *ptr == '\t') && position < length)
        {
            *ptr = '\0';
            ptr++; position++;
        }

        if (argc >= RT_FINSH_ARG_MAX)
        {
            rt_kprintf("Too many args ! We only Use:\n");
            for (i = 0; i < argc; i++)
            {
                rt_kprintf("%s ", argv[i]);
            }
            rt_kprintf("\n");
            break;
        }

        if (position >= length) break;

        /* handle string */
        if (*ptr == '"')
        {
            ptr++; position++;
            argv[argc] = ptr; argc++;

            /* skip this string */
            while (*ptr != '"' && position < length)
            {
                if (*ptr == '\\')
                {
                    if (*(ptr + 1) == '"')
                    {
                        ptr++; position++;
                    }
                }
                ptr++; position++;
            }
            if (position >= length) break;

            /* skip '"' */
            *ptr = '\0'; ptr++; position++;
        }
        else
        {
            argv[argc] = ptr;
            argc++;
            while ((*ptr != ' ' && *ptr != '\t') && position < length)
            {
                ptr++; position++;
            }
            if (position >= length) break;
        }
    }
    return argc;
}
#endif


static void *rt_memmove(void *dest, const void *src, uint32_t n) {
    char *tmp = (char *) dest, *s = (char *) src;

    if (s < tmp && tmp < s + n) {
        tmp += n;
        s += n;

        while (n--)
            *(--tmp) = *(--s);
    } else {
        while (n--)
            *tmp++ = *s++;
    }

    return dest;
}

static char *rt_strncpy(char *dst, const char *src, uint32_t n) {
    if (n != 0) {
        char *d = dst;
        const char *s = src;

        do {
            if ((*d++ = *s++) == 0) {
                /* NUL pad the remaining n-1 bytes */
                while (--n != 0)
                    *d++ = 0;
                break;
            }
        } while (--n != 0);
    }

    return (dst);
}

static int str_common(const char *str1, const char *str2) {
    const char *str = str1;
    while ((*str != 0) && (*str2 != 0) && (*str == *str2)) {
        str++;
        str2++;
    }
    return (str - str1);
}


int AtShell::ImportForMsh(uint8_t *buf, uint32_t len, uint32_t ms) {
    for (size_t i = 0; i < len; i++) {
        MshAddChar(buf[i]);
    }
    return len;
}


void AtShell::ShellAutoComplete(char *prefix) {
    this->Printf("\r\n");
    MshAutoComplete(prefix);
    this->Printf("%s%s", FINSH_PROMPT, prefix);
}


void AtShell::MshAutoComplete(char *prefix) {
    int length, min_length;
    const char *name_ptr, *cmd_name;
    int index;
    min_length = 0;
    name_ptr = 0;
    if (*prefix == '\0') {
        return;
    }
    for (index = 0; index < g_atShell.GetCmdNum(); index++) {
        /* skip finsh shell function */
        cmd_name = s_ap_cmd_entry[index].methodName;
        if (strncmp(prefix, cmd_name, strlen(prefix)) == 0) {
            if (min_length == 0) {
                /* set name_ptr */
                name_ptr = cmd_name;
                /* set initial length */
                min_length = strlen(name_ptr);
            }
            length = str_common(name_ptr, cmd_name);
            if (length < min_length)
                min_length = length;

            this->Printf("%s\r\n", cmd_name);
        }
    }


    /* auto complete string */
    if (name_ptr != NULL) {
        rt_strncpy(prefix, name_ptr, min_length);
    }
    return;
}

void AtShell::MshAddChar(char ch) {
    enum input_stat {
        WAIT_NORMAL = 0,
        WAIT_SPEC_KEY,
        WAIT_FUNC_KEY,
    };
    /*
     * handle control key
     * up key  : 0x1b 0x5b 0x41
     * down key: 0x1b 0x5b 0x42
     * right key:0x1b 0x5b 0x43
     * left key: 0x1b 0x5b 0x44
     */
    if (ch == 0x1b) {
        m_stat = WAIT_SPEC_KEY;
        return;
    } else if (m_stat == WAIT_SPEC_KEY) {
        if (ch == 0x5b) {
            m_stat = WAIT_FUNC_KEY;
            return;
        }
        m_stat = WAIT_NORMAL;
    } else if (m_stat == WAIT_FUNC_KEY) {
        m_stat = WAIT_NORMAL;
        if (ch == 0x41) /* up key */
        {
            /* prev history */
            if (m_currentHistory > 0)
                m_currentHistory--;
            else {
                m_currentHistory = 0;
                return;
            }

            /* copy the history command */
            memcpy(m_line, &m_cmdHistory[m_currentHistory][0], FINSH_CMD_SIZE);
            m_lineCurpos = m_linePosition = strlen(m_line);
            ShellHandleHistory();
            return;
        } else if (ch == 0x42) {
            /* next history */
            if (m_currentHistory < m_historyCount - 1)
                m_currentHistory++;
            else {
                /* set to the end of history */
                if (m_historyCount != 0)
                    m_currentHistory = m_historyCount - 1;
                else
                    return;
            }

            memcpy(m_line, &m_cmdHistory[m_currentHistory][0],
                   FINSH_CMD_SIZE);
            m_lineCurpos = m_linePosition = strlen(m_line);
            ShellHandleHistory();
            return;
        } else if (ch == 0x44) /* left key */
        {
            if (m_lineCurpos) {
                this->Printf("\b");
                m_lineCurpos--;
            }

            return;
        } else if (ch == 0x43) /* right key */
        {
            if (m_lineCurpos < m_linePosition) {
                this->Printf("%c", m_line[m_lineCurpos]);
                m_lineCurpos++;
            }

            return;
        }
    }
    /* received null or error */
    if (ch == '\0' || ch == 0xFF) return;
        /* handle tab key */
    else if (ch == '\t') {
        int i;
        /* move the cursor to the beginning of line */
        for (i = 0; i < m_lineCurpos; i++)
            this->Printf("\b");

        /* auto complete */
        ShellAutoComplete(&m_line[0]);
        /* re-calculate position */
        m_lineCurpos = m_linePosition = strlen(m_line);
        return;
    }
        /* handle backspace key */
    else if (ch == 0x7f || ch == 0x08) {
        /* note that line_curpos >= 0 */
        if (m_lineCurpos == 0)
            return;

        m_linePosition--;
        m_lineCurpos--;
        if (m_linePosition > m_lineCurpos) {
            int i;
            rt_memmove(&m_line[m_lineCurpos],
                       &m_line[m_lineCurpos + 1],
                       m_linePosition - m_lineCurpos);
            m_line[m_linePosition] = 0;
            this->Printf("\b%s  \b", &m_line[m_lineCurpos]);

            /* move the cursor to the origin position */
            for (i = m_lineCurpos; i <= m_linePosition; i++)
                this->Printf("\b");
        } else {
            this->Printf("\b \b");
            m_line[m_linePosition] = 0;
        }

        return;
    }

    /* handle end of line, break */
    if (ch == '\r' || ch == '\n') {
        ShellPushHistory();

        this->Printf("\r\n");
        // printf("%s", line);
        m_line[m_linePosition] = '\0';
        g_atShell.Exec(m_line);
        this->Printf("\r\n%s", FINSH_PROMPT);
        memset(m_line, 0, sizeof(m_line));
        m_lineCurpos = m_linePosition = 0;
        return;
    }

    /* it's a large line, discard it */
    if (m_linePosition >= FINSH_CMD_SIZE)
        m_linePosition = 0;

    /* normal character */
    if (m_lineCurpos < m_linePosition) {
        int i;
        rt_memmove(&m_line[m_lineCurpos + 1], &m_line[m_lineCurpos], m_linePosition - m_lineCurpos);
        m_line[m_lineCurpos] = ch;
        this->Printf("%s", &m_line[m_lineCurpos]);
        /* move the cursor to new position */
        for (i = m_lineCurpos; i < m_linePosition; i++)
            this->Printf("\b");
    } else {
        m_line[m_linePosition] = ch;
        this->Printf("%c", ch);
    }

    ch = 0;
    m_linePosition++;
    m_lineCurpos++;
    if (m_linePosition >= FINSH_CMD_SIZE) {
        /* clear command line */
        m_linePosition = 0;
        m_lineCurpos = 0;
    }
}


void AtShell::ShellPushHistory() {
    if (m_linePosition != 0) {
        /* push history */
        if (m_historyCount >= FINSH_HISTORY_LINES) {
            /* if current cmd is same as last cmd, don't push */
            if (memcmp(&m_cmdHistory[FINSH_HISTORY_LINES - 1], m_line, FINSH_CMD_SIZE)) {
                /* move history */
                int index;
                for (index = 0; index < FINSH_HISTORY_LINES - 1; index++) {
                    memcpy(&m_cmdHistory[index][0],
                           &m_cmdHistory[index + 1][0], FINSH_CMD_SIZE);
                }
                memset(&m_cmdHistory[index][0], 0, FINSH_CMD_SIZE);
                memcpy(&m_cmdHistory[index][0], m_line, m_linePosition);
                m_historyCount = FINSH_HISTORY_LINES;
            }
        } else {
            /* if current cmd is same as last cmd, don't push */
            if (m_historyCount == 0 || memcmp(&m_cmdHistory[m_historyCount - 1], m_line, FINSH_CMD_SIZE)) {
                m_currentHistory = m_historyCount;
                memset(&m_cmdHistory[m_historyCount][0], 0, FINSH_CMD_SIZE);
                memcpy(&m_cmdHistory[m_historyCount][0], m_line, m_linePosition);
                m_historyCount++;
            }
        }
    }
    m_currentHistory = m_historyCount;
}


bool AtShell::ShellHandleHistory() {
#if defined(_WIN32)
    int i;
    this->Printf("\r");

    for (i = 0; i <= 60; i++)
      this->Write(' ');
      this->Printf("\r");

#else
    rt_kprintf("\033[2K\r");
#endif
    this->Printf("%s%s", FINSH_PROMPT, m_line);
    return 0;
}

#endif


#if CON_AT_USE_EXPORT == 1
void AtShell::Export(){
    if(m_out_bufLen>0){
        m_writeFun((uint8_t *) m_out_buf, m_out_bufLen,CON_AT_WRITE_TIMEOUT);
    }
    m_out_bufLen=0;
}

#endif

#if CON_AT_USE_EXPORT == 1
int AtShell::AsyncPrintf(const char *format, ...){
    char log_buf[256];
    va_list args;
    va_start(args, format);
    vsprintf(log_buf, format, args);
    uint32_t len = strlen(log_buf);
    if(m_out_bufLen+len < CON_AT_OUT_BUF_SIZE){
        memcpy(m_out_buf + m_out_bufLen, log_buf, len);
        m_out_bufLen=m_out_bufLen+len;
    } else{
        AT_error("error \r\n");
    }
    va_end(args);
    return len;
}
#else
int AtShell::AsyncPrintf(const char *format, ...){
    char log_buf[256];
    va_list args;
    va_start(args, format);
    vsprintf(log_buf, format, args);
    uint32_t len = strlen(log_buf);
    m_writeFun((uint8_t *) log_buf, len,0);
    va_end(args);
    return len;
}
#endif

#if CON_AT_USE_CycleMonitorData==1
static uint8_t _sum(uint8_t * buffer, int len){
    uint8_t sum = 0;
    for (int i = 0; i < len; i++) {
        sum += buffer[i];
    }
    return(sum);
}
int AtShell::MonitorDataPush(const char *tag, uint8_t * buffer, int len){
    if(m_monitorData.curInx==CON_CYCLE_MONITOR_DATA_PACK_NUM){
        m_monitorData.curInx=0;
    }
    sprintf(m_monitorData.tag[m_monitorData.curInx], "%s", tag);
    memcpy(m_monitorData.buffer[m_monitorData.curInx],buffer,len);
    m_monitorData.bufferLen[m_monitorData.curInx]=len;
    m_monitorData.curInx++;
    return 0;
}

int AtShell::MonitorDataViewInfo(){
    int inx=m_monitorData.curInx-1;
    for(int i=0;i<CON_CYCLE_MONITOR_DATA_PACK_NUM;i++){
        if(inx==-1){
            inx=9;
        }
        this->Printf("%s:%3d.%3d@",m_monitorData.tag[inx],m_monitorData.bufferLen[inx],_sum(m_monitorData.buffer[inx],m_monitorData.bufferLen[inx]));
        AT_printfBs( m_monitorData.buffer[inx],m_monitorData.bufferLen[inx]);
        this->Printf("\r\n");
        inx--;
    }
    return 0;
}
#endif



void at_init(ATWriteFun writeFun) {
    g_atShell.Init(writeFun);
}


int at_import(uint8_t *buf, uint32_t len, uint32_t ms) {
    return g_atShell.Import(buf, len, ms);
}
bool at_try_import(uint8_t *buf, uint32_t len, uint32_t ms) {
    if (CON_AT_MSH || strstr((char*)buf, "AT") == (char*)buf || strstr((char*)buf, "\r\n") == (char*)buf) {
        g_atShell.Import(buf, len, ms);
        return true;
    }
    return false;
}

#if CON_AT_USE_EXPORT == 1
void at_export() {
    g_atShell.Export();
}
#endif

bool at_register(AT_CMD_ENTRY_TypeDef cmd) {
    return g_atShell.Regist(cmd);
}

bool at_register_many(AT_CMD_ENTRY_TypeDef *cmdList, int cmdLen) {
    return g_atShell.Regist(cmdList, cmdLen);
}



int at_write(uint8_t *buf, uint32_t len,uint32_t timeout){
    return  g_atShell.Write(buf,len,timeout);
}

int at_printf(const char *format, ...) {
    char log_buf[256];
    va_list args;
    va_start(args, format);
    vsprintf(log_buf, format, args);
    uint32_t len = strlen(log_buf);
    g_atShell.Write((uint8_t *) log_buf, len,CON_AT_WRITE_TIMEOUT);
    va_end(args);
    return len;
}



int at_awrite(uint8_t *buf, uint32_t len){
#if CON_AT_USE_EXPORT == 1
    if(g_atShell.m_out_bufLen+len < CON_AT_OUT_BUF_SIZE){
            memcpy(g_atShell.m_out_buf + g_atShell.m_out_bufLen, buf, len);
            g_atShell.m_out_bufLen=g_atShell.m_out_bufLen+len;
            return len;
        }
        return 0;
#else
    return g_atShell.Write(buf,len,0);;
#endif
}


int at_aprintf(const char *format, ...) {
    char log_buf[256];
    va_list args;
    va_start(args, format);
    vsprintf(log_buf, format, args);
    uint32_t len = strlen(log_buf);
    at_awrite((uint8_t *) log_buf, len);
    va_end(args);
    return len;
}





int at_reply(uint8_t errCode) {
    return g_atShell.Reply(errCode);
}


void at_show_version() {
    g_atShell.ShowVersion();
}


int at_hexStringToByteArray(const char *hexStr, unsigned char *bs) {
    char token[3];
    int i = 0;
    long int byteValue;
    const int byteArrayLen = (strlen(hexStr) + 1) / 3;
    while (*hexStr && i < byteArrayLen) {
        if (isspace(*hexStr)) {
            hexStr++;
            continue;
        }

        if (!isxdigit(*hexStr)) {
            return i;
        }
        token[0] = *hexStr++;
        token[1] = *hexStr++;
        token[2] = '\0';
        byteValue = strtol(token, NULL, 16);
        bs[i] = (unsigned char) byteValue;
        i++;
    }
    return i;
}

long at_str_to_int(char* str) {
    char* endptr;
    int base = 10;
    if (str == 0) {
        return 0;
    }
    if (str[0] == '0') {
        if (str[1] == 'x' || str[1] == 'X') {
            base = 16;
            str += 2;
        }
        else {
            base = 8;
        }
    }
    long result = strtol(str, &endptr, base);
    if (*endptr != '\0') {

        return 0;
    }
    return result;
}

#if CON_AT_USE_CycleMonitorData == 1

void  at_monitor_init(){
    g_atShell.m_monitorData.curInx=0;
}
void  at_monitor_push(const char *tag, uint8_t * buffer, int len){
    g_atShell.MonitorDataPush(tag, buffer, len);
}
void  at_monitor_viewInfo(){
    g_atShell.MonitorDataViewInfo();
}

#endif

LogUtils.h

c 复制代码
#ifndef _LogUtils_HPP
#define _LogUtils_HPP

#include <string>
#include <stdint.h>
using namespace std;

#define LOG_ERROR(...) LogUtils::LogError(__FILE__, __LINE__, __VA_ARGS__)
#define LOG_INFO(...)  LogUtils::LogInfo(__FILE__, __LINE__, __VA_ARGS__)
#define LOG_DEBUG(...) LogUtils::LogDebug(__FILE__, __LINE__, __VA_ARGS__)
#define LOG_DEBUG_DT(...) LogUtils::LogDebugDifTime(__FILE__, __LINE__, __VA_ARGS__)

namespace Common {
    class LogUtils {
    public:
        static int Printf(const char *format, ...) ;
        static int Println(const char *format, ...) ;
        static void PrintBuf(const string tag, uint8_t *buf, uint16_t bufLen) ;
        static void LogInfo(const char *fmt, ...) ;
        static void LogInfo(const char* file, int line, const char* format, ...);
        static void LogDebug(const char *fmt, ...) ;
        static void LogDebugDifTime(const char* file, int line, const char* format, ...);
        static void LogDebug(const char* file, int line, const char* format, ...);
        static void LogError(const char *fmt, ...) ;
        static void LogError(const char* file, int line, const char* format, ...);
    };
}
#endif

LogUtils.cpp

c 复制代码
#include "LogUtils.h"
#include "stdarg.h"
#include "string.h"
#include "stdio.h"
#include <string>


#define CON_ENABLE_LOG 1


using namespace std;
using namespace Common;


int LogUtils::Printf(const char *format, ...) {
    if (CON_ENABLE_LOG) {
        char log_buf[256];
        va_list args;
        va_start(args, format);
        vsprintf(log_buf, format, args);
        va_end(args);
        uint32_t len = strlen(log_buf);
        printf("%s", log_buf);
        return len;
    }
    return 0;
}

int LogUtils::Println(const char *format, ...) {
    if (CON_ENABLE_LOG) {
        char log_buf[256];
        va_list args;
        va_start(args, format);
        vsprintf(log_buf, format, args);
        va_end(args);
        uint32_t len = strlen(log_buf);
        printf("%s\n\r", log_buf);
        return len;
    }
    return 0;
}

void LogUtils::PrintBuf(const string tag, uint8_t *buf, uint16_t bufLen) {
    if (CON_ENABLE_LOG) {
        LogUtils::Printf(tag.c_str());
        for (int i = 0; i < bufLen; i++) {
            LogUtils::Printf("%02x ", buf[i]);
        }
        LogUtils::Printf("\n");
    }
}

void LogUtils::LogInfo(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    fprintf(stdout, "[INFO] ");
    vfprintf(stdout, fmt, args);
    fprintf(stdout, "\n");
    va_end(args);
}


void LogUtils::LogInfo(const char* file, int line, const char* format, ...) {
    char buffer[1024];
    va_list args;
    va_start(args, format);
    vsnprintf(buffer, sizeof(buffer), format, args);
    va_end(args);
    const char* filename = strrchr(file, '/');
    if (!filename) {
        filename = strrchr(file, '\\');
    }
    filename = filename ? filename + 1 : file;
    fprintf(stdout, "[INFO] %s:%d - %s\n", filename, line, buffer);
}

void LogUtils::LogDebug(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    fprintf(stdout, "[Debug] ");
    vfprintf(stdout, fmt, args);
    fprintf(stdout, "\n");
    va_end(args);
}


void LogUtils::LogDebug(const char* file, int line, const char* format, ...) {
    char buffer[1024];
    va_list args;
    va_start(args, format);
    vsnprintf(buffer, sizeof(buffer), format, args);
    va_end(args);
    const char* filename = strrchr(file, '/');
    if (!filename) {
        filename = strrchr(file, '\\');
    }
    filename = filename ? filename + 1 : file;
    fprintf(stdout, "[Debug] %s:%d - %s\n", filename, line, buffer);
}

void LogUtils::LogDebugDifTime(const char* file, int line, const char* format, ...) {
    if(format== nullptr){
        return;
    }
    static uint32_t lastTime=0;
    char buffer[1024];
    va_list args;
    va_start(args, format);
    vsnprintf(buffer, sizeof(buffer), format, args);
    va_end(args);
    const char* filename = strrchr(file, '/');
    if (!filename) {
        filename = strrchr(file, '\\');
    }
    filename = filename ? filename + 1 : file;
    uint32_t nowTime=1;
    fprintf(stdout, "[Debug] %s:%d@t:%d - %s\n", filename, line,nowTime-lastTime, buffer);
    lastTime=nowTime;
}


void LogUtils::LogError(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    fprintf(stderr, "[ERROR] ");
    vfprintf(stderr, fmt, args);
    fprintf(stderr, "\n");
    fflush(stderr);
    va_end(args);
}

void LogUtils::LogError(const char* file, int line, const char* format, ...) {
    char buffer[1024];
    va_list args;
    va_start(args, format);
    vsnprintf(buffer, sizeof(buffer), format, args);
    va_end(args);
    const char* filename = strrchr(file, '/');
    if (!filename) {
        filename = strrchr(file, '\\');
    }
    filename = filename ? filename + 1 : file;
    fprintf(stderr, "[ERROR] %s:%d - %s\n", filename, line, buffer);
    fflush(stderr);
}

EventBus.h

c 复制代码
#ifndef COMMON_EVENT_BUS_H
#define COMMON_EVENT_BUS_H

#include <string>
#include <functional>
#include <map>
#include <vector>
#include <mutex>


#define  EventSubscribe(type,instance,handler) EventBus::GetInstance().Subscribe(type,instance,handler)
#define  EventPublish(type,data) EventBus::GetInstance().Publish(type,data)

namespace Common {

enum class EventType : uint32_t {
        None = 0,
        PUSH_DATA=1
    };

class EventBus {
public:
    // 事件处理函数类型
    template<typename T>
    using MemberHandler = void (T::*)(EventType event, void* data);

    // 获取单例实例
    static EventBus& GetInstance();

    // 订阅事件(使用成员函数)
    template<typename T>
    void Subscribe(EventType eventType, T* instance, MemberHandler<T> handler) {
        auto callback = [instance, handler](EventType evt, void* data) {
            (instance->*handler)(evt, data);
        };
        Subscribe(eventType, callback);
    }
    // 订阅事件(使用普通函数或 lambda)
    void Subscribe(EventType eventType, std::function<void(EventType, void*)> handler);
    // 取消订阅
    void Unsubscribe(EventType eventType);
    // 发布事件
    void Publish(EventType event, void* data);
    // 检查是否有订阅者
    bool HasSubscribers(EventType event) const;
    // 获取订阅者数量
    size_t GetSubscriberCount(EventType event) const;

private:
    EventBus();
    ~EventBus();
    struct Storage;
    Storage* storage_;
};

} // namespace Common

#endif // COMMON_EVENT_BUS_H 

EventBus.cpp

c 复制代码
#include "EventBus.h"
#include "LogUtils.h"
#include "pico/sync.h"

namespace Common {

    struct EventBus::Storage {
        critical_section_t cs;
        std::map<EventType,
                std::vector<std::function<void(EventType, void*)>>> subs;
    };

    EventBus& EventBus::GetInstance() {
        static EventBus instance;
        return instance;
    }

    EventBus::EventBus() {
        storage_ = new Storage;
        critical_section_init(&storage_->cs);
    }
    EventBus::~EventBus() {
        critical_section_deinit(&storage_->cs);
        delete storage_;
    }

    void EventBus::Subscribe(EventType eventType, std::function<void(EventType, void*)> handler) {
        critical_section_enter_blocking(&storage_->cs);
        storage_->subs[eventType].push_back(handler);
        critical_section_exit(&storage_->cs);
    }

    void EventBus::Unsubscribe(EventType eventType) {
        critical_section_enter_blocking(&storage_->cs);
        storage_->subs.erase(eventType);
        critical_section_exit(&storage_->cs);
    }

    void EventBus::Publish(EventType eventType, void* data) {
        std::vector<std::function<void(EventType, void*)>> handlers;
        critical_section_enter_blocking(&storage_->cs);
        auto it = storage_->subs.find(eventType);
        if (it != storage_->subs.end())
            handlers = it->second;
        critical_section_exit(&storage_->cs);
        for (auto& h : handlers){
            h(eventType, data);
        }

    }

    bool EventBus::HasSubscribers(EventType eventType) const {
        auto it = storage_->subs.find(eventType);
        return it != storage_->subs.end() && !it->second.empty();
    }

    size_t EventBus::GetSubscriberCount(EventType eventType) const {
        auto it = storage_->subs.find(eventType);
        return it != storage_->subs.end() ? it->second.size() : 0;
    }
}

配置文件

.gitignore

bash 复制代码
cmake-build-debug
# 第一步:忽略 .idea 目录下的所有文件/子目录
.idea/*
!.idea/encodings.xml
!.idea/

wokwi.toml

bash 复制代码
[wokwi]
version = 1
firmware = "cmake-build-debug/pipo_project.uf2"
elf = "cmake-build-debug/pipo_project.elf"
rfc2217ServerPort = 4000

pico_sdk_import.cmake

bash 复制代码
# This is a copy of the pico_sdk_import.cmake file from the Pico SDK
# It's needed to properly import the Pico SDK in CMake projects

if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
    set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
    message("Using PICO_SDK_PATH from environment: ${PICO_SDK_PATH}")
endif ()

if (NOT PICO_SDK_PATH)
    message(FATAL_ERROR "PICO_SDK_PATH is not defined. Either set it in the environment, or pass -DPICO_SDK_PATH=xxx to cmake")
endif ()

set(PICO_SDK_INIT_CMAKE_FILE "${PICO_SDK_PATH}/pico_sdk_init.cmake")
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
    message(FATAL_ERROR "Could not find pico_sdk_init.cmake in ${PICO_SDK_PATH}")
endif ()

include( ${PICO_SDK_INIT_CMAKE_FILE})

message(AAAAAAAAAAAAA ${PICO_SDK_INIT_CMAKE_FILE})

diagram.json

json 复制代码
{
  "version": 1,
  "author": "wang minglie",
  "editor": "wokwi",
  "parts": [
    {
      "type": "wokwi-pi-pico",
      "id": "pico",
      "top": -3.15,
      "left": 3.6,
      "attrs": { "builder": "pico-sdk" }
    },
    { "type": "wokwi-logic-analyzer", "id": "logic1", "top": -143.65, "left": 326.4, "attrs": {} }
  ],
  "connections": [
    [ "pico:GP0", "$serialMonitor:RX", "", [] ],
    [ "pico:GP1", "$serialMonitor:TX", "", [] ],
    [ "pico:GP21", "logic1:D0", "green", [ "h116.4", "v-230.4", "h19.2", "v-38.4" ] ]
  ],
  "dependencies": {}
}

CMakeLists.txt

bash 复制代码
cmake_minimum_required(VERSION 3.13)

# Set Pico SDK path

# Include the Pico SDK CMake configuration
include(pico_sdk_import.cmake)

project(pipo_project C CXX ASM)
pico_sdk_init()
add_executable(pipo_project
        src/main.cpp
        src/mi_hw_alarm.cpp
        src/user/user_atshell.cpp
        src/lib/AtShell.cpp
        src/common/LogUtils.cpp
        src/AlarmProtothread.cpp
)


target_link_libraries(
        pipo_project
        pico_stdlib
        hardware_exception
)


# Enable USB output, disable UART output
pico_enable_stdio_usb(pipo_project 0)
pico_enable_stdio_uart(pipo_project 1)

pico_add_extra_outputs(pipo_project)

copy_uf2.bat

bash 复制代码
@echo off
set "src=D:\workspace\gitee\2\ming_picoman\cmake-build-debug\pipo_project.uf2"
set "dst=F:"

if not exist "%src%" (
    echo Error: UF2 file not found!
    pause
    exit 1
)
if not exist "%dst%" (
    echo Error: USB drive %dst% not found!
    pause
    exit 1
)

copy /Y "%src%" %dst%
if %errorlevel% equ 0 (
    echo Success! Pico is rebooting...
) else (
    echo Failed! Reconnect Pico and try again.
)
pause
相关推荐
Godspeed Zhao2 小时前
自动驾驶中的传感器技术79——Sensor Fusion(2)
人工智能·fpga开发·自动驾驶
ShiMetaPi3 小时前
GM-3568JHF丨ARM+FPGA异构开发板系列教程:外设教程 07 音频
arm开发·fpga开发·音视频·fpga·rk3568
bitQ13 小时前
ZBoot-MP:ZYNQ 多介质 Linux 启动与升级解决方案
fpga开发
禾仔仔15 小时前
USB MSC从理论到实践(模拟U盘为例)——从零开始学习USB2.0协议(六)
嵌入式硬件·mcu·计算机外设
slp94101920 小时前
SCB_Type CPACR 寄存器解析 ---fpu
mcu
我爱C编程1 天前
【1.22】基于FPGA的Costas环开发课程学习总结
学习·fpga开发·costas环
FPGA_无线通信1 天前
USB3.0 GPIF II接口
fpga开发
Terasic友晶科技1 天前
5-DE10-Nano的HDMI方块移动案例——基于FPGA的I2C控制模块设计
单片机·嵌入式硬件·fpga开发·时钟分频·hdmi方块移动·i2c控制模块设计·配置寄存器
Joshua-a1 天前
AGM FPGA AG10K 使用Supra新建Quartus工程开发教程
fpga开发