邀黑桃三回答个问题😁
邀黑桃三回答个问题😁:
怎么做到链接跳转时站外新页面, 站内当前页面?
邀黑桃三回答个问题😁:
怎么做到链接跳转时站外新页面, 站内当前页面?
原文https://pyserial.readthedocs.io/en/latest/tools.html
安装pyserial
python -m pip install pyserial
使用Miniterm工具
python -m serial.tools.miniterm COM5 115200
在cmd
命令行窗口支持ESC颜色
python -m serial.tools.miniterm COM5 115200 -f direct
远程不能直接使用vscode idf插件的命令行, 需要打开idf插件的配置,将其中的path等信息改成bat
运行一下.
示例如下:
set path=D:\r\esp\.espressif\tools\xtensa-esp32-elf\esp-2021r2-8.4.0\xtensa-esp32-elf\bin;D:\r\esp\.espressif\tools\xtensa-esp32s2-elf\esp-2021r2-8.4.0\xtensa-esp32s2-elf\bin;D:\r\esp\.espressif\tools\xtensa-esp32s3-elf\esp-2021r2-8.4.0\xtensa-esp32s3-elf\bin;D:\r\esp\.espressif\tools\riscv32-esp-elf\esp-2021r2-8.4.0\riscv32-esp-elf\bin;D:\r\esp\.espressif\tools\esp32ulp-elf\2.28.51-esp-20191205\esp32ulp-elf-binutils\bin;D:\r\esp\.espressif\tools\esp32s2ulp-elf\2.28.51-esp-20191205\esp32s2ulp-elf-binutils\bin ;D:\r\esp\.espressif\tools\cmake\3.20.3\bin;D:\r\esp\.espressif\tools\openocd-esp32\v0.10.0-esp32-20211111\openocd-esp32\bin;D:\r\esp\.espressif\tools\ninja\1.10.2;D:\r\esp\.espressif\tools\idf-exe\1.0.3;D:\r\esp\.espressif\tools\ccache\4.3\ccache-4.3-windows-64;D:\r\esp\.espressif\tools\dfu-util\0.9\dfu-util-0.9-win64;D:\r\esp\.espressif\tools\idf-git\2.30.1\cmd\;D:\r\esp\.espressif\python_env\idf4.4_py3.8_env\Scripts\;%PATH%
set IDF_PATH=d:\r\esp\esp-idf\
set IDF_TOOLS_PATH=D:\r\esp\.espressif
Set OPENOCD_SCRIPTS=D:\\r\\esp\\.espressif\\tools\\openocd-esp32\\v0.10.0-esp32-20211111/openocd-esp32/share/openocd/scripts
set IDF_CCACHE_ENABLE=1
还有一种方式是在esp-idf
下有一些export.bat/.sh
文件, 是用于配置idf环境的,不过可能需要修改.
通过 SSHFS windows manager
可以挂载远程硬盘. 参考文章
项目地址: https://github.com/tencentyun/qcloud-iot-esp-wifi/tree/master/qcloud-iot-esp8266-demo
文档地址: https://cloud.tencent.com/document/product/1081/48370
8266项目的SDK又是基于腾讯的C SDK抽取的, C SDK地址: https://github.com/tencentyun/qcloud-iot-explorer-sdk-embedded-c
8266项目目录下components/qcloud_iot/qcloud_iot_c_sdk/includes
中的几个文件是要修改的参数和调用的api放置的地方.
还有三元组信息在 components/qcloud_iot/qcloud_iot_c_sdk/platform
下的HAL_Device_freertos.c
文件中. 居然不是在头文件里定义, 感觉挺糟糕的.
qcloud_iot_export.h
这个qcloud_iot_export.h
文件实际上include了所有在exports
目录里面的.h文件. 所有的接口API在其中描述.
qcloud_iot_export_variables.h
MQTT的心跳时间240s建议改为200s. 默认240s与服务器端太一致了, 设备时钟慢一点就会被服务器踢出.
/* default MQTT keep alive interval (unit: ms) */
#define QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL (200 * 1000) // 原设置: 240*1000
HAL_Device_freertos.c
#ifdef DEBUG_DEV_INFO_USED
/* product Id */
static char sg_product_id[MAX_SIZE_OF_PRODUCT_ID + 1] = "PRODUCT_ID"; // 修改
/* device name */
static char sg_device_name[MAX_SIZE_OF_DEVICE_NAME + 1] = "YOUR_DEV_NAME"; // 根据mac生成的话, 需要在代码中动态修改 . 似乎应该在HAL_SetDevInfo()调用前或者干脆就在这个函数里修改
/* device secret of PSK device */
static char sg_device_secret[MAX_SIZE_OF_DEVICE_SECRET + 1] = "YOUR_IOT_PSK";
/* region */
static char sg_region[MAX_SIZE_OF_PRODUCT_REGION + 1] = "ap-guangzhou";
#ifdef GATEWAY_ENABLED
/* sub-device product id */
static char sg_sub_device_product_id[MAX_SIZE_OF_PRODUCT_ID + 1] = "PRODUCT_ID";
/* sub-device device name */
static char sg_sub_device_name[MAX_SIZE_OF_DEVICE_NAME + 1] = "YOUR_SUB_DEV_NAME";
#endif
#ifdef DEV_DYN_REG_ENABLED
/* product secret for device dynamic Registration */
static char sg_product_secret[MAX_SIZE_OF_PRODUCT_SECRET + 1] = "YOUR_PRODUCT_SECRET"; // 动态验证需要改这儿
#endif
component.mk
如果在main
目录下自定义了新的文件夹用于放源码, 需要将这个文件夹加入编译路径 ,在component.mk
中, 如增加一行:
COMPONENT_SRCDIRS += ./lamploop
在samples
目录下有4个demo程序, 实际上是根据sdkconfig
里面的配置选择不同的demo进行编译. 以data_template_light
的主流程示例如下:
main.c中的app_main()创建qcloud_demo_task任务
-->演示获取WIFI信息
-->联网
-->设置时间服务器
-->调用主逻辑qcloud_iot_explorer_demo
-->qcloud_iot_explorer_demo在samples/data_template_light/light_data_template_sample.c中定义
-->初始化连接信息_setup_connect_init_params
-->构建物模型也就是所谓的数据模板IOT_Template_Construct
-->初始化数据模板_init_data_template
-->注册模板属性_register_data_template_property
-->注册模板动作_register_data_template_action
-->获取系统信息_get_sys_info, 并上报IOT_Template_Report_SysInfo_Sync
-->获取数据状态IOT_Template_GetStatus_sync
-->处理下行数据逻辑deal_down_stream_user_logic
-->使能ota任务enable_ota_task
-->初始化报告时钟
-->倒计时10秒
-->进入task的死循环
-->断开连接(!IOT_Template_IsConnected)超过20秒直接跳出死循环
-->判断固件在下载is_fw_downloading, 睡0.5秒后,进入下次循环
-->收到控制消息sg_control_msg_arrived
-->进入控制信令处理deal_down_stream_user_logic
-->回复服务器IOT_Template_ControlReply
-->构造变化属性的报告deal_up_stream_user_logic(pReportDataList, &ReportCount)
-->如果属性有变化, 则构造报告数组IOT_Template_JSON_ConstructReportArray
-->上传报告数组IOT_Template_Report
-->如果开启了事件处理, 则进行事件处理.
-->睡到1秒
-->如果各种原因跳出了循环(看到的原因只有断开连接)
-->关闭ota任务
-->销毁模板
下面基于乐鑫的v3.1example/project_template
项目基础上, 增加qcloud_iot功能
为解说方便, 将qcloud-iot-esp8266-demo
项目称之为qcloud
项目, 将将project_template
项目称之为template
项目
components\qcloud_iot\qcloud_iot_c_sdk
目录复制到template项目的components\qcloud_iot_c_sdk
,去掉一级目录qcloud_iot
.components\component.mk
复制过来sdkconfig
复制过来, 或者进入make menuconfig
手动修改. 主要要改component config-->SSL-->mbedTLS
下的内容:
在`TLS Key Exchange Methods`下修改:
要深入了解文档,还是要看C-SDK项目的示例而不是8266-C-SDK的项目示例
qcloud-iot-explorer-sdk-embedded-c
视频教程
https://git.code.tencent.com/hubertxxu/qcloud_iot_explorer_esp8266
参考https://code.visualstudio.com/docs/remote/ssh-tutorial
远程开发的机器称为服务器, 如果是win10的话, 要安装并运行sshd服务, 并且服务使用powershell
作为命令行, 参见前一篇文章.
如果开发服务器在内网,可以使用中转穿透服务frp.
如果都装好了, 使用ssh也可以登录了, 如果是通过frp登录的话, 命令可能是ssh username@frp_server_ip -p 7777 -oPort=7000
, 其中7777是frpc客户端(也就是开发服务器)要求frps服务端开启的端口, 7000是frps固有服务端口.
在vscode上点击左下角, 选择connect to host...
输入ssh命令, 不需要输入-oPort=7000
部分,按说明还需要加一个-A
(干吗用的还不知道), 如: ssh username@frp_server_ip -p 7777 -A
然后就可以连接成功了.
如果远程开发服务器上有docker容器, 可以在vscode的cmd窗口进入docker容器, 命令如下:
docker exec -it 容器名 /bin/bash
如果容器里面没有bash
还可以把bash换成sh
有个很有意思的现象: 如果vscode连接的开发服务器必须同时开着sshd
和frpc
, 但是如果vscode已经连上了, sshd
就可以关掉了, 只开着frpc
就可以.这时候ssh
命令行已经无法连接上了. 似乎vscode借用了22端口,但连接上以后没有走sshd协议
参考https://winaero.com/enable-openssh-server-windows-10/#:~:text=Enable%20the%20OpenSSH%20Server%20in%20Windows%2010%201,on%20the%20Install%20button.%205%20Restart%20Windows%2010.
https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse
SSH服务器
和SSH客户端
都安装.C:\windows\system32\OpenSSH
目录下就会安装ssh的相关文件, 包括sshd.exe
, 可以直接运行sshd就开启服务,也可以按文章的介绍在服务
中开启.services.msc
, 查看OpenSSH SSH server
项, 双击, 选择登录
页面,查看或修改可以登录的用户. 设置服务为自动
并开启运行.cmd
,运行ssh-keygen -A
生成服务端key, 默认生成的位置在C:\ProgramData\ssh
. 这儿还存放sshd_config
文件, 如果没有就创建一个.ssh-keygen
,生成本地ssh keyid_rsa
和id_rsa.pub
,位置在C:\Users\username/.ssh/
,这儿还存放authorized_keys
. authorized_keys
就是其他客户端的pub key的集合ssh-keygen
,生成本地ssh keyid_rsa
和id_rsa.pub
, 将id_rsa.pub
复制一份改名为authorized_keys
到服务端的C:\Users\username/.ssh/
目录regedit
, New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -PropertyType String -Force
当然你也可以改成其他命令行如cmd
或者bash
,但是如果要使用vscode远程的话必须设置为如上的powershell
总结: 涉及的目录和注册表有以下几项:
软件安装目录: C:\windows\system32\OpenSSH
服务key存放目录: C:\ProgramData\ssh
用户Key存放目录: C:\Users\username/.ssh/
服务名: OpenSSH SSH Server, 或 sshd
注册表项: HKLM:\SOFTWARE\OpenSSH, DefaultShell
官网示例是针对端口全开的公网ip, 而阿里云是有安全策略, 需要开放端口.
frp用起来算是及其简单惬意了.
systemd
系统服务. 先进入frp解压目录, 然后是如下步骤:cp systemd/*.* /usr/lib/systemd/system/
cp frps /usr/bin/
chmod +x /usr/bin/frps
mkdir /etc/frp
cp frps.ini /etc/frp/
systemctl start frps
systemctl status frps
最后一条命令可以看到frps是否启动成功.
可以看一下cat /etc/frp/frps.ini
的内容, 默认是:
[common]
bind_port = 7000
这时候可以看一下frps的端口占用:
#netstat -tunlp|grep frps
tcp6 0 0 :::7000 :::* LISTEN 4928/frps
aliyun.com
, 在ECS服务器->实例->安全组
中的入方向
添加一条TCP记录, 端口号写一个区间:7000/7100
.后面frp配置的所有端口号都要在这个范围内才能访问. 授权对象为0.0.0.0/0
frpc.ini
,假设服务器地址是x.x.x.x
,如下:
[common]
server_addr = x.x.x.x
server_port = 7000
[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 7001
运行`frpc`
5.这时候可以回到阿里云服务器看一下frps的端口占用,发现服务器打开了7001端口:
tcp6 0 0 :::7000 ::: LISTEN 4928/frps
tcp6 0 0 :::7001 ::: LISTEN 4928/frps
6. SSH客户端连接SSH服务器
ssh username@x.x.x.x -p 7001 -oPort=7000
https://zhuanlan.zhihu.com/p/303175108
本想用ngork, 然而注册总是不成功. 另一个natapp的页面看起来似乎运营实力一般,再加上免费版本有些限制, 所以就看了看其他的方式.
因为有个阿里云的服务器, 最后选择frp
注意: 固件编译只能通过 docker进行.
原因是:
xtensa-lx106-elf-linux64-1.22.0-92-g8facf4c-5.2.0.tar.gz
), 所以干脆用了腾讯提供的docker img了.docker img获取方式: docker pull hubertxxu/esp8266_build:0.1
, 由腾讯的xph
提供.
docker项目文件夹在/r/
下.
docker linux 版本:
uname -a
Linux bff9dfda8fd0 5.10.16.3-microsoft-standard-WSL2 #1 SMP Fri Apr 2 22:23:49 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
支持yum install
命令
docker容器挂载windows目录的启动方式:
docker run --name esp -v path/to/host/folder:/path/to/container/folder -dt hubertxxu/esp8266_build:0.1
挂载以后进行编译.
container修改esp工具链可访问性
chmod -R 777 /home/esp8266
cd /etc
mv yum.repos.d yum.repos.d.backup
mkdir yum.repos.d
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-8.repo
另外, dnf
是比yum更先进的包管理工具,可以换用dnf
使用了下面一长串的命令启动了ssh, 但是docker内的ip不是真实ip,host ping container是不通的. 我在host上建立了ftp server,通过container登录来看container的ip,发现竟然就是host的ip.
使用如下步骤开启了sshd
yum install net-tools
yum install openssh-server
yum install passwd
mkdir -p /var/run/sshd
ssh-keygen -A
然后编辑sshd配置
vi /etc/ssh/sshd_config
打开以下配置
Port 22
ListenAddress 0.0.0.0
PasswordAuthentication Yes
设置root密码
passwd
启动服务
/usr/sbin/sshd -D &
查看进程和端口占用
ps -A
netstat -nptl
本地连接测试
ssh localhost
最近做完了通过8266 Qcloud AT做腾讯连连的台灯的项目, 使用enum做连接状态记录, 在做网络连接的过程中发现了一些问题, 这儿讨论一下.
typedef enum _ESP_STATUS{
ESP_STATUS_INIT,
ESP_STATUS_READY,
ESP_STATUS_GOT_MAC,
ESP_STATUS_CONFIGED_NTP_SERVER,
ESP_STATUS_GOT_TIME,
ESP_STATUS_CONNECTED_MQTT,
ESP_STATUS_SUBED_MQTT,
ESP_STATUS_WIFI_DISCONNECTTED,
}ESP_STATUS;
上面这种enum用下划线, type去掉下划线, 并在枚举项内体现type名称的命名习惯从华为开始就确定了. 这个不需要讨论.
从上到下依次为按时间顺序会依次进入的状态, 这个也没有问题.
GOT_MAC
先动作再对象也是对的.
想讨论的READY/GOT/CONFIGED
这种过去时态. 我认为不如进行时态. 过去时态会造成当前状态与上一个状态在意义上的耦合. 因为READY以后就要获取MAC, 所以READY
不如改名为GETTING_MAC
, GOT_MAC
不如改名为CONFIGING_NTP_SERVER
. 也就是凡是要主动做操作的, 都用进行时态.
另外还有一种是处在状态中不主动动作, 而是被动等待状态改变(相当于什么也不做). 名称应改为WAITFOR_DO_XXX
, 如WAITFOR_RECONNECT_WIFI
.
修改如下:
typedef enum _ESP_STATUS{
ESP_STATUS_WAITFOR_READY,
ESP_STATUS_GETTING_MAC,
ESP_STATUS_CONFIGING_NTP_SERVER,
ESP_STATUS_GETTING_TIME,
ESP_STATUS_CONNECTING_MQTT,
ESP_STATUS_SUBING_MQTT,
ESP_STATUS_REPORTING_MQTT_PROPERTY,
ESP_STATUS_WAITFOR_CONNECT_WIFI
}ESP_STATUS;
其中, ESP_STATUS_SUBING_MQTT
又可以分为对SERVICE和PROPERTY分别SUB, 两者虽然没有先后之分, 但是仍应该人为规定先后顺序并放入枚举项中. 改为:
typedef enum _ESP_STATUS{
ESP_STATUS_INIT, // 这时候等待READY
ESP_STATUS_GETTING_MAC,
ESP_STATUS_CONFIGING_NTP_SERVER,
ESP_STATUS_GETTING_TIME,
ESP_STATUS_CONNECTING_MQTT,
ESP_STATUS_SUBING_MQTT_SERVICE,
ESP_STATUS_SUBING_MQTT_PROPERTY,
ESP_STATUS_REPORTING_MQTT_PROPERTY,
}ESP_STATUS;
.h文件
是用来放置public公有性质的内容, #define 常量
, typedef enum
, typedef struct
, extern 全局变量
, 公用函数声明返回值 函数值(参数列表);
. 不可以做c文件的私有全局变量声明, 也不可以做函数定义, 也不可以做c文件私有函数声明.
.c文件
用来放置公有和私有的全局变量定义, 私有函数声明, 共有和私有的函数定义.
.h
文件内部由于有了#ifndef 头文件名 #define 头文件名 #endif
的结构, 所以可以#include
其他.h
文件.
.c
文件任何时候都不应被其他文件#include
将1对同名.h
和.c
文件作为一个模块的话, 项目中为模块划分层次, 底层模块为上层提供服务, 设置基本共识. 需要注意的是上层模块可以#include下层模块, 但下层模块不能#include上层模块, 也就是说, 下层不应依赖上层, 上层的任何变化不应使下层模块无法使用, 无论上层如何变化下层都应提供一致的服务.
为进一步让上层解耦, 摆脱对特定实现方式的依赖, 下层可以定义标准接口性质的.h
文件, 上层#include和调用标准.h, 下层在不修改标准.h的情况下进行升级修改, 或者完全替换为另一种实现.
同层模块可以相互#include.
目前考虑, 项目基础配置处于最底层L0. 硬件资源驱动处于L1, 基于硬件驱动提供的服务处于L2, 操作系统处于L3, 基于操作系统提供的服务处于L4, 具体应用处于L5.
按这种划分, 以灯为例, 作表如下:
层数 | 层名 | 功能模块 |
---|---|---|
L5 | 应用层 | 灯的逻辑功能 |
L4 | 系统服务层 | 系统Tick的时钟 |
L3 | 系统层 | RTOS |
L2 | 硬件服务层 | 驱动回调 |
L1 | 硬件驱动层 | Uart, Adc |
L0 | 配置层 |
amazon官方文档, 中文好评!https://docs.amazonaws.cn/freertos/index.html
官网: https://freertos.org/
官方文档,英文的
AWS上的文档,也是英文的
FreeRTOS与RT-Thread对比: 后者自带很多第三方协议, 如i2c/mqtt等等, 而FreeRTOS似乎要自己找自己组装进去.
任务:
xTaskCreate
建立多个任务, 然后使用vTaskStartScheduler
调度任务函数. 任务函数执行完成任务后应该进入等待.
队列:
xQueueCreate
建立队列, xQueueReceive
接收队列.
根据FreeRTOS操作系统最全面使用指南 整理
似乎就主要3个功能: 任务调度/FIFO队列通讯/内存管理.
时间管理主要就是一个延迟函数vTaskDelay
下面看官网教程https://www.freertos.org/FreeRTOS-quick-start-guide.html的Developer Docs
部分. 描述Kernel部分.
FreeRTOS用的是MISRA标准.
变量类型 | 前缀 |
---|---|
uint32_t | ul |
uint16_t | us |
uint8_t | uc |
有符号非标准int(如Type_t/TickType_t/size_t) | x |
无符号非标准int(如UbaseType_t) | ux |
enum类型 | e |
* 指针 | p |
函数类型 | 前缀 |
---|---|
文件内函数(类似private) | prv |
void空返回函数 | v |
其他返回值函数 | 参照变量前缀 |
宏也有前缀, 应含有或部分含有所在的文件名(小写).其他部分为大写, 如: configUSE_PREEMPTION
, 习惯了全大写的看起来怪怪的
//
注释, 哈哈. 一行80列以内.#include
的顺序是标准库/自己的库/硬件特定的库#include
后面依次是#define
, 文件内函数声明(都加了static
), 文件内全局变量声明, 函数定义/*-------*/
分行FreeRTOS要做的跟Windows一样, 一个任务是一个窗口, 有自己的上下文和环境, 看上去就像独立运行的一样. 多个任务看起来就像是同时在运行, 实际上由任务调度去决定任务的切换, 每一时间只有一个任务占领CPU(当然Windows也是一样的)
占领CPU的时候就是 running态, 暂时调度给其他任务就是ready态.
任务自己进入等待而把CPU主动让出来交Blocked态, 如调用了vBlockDelay()
或等待其他事件. 等到了就恢复ready,或者实在等不到(超时timeout了)也恢复ready
啥也不干的任务可以调用vTaskSuspend()
进入Suspended态, 跟Blocked区别就是不会超时. 只有调用xTaskResume()
才会回到ready态.
数字高的先运行. 0是最低数字. 当前运行的就是最高优先级的一个或多个ready任务. 多个任务同时的话就用时间片切换.
高优先级永远最先运行, 如果占着CPU不放, 低优先级永远不会运行. 所以要高风亮节, 等事的时候就把CPU让出来, 进入Blocked或者Suspended, 事儿来了再处理.
抢占式和时间分片这两个特性可以在FreeRTOSConfig.h
中关闭.
另外支持大小核(AMP)和同等多核(SMP)等CPU
void vATaskFunction( void *pvParameters )
{
for( ;; )
{
-- Task application code here. --
}
/* Tasks must not attempt to return from their implementing
function or otherwise exit. In newer FreeRTOS port
attempting to do so will result in an configASSERT() being
called if it is defined. If it is necessary for a task to
exit then have the task call vTaskDelete( NULL ) to ensure
its exit is clean.
翻译: 任务别想着return, 新的port里这样做会导致configASSERT()
调用报错. 如果非得退出, 调用vTaskDelete( NULL ) */
vTaskDelete( NULL );
}
函数形态咯. for(;;)
的意思是任务一直执行, 直到vTaskDelete
, 相当于windows的×
. 任务里面是坚决不能有return
的, 否则就会报错.
一直执行的任务里面, 自己决定是不是要Blocked或者Suspended把CPU让出来. 所以这个for(;;)在裸机系统里面看起来像是个死锁, 实际上却是让任务保持运行的方式, 毕竟它不能return
任务函数的类型是TaskFunction_t
void vATaskFunction( void *pvParameters )
{
for( ;; )
{
/* Psudeo code showing a task waiting for an event
with a block time. If the event occurs, process it.
If the timeout expires before the event occurs, then
the system may be in an error state, so handle the
error. Here the pseudo code "WaitForEvent()" could
replaced with xQueueReceive(), ulTaskNotifyTake(),
xEventGroupWaitBits(), or any of the other FreeRTOS
communication and synchronisation primitives.
翻译: 下面伪码展示了任务等待事件的方式. 事件发生了就处理事件,
超时了就处理报错. 伪码WaitForEvent可以换成 xQueueReceive(),
ulTaskNotifyTake(), xEventGroupWaitBits()或其他通讯同步机制 */
if( WaitForEvent( EventObject, TimeOut ) == pdPASS )
{
-- Handle event here. --
}
else
{
-- Clear errors, or take actions here. --
}
}
/* As per the first code listing above. */
vTaskDelete( NULL );
}
任务使用 xTaskCreate()
xTaskCreateStatic()
创建, 使用vTaskDelete()
销毁.
超小RAM的MCU可以用协程, 32位的CPU一般就用不到了. 这说的是Arduino的那个8位机吗? 这儿也不介绍了, 一般这种情况估计都用裸机了.
fromISR
结尾的API函数. ISR意思是Interrupt Service Routine中断服务事务,就是中断处理函数.先说啥是信号量. 简单地说就是解决资源占用的方式, 跟十字路口的信号灯一样. 拿到绿灯的占用路口走, 给另一个方向的车辆红灯, 另一个方向的就等着(Blocking).有限的资源多个任务都想用, 就用信号量来解决. 也有点像停车场剩余车位数(计数信号量). 没车位的都在外面等着(Blocking).
这儿和这儿有中文讲解.
互斥和同步. 互斥就是一个球只有一个人拍, "排斥"了其他人拍球, 想玩的排队. 同步就是想做才得先小工洗菜,再大厨炒菜,再跑堂端菜, 都是菜, 但就是得按顺序处理, 没到操作步骤的也排着队等.这儿看到同步也是一种互斥, 除了等资源, 还得步骤到了.
我在处理腾讯连连的MQTT时可能就是一种同步机制, 需要按顺序处理ready->GOT IP->时间->MQTT.只不过我是用enum的不同状态表示的.
二进制信号量就是占用时加锁, 释放时解锁, 表明这两种状态的.
Task Notifications任务通知
有时候可以替代二进制信号量, 并且更轻量一些.
xSemaphoreGiveFromISR()
同样
Task Notifications任务通知
有时候做轻量级替代.
我在MQTT处理对pub消息的回应时用了类似的计数机制. 为防止在连续发出的第二条pub指令之后, 实际执行pub之前, 收到了上一条pub指令的success消息, 将pub指令取消导致第二条pub没有执行(同时还要兼顾失败重发机制), 我在pub发出指令后增加计数/在success时减少计数, 只有计数为0时才完全停止pub.
用于对有限资源的访问.
互斥量借用完要自己还回去(只有1个且不会被消耗), 而二进制信号量用完不用还(持续被生产出来并且被消耗掉).
低优先级的任务持有互斥量时, 高优先级的再去获取, 会把低优先级的任务的优先级暂时提高到高优先级.
适合任务间确认谁访问资源, 不适合中断使用. 因为中断是不可能去等待任务给他返还可用性的.
可以被一个任务连续借用多次, 但也要被还回去这么多次, 其他任务才能用.
算是新特性, 从v8.2.0开始引入, 在v10.4.0中增加了单任务多通知功能
xTaskNotifyWait()/xTaskNotifyWaitIndexed()
读了值以后, 通知状态就会变成Not Pending.xTaskNotifyStateClear()/xTaskNotifyStateClearIndexed()
清除通知的Pending态xTaskNotifyIndexed()
xTaskNotifyGiveIndexed()
发通知. 通知进入Pending态, 直到任务B调用 xTaskNotifyWaitIndexed()
ulTaskNotifyTakeIndexed()
. 函数名里面的Indexed
可以去掉, 就对应的是Index==0
的通知(感觉是为了保持版本的兼容性)10.0.0以后才有的特性
TBD. 这么新的特性估计暂时用不到, 晚些在研究.
最新版本有5种堆管理方式.
这部分感觉暂时也用不上, 看原文吧.
直到vTaskStartScheduler()
调用后, 程序才表现得像个RTOS的样子.
百度搜索了资料, 似乎只能通过打印信息调试, 搜索"FreeRTOS 断点调试"找不到结果.
Bing搜索"FreeRTOS breakpoint debug"还是能找到几条使用Keil设置断点调试的方式, 主要是说要在task里面设置断点, 并且不能用步入模式调试.
我设想一种调试方式, 即只针对task进行调试, 把task当做一个系统. 把FreeRTOS的API函数和硬件函数全部Stub来调试逻辑. 剩下的硬件逻辑和OS相关逻辑通过真机printf调试. 当然单元测试应该是可以用的.
似乎要先看这个FreeRTOS+lwIP 平台接入指引, 里面会以8266举例. 然后再看C SDK使用参考
另外还有个C SDK 移植接入指引不知道有用否.
SDK 说明及下载似乎包括了所有有关SDK的下载.
本来用流程图做, 但发现流程图这个形式很难做, 箭头重复的太多: 【腾讯文档】腾讯连连ESP8266-QCloud-AT固件使用指南 ,大概原因是腾讯文档只能用直角箭头.
改用思维导图做.【腾讯文档】腾讯连连ESP8266-QCloud-AT固件使用指南