xuan9527

常能遣其欲而心自静,澄其心而神自清。

0%

杂项笔记

J-Flash批处理脚本配置烧录:当然,前提是要添加J-Link的可执行程序路径到$PATH环境变量中

  • program.bat脚本代码如下,参考修改即可:
    1
    2
    3
    echo start...

    JLink -device N32L406CB -if swd -speed 4000 -CommanderScript "C:\Users\Breo\Desktop\Wireless moxibustion\Software\program.jlink"
  • program.jlink文件代码如下,其中目标设备、文件路径、烧录地址等根据需要配置:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    connect
    device N32L406CB
    si SWD
    speed 4000
    h // halt-停止
    r // 复位,可以考虑去掉
    erase // 或 erase 0x8002800,去掉也行,但可能会出现error fail address 0x00000000错误提示

    loadfile app.bin 0x8002800 // loadfile app.hex 或 loadfile app.bin 0x8000000
    verifybin app.bin 0x8002800
    r
    go // r go表示reset and run
    q // 退出J-Link命令行工具

Ubuntu 无法更新问题

Ubuntu 无法使用apt update

更改软件源

编辑 /etc/apt/sources.list 文件,将以下内容添加到文件末尾

deb https://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
deb-src https://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse

deb https://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
deb-src https://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse

deb https://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
deb-src https://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse

清除 apt 缓存

sudo apt clean
sudo apt autoclean

尝试更新系统

sudo apt update

显示 ModuleNotFoundError: No module named 'apt_pkg',重新安装 “apt_pkg“ 模块:

sudo apt install --reinstall python3-apt

显示 ERROR:

E: Could not read response to hello message from hook [ ! -f /usr/bin/snap ] || /usr/bin/snap advise-snap --from-apt 2>/dev/null || true: Success

如果问题仍然存在,尝试修复 Python 包:

sudo apt install --fix-broken

然后就更新系统了:

sudo apt update
sudo apt upgrade

执行sudo apt upgrade后显示ERROR:

Errors were encountered while processing:
/tmp/apt-dpkg-install-cQBLJW/626-linux-iot-tools-common_5.4.0-1030.31_all.deb
E: Sub-process /usr/bin/dpkg returned an error code (1)

最后一步,修复损坏的软件包配置:

sudo dpkg --configure -a
sudo apt upgrade

Breo蓝牙启动异常

Breo蓝牙初始化

蓝牙初始化没完成,透传未开启,app就连接蓝牙了。
设置透传参数,开启透传的的状态中增加连接蓝牙接受指令。

心跳包回复超时

现在是接收/刷新设备数据200ms超时,延迟太长还可以缩短。

SPI级联led灯调试小助手

产品名称:1209RGB幻彩雾状
产品型号:XTQ-016B.RGB-2307125-20

SPI级联led灯问题汇总

充电闪灯问题

问题分析

充电中拔掉电源,立即再次插入,会闪一下灯;若等两秒再插入则不会出现,初步判断是此款芯片有锁存功能,会保存到寄存器中,未完全掉电再次插入则会继续执行上次的寄存器数据。

解决方法

充电中拔掉电源,程序不要立即断电,持续两秒反初始化spi灯珠,即给spi寄存器写全灭数据。

呼吸灯闪烁问题

问题分析

程序中呼吸灯会跑偶尔闪烁,影响显示效果。分析发现,代码中有电机的FG检测,此检测开启了输入捕获功能,会持续中断触发,虽然spi灯的驱动是dma发送,但是也是由CPU来调度的,并不能与中断并行。spi呼吸灯效果需要持续发送数据,且时序要求很高,中断会抢占spi的dma发送,打断spi传输数据,导致数据传输出错,造成闪灯效果。

解决方法

1、提高芯片主频,n32l403KB最高主频为64MHz,如果主频提高效果会好一些。
2、spi呼吸灯效果持续发送数据,不能与中断频繁的代码一起使用。

某项目充电保护仍充电

修改代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{-1, 1, PIN_CHARGE_CC_DETECT, PIN_MODE_INPUT},	//input/output switch

static void board_charge_cc_set(bool en)
{
struct pin_status_desc *pin = pin_handle(PIN_CHARGE_CC_DETECT);
if(en)
{
pin->type = PIN_MODE_INPUT;
drv_pin_mode(pin->pin_id,pin->type);
pin->lvl_rt = -1; //重置lvl_rt,以防止output出问题
}else{
pin->type = PIN_MODE_OUTPUT;
drv_pin_mode(pin->pin_id,pin->type);
pin_set_func(pin, PIN_HIGH);
}
}

可变参数函数详解

C语言中的可变参数函数允许您定义函数,其参数个数是不确定的,可以根据具体需求接受可变数量的参数。这在处理不定数量参数的情况下非常有用,比如printfscanf等函数。让我为您详细解释一下可变参数函数的原理和实现。

1. 原理与实现:

  • 可变参数函数的参数列表是从右往左压入堆栈的。假设堆栈中有以下参数:不可变参数1、不可变参数2、…、不可变参数n、可变参数1、可变参数2、…、可变参数n。

  • 为了获取可变参数,我们需要知道每个可变参数的地址。这是通过前一个不可变参数的地址和类型来实现的。

  • ANSI标准提供了三个宏来实现这个过程:

    • va_start(va_list arg_ptr, prev_param): 初始化可变参数列表,将arg_ptr指向第一个可变参数。

    • va_arg(va_list arg_ptr, type): 获取当前参数的值,类型由前面的不可变参数传递。例如,printf中的格式化字符串或者可变参数列表的参数类型和第几个不可变参数的相同。

    • va_end(va_list arg_ptr): 释放资源,结束可变参数列表的访问。

  • 这些宏的实现细节由编译器和标准库提供,我们只需调用它们即可。

2. 可变参数函数:

  • 可变参数函数允许在函数定义中接受不定数量的参数。
  • C语言提供了 stdarg.h 头文件来支持可变参数函数的实现。
  • 下面是一个示例代码,展示了如何实现一个可变参数函数 sum,它接受一个整数参数 count,表示接下来的可变参数的数量:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdarg.h>

int sum(int count, ...) {
int total = 0;
va_list args;
va_start(args, count);
for (int i = 0; i < count; i++) {
int num = va_arg(args, int);
total += num;
}
va_end(args);
return total;
}

int main() {
int result = sum(4, 10, 20, 30, 40);
printf("Sum: %d\n", result);
return 0;
}
  • 在这个示例中,我们定义了一个可变参数函数 sum,它计算传入的整数参数的总和。

3. 可变参数宏:

  • 可变参数宏允许在宏调用中接受可变数量的参数。
  • 在C语言中,可变参数宏使用 __VA_ARGS__ 表示可变参数的部分。
  • 下面是一个示例代码,展示了如何定义一个可变参数宏 PRINT_VALUES,它使用 printf 函数来打印可变数量的值:
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

#define PRINT_VALUES(...) do { \
printf("Values: "); \
printf(__VA_ARGS__); \
printf("\n"); \
} while (0)

int main() {
PRINT_VALUES("%d %s %f", 10, "hello", 3.14);
return 0;
}
  • 在这个示例中,我们定义了一个可变参数宏 PRINT_VALUES,它使用 printf 函数来打印多个值。

4. 实现自己的 printf 函数:

  • printf 函数接受一个格式字符串作为第一个参数,后面是可变数量的参数,用于替换格式字符串中的格式占位符。
  • 以下是一个简化版的示例代码,展示了一个实现类似于 printf 函数的功能的函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>
#include <stdarg.h>

void my_printf(const char* format, ...) {
va_list args;
va_start(args, format);
while (*format != '\0') {
if (*format == '%') {
format++; // 移动到占位符的下一个字符
if (*format == 'd') {
int value = va_arg(args, int);
printf("%d", value);
} else if (*format == 'f') {
double value = va_arg(args, double);
printf("%f", value);
} else if (*format == 's') {
char* value = va_arg(args, char*);
printf("%s", value);
} else if (*format == 'c') {
int value = va_arg(args, int);
printf("%c", value);
} else {
printf("Unsupported format specifier: %c", *format);
}
} else {
printf("%c", *format);
}
format++; // 移动到下一个字符
}
va_end(args);
}

int main() {
int num = 42;
double pi = 3.14159;
char str[] = "Hello, world!";
char ch = 'A';

my_printf("Integer: %d\n", num);
my_printf("Float: %f\n", pi);
my_printf("Float: %s\n", str);
my_printf("Float: %c\n", ch);

return 0;
}

汇编基础

参考文档:汇编语言入门教程

实例分析

了解寄存器和内存模型以后,就可以来看汇编语言到底是什么了。下面是一个简单的程序example.c。

1
2
3
4
5
6
7
int add_a_and_b(int a, int b) {
return a + b;
}

int main() {
return add_a_and_b(2, 3);
}

gcc 将这个程序转成汇编语言。

1
$ gcc -S example.c

example.s经过简化以后,大概是下面的样子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
_add_a_and_b:
push %ebx
mov %eax, [%esp+8]
mov %ebx, [%esp+12]
add %eax, %ebx
pop %ebx
ret

_main:
push 3
push 2
call _add_a_and_b
add %esp, 8
ret

可以看到,原程序的两个函数add_a_and_b和main,对应两个标签_add_a_and_b和_main。每个标签里面是该函数所转成的 CPU 运行流程。


VSCODE文件标签栏多行显示

  • 如果你希望打开多个文件时,所有文件都能显示出来,而不是需要滚动条来查看,可以启用文件标签栏多行显示功能:
    • 按下 Ctrl + Shift + P(或 Command + Shift + P),输入 “Open Workspace Settings“ 并选择。
    • 在搜索框中输入 “Show Tabs“,显示”Workbench › Editor: Show Tabs“选择框,勾选”multiple“设置项。

通过上述方法,你可以解决 VSCode 只显示一个文件,点击另一个文件会覆盖当前文件的问题。希望这些信息对你有所帮助!


扣子智能语音

  • 设置wifi账号密码,
  • 创建智能体,生成space_idbot_IDAccess token,填入menuconfig中。
  • 创建个人Access token的时候要添加APIChat SDK模板。
  • idf.py menuconfigmenuconfig → Component config → ESP-TLS需要修改
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 原始配置
    Choose SSL/TLS library for ESP-TLS (See help for more Info) (mbedTLS) --->
    [*] Use Digital Signature (DS) Peripheral with ESP-TLS
    [ ] Enable client session tickets
    [ ] Enable server session tickets
    [ ] Certificate selection hook
    [ ] ESP-TLS Server: Set minimum Certificate Verification mode to Optional
    [ ] Enable PSK verification
    [*] Allow potentially insecure options
    [*] Skip server certificate verification by default (WARNING: ONLY FOR TESTING PURPOSE, READ HELP)
  • 建议在 menuconfig 里做两件事:
    • 保留 [*] Skip server certificate verification by default(方便测试)
    • 关闭 [*] Use Digital Signature (DS) Peripheral with ESP-TLS(除非 demo 专门用了 DS)
      这样 TLS 会走 mbedTLS 普通模式,直接用 HTTPs 连 Coze,不会因为硬件 DS 或证书校验卡住。

  • 遇到bug如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
E (109608) ESP_COZE_CHAT: Audio data send to queue failed
E (109668) ESP_COZE_CHAT: Audio data send to queue failed
E (109728) ESP_COZE_CHAT: Audio data send to queue failed
E (109803) ESP_COZE_CHAT: Audio data send to queue failed
W (109849) ESP_COZE_CHAT: coze websocket error reason: {"event_type":"error","id":"3c08849d-6518-4ed6-8884-bee1a853a19a","data":{"code":4000,"msg":"The parameter audio is invalid. It should follow the format: wav. or set input_audio via chat.update event. Please review your input."},"detail":{"logid":"021755077983527fdbddc030018071200000000000000325163fb","respond_at":"1755077992134"}}�2<Y

E (109923) ESP_COZE_CHAT: Audio data send to queue failed
E (110118) ESP_COZE_CHAT: Audio data send to queue failed
W (110152) ESP_COZE_CHAT: coze websocket error reason: {"event_type":"error","id":"0c329541-cc0b-44d0-9ef4-b5954edd3dc1","data":{"code":4000,"msg":"The parameter audio is invalid. It should follow the format: wav. or set input_audio via chat.update event. Please review your input."},"detail":{"logid":"021755077983527fdbddc030018071200000000000000325163fb","respond_at":"1755077994608"}}~2<

I (113425) COZE_CHAT_APP: vad start
I (113427) AUDIO_PROCESSOR: VAD_START
W (113531) ESP_COZE_CHAT: coze websocket error reason: {"event_type":"error","id":"e70d4fef-42a4-4d06-8542-4ba09e47c0fe","data":{"code":4000,"msg":"The parameter audio is invalid. It should follow the format: wav. or set input_audio via chat.update event. Please review your input."},"detail":{"logid":"021755077983527fdbddc030018071200000000000000325163fb","respond_at":"1755077997990"}}<2<

W (113657) ESP_COZE_CHAT: coze websocket error reason: {"event_type":"error","id":"966a0434-f467-48f7-8447-3e951fa56b72","data":{"code":4000,"msg":"The parameter audio is invalid. It should follow the format: wav. or set input_audio via chat.update event. Please review your input."},"detail":{"logid":"021755077983527fdbddc030018071200000000000000325163fb","respond_at":"1755077998114"}}2<�

W (113754) ESP_COZE_CHAT: coze websocket error reason: {"event_type":"error","id":"a2d73423-d0d7-4356-80fc-0f5a963ffe45","data":{"code":4000,"msg":"The parameter audio is invalid. It should follow the format: wav. or set input_audio via chat.update event. Please review your input."},"detail":{"logid":"021755077983527fdbddc030018071200000000000000325163fb","respond_at":"1755077998203"}}s2<�

W (114268) ESP_COZE_CHAT: coze websocket error reason: {"event_type":"error","id":"e136e24c-da6a-486e-8ba3-1fe0b9cd6b0d","data":{"code":4000,"msg":"The parameter audio is invalid. It should follow the format: wav. or set input_audio via chat.update event. Please review your input."},"detail":{"logid":"021755077983527fdbddc030018071200000000000000325163fb","respond_at":"1755077998542"}}�2<�

E (114421) esp-tls-mbedtls: write error :-0x6C00
E (114422) transport_base: esp_tls_conn_write error, errno=Socket is not connected
E (114422) transport_ws: Error write header
E (114426) websocket_client: esp_transport_write() returned -1, transport_error=ESP_ERR_MBEDTLS_SSL_WRITE_FAILED, tls_error_code=27648, tls_flags=0, errno=128
E (114440) ESP_COZE_CHAT: WEBSOCKET_EVENT_ERROR
I (114445) websocket_client: Reconnect after 10000 ms
I (114449) ESP_COZE_CHAT: WEBSOCKET_EVENT_DISCONNECTED
E (114454) ESP_COZE_CHAT: Last error reported from esp-tls: 0x8018
E (114460) ESP_COZE_CHAT: Last error reported from tls stack: 0x6c00
E (114468) websocket_client: Websocket client is not connected
E (114473) websocket_client: Websocket client is not connected
E (114478) websocket_client: Websocket client is not connected
  • 我们可以直接在你的 audio_data_read_task() 里做几个改动,让它满足 Coze 的 WAV 格式要求,同时解决队列满导致的 Audio data send to queue failed。
  • 主要改动点:
  1. 将 PCM 封装为 WAV:
    • 16kHz,16bit,mono。
    • 每次发送一小块(如 320~640 字节 PCM)生成对应 WAV chunk。
  2. 增加队列发送重试:
    • 如果 esp_coze_chat_send_audio_data() 失败,等待 5ms 再重试。
    • 循环复用 buffer,避免频繁 malloc/free。
- coze_chat_app.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#define WAV_HEADER_SIZE 44
#define PCM_CHUNK_SIZE 640 // 每次发送的 PCM 数据长度

// WAV 头结构
typedef struct {
char riff[4]; // "RIFF"
uint32_t chunk_size;
char wave[4]; // "WAVE"
char fmt[4]; // "fmt "
uint32_t subchunk1_size; // 16 for PCM
uint16_t audio_format; // PCM = 1
uint16_t num_channels; // 1
uint32_t sample_rate; // 16000
uint32_t byte_rate;
uint16_t block_align;
uint16_t bits_per_sample;
char subchunk2_id[4]; // "data"
uint32_t subchunk2_size; // PCM 数据长度
} wav_header_t;

static void fill_wav_header(wav_header_t *hdr, uint32_t pcm_len)
{
memcpy(hdr->riff, "RIFF", 4);
hdr->chunk_size = 36 + pcm_len;
memcpy(hdr->wave, "WAVE", 4);
memcpy(hdr->fmt, "fmt ", 4);
hdr->subchunk1_size = 16;
hdr->audio_format = 1;
hdr->num_channels = 1;
hdr->sample_rate = 16000;
hdr->bits_per_sample = 16;
hdr->byte_rate = hdr->sample_rate * hdr->num_channels * hdr->bits_per_sample / 8;
hdr->block_align = hdr->num_channels * hdr->bits_per_sample / 8;
memcpy(hdr->subchunk2_id, "data", 4);
hdr->subchunk2_size = pcm_len;
}

static void audio_data_read_task(void *pv)
{
uint8_t *pcm_buf = esp_gmf_oal_calloc(1, PCM_CHUNK_SIZE);
uint8_t *wav_buf = esp_gmf_oal_calloc(1, PCM_CHUNK_SIZE + WAV_HEADER_SIZE);
int ret;

while (true) {
#if defined CONFIG_KEY_PRESS_DIALOG_MODE
xEventGroupWaitBits(coze_chat.data_evt_group, BUTTON_REC_READING, pdFALSE, pdFALSE, portMAX_DELAY);
#endif

ret = audio_recorder_read_data(pcm_buf, PCM_CHUNK_SIZE);
if (ret <= 0) {
vTaskDelay(pdMS_TO_TICKS(5));
continue;
}

// 填充 WAV 头
fill_wav_header((wav_header_t *)wav_buf, ret);
memcpy(wav_buf + WAV_HEADER_SIZE, pcm_buf, ret);

// 发送到 Coze
int send_len = ret + WAV_HEADER_SIZE;
while (esp_coze_chat_send_audio_data(coze_chat.chat, (char *)wav_buf, send_len) != ESP_OK) {
vTaskDelay(pdMS_TO_TICKS(5));
}
}
}