目录
1)通过NetworkManager连接wifi时显示错误(53)无法找到网络
1)使用stlink在cubeide上进行烧录,烧录失败,提示无法加载elf文件
2)Failed to bind to port 61234/详细信息提示检查st设备电源
10)使用FreeRTOS,程序卡死在HAL_Delay函数(与7)同)
11)使用定时器HAL库函数自己编写一个与freertos兼容的微秒级延时函数
12)CubeMX配置ADC通道(带DMA)HAL库代码(转载)
14)stm32_FreeRTOS_HAL库_串口空闲中断收数据导致卡死
15)Debug发现程序卡死在prvportStartFirstTask()
16)FreeRTOS/xQueuePeek和xQueueReceive的区别
17)region `FLASH' overflowed by 1088 bytes
18)卡死在uint16_t n;for(uint8_t i = 0;i;i++)循环内,一个很傻但值得记录的问题<>
1)函数的形参是字符指针char*,而传参是一个字符串string
2)函数的实现在.cpp文件中,但该函数在.c文件中调用了,在两个文件及头文件中均进行了函数声明
2)容器vector/哈希表容器unordered_set/字典unordered_map
4)优先队列priority_queue(自动按照对头到队尾从大到小顺序排序)
6)动态定义一个由变量决定大小的数组(new/delete/memset)
9)二进制枚举法的一些笔记(__builtin_popcount(n)函数)
---------------------------------------------------------------------------------------------------------------------------------
Linux_bug_log
1)通过NetworkManager连接wifi时显示错误(53)无法找到网络
尝试1:由于连接wpa2/wpa3混合网络找不到正确配置,故修改网络连接配置
//无效
尝试2:重启wifi,自动连接成功,但输入
nmcli dev con
查看网络连接记录时发现由于输入太多次wifi连接命令,导致在网络连接记录中出现多个重名wifi,所以无法连接。
//--有效--//
2)apt-get进程被锁死
E: Could not get lock /var/lib/dpkg/lock-frontend - open (11: Resource temporarily unavailable)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), is another process using it?
尝试1:
进程中存在与apt相关的正在运行的进程:
首先检查是否在运行apt,apt-get相关的进程
ps aux | grep -i apt
若存在则kill掉进程
sudo kill -9 <process id>
或
sudo killall apt apt-get
----均显示,无进程在运行
尝试2:
出现
dpkg: error: dpkg frontend is locked by another process
原因是包管理器没有正确关闭。需要重启计算机或者重新打开终端 输入:
sudo rm /var/lib/dpkg/lock
sudo dpkg --configure -a
-----!解决!------
3)启动roscore显示权限不足
Traceback (most recent call last):
File "/opt/ros/melodic/lib/python2.7/dist-packages/roslaunch/__init__.py", line 290, in main
write_pid_file(options.pid_fn, options.core, options.port)
File "/opt/ros/melodic/lib/python2.7/dist-packages/roslaunch/__init__.py", line 112, in write_pid_file
with open(pid_fn, "w") as f:
IOError: [Errno 13] Permission denied: '/home/cjp/.ros/roscore-11311.pid'
直接对ros根文件夹进行权限赋予
sudo chmod 777 /home/...(name)/.ros/
--------------------------------------------------------------------------------------------------------------------------------
stm32_bug_log
1)使用stlink在cubeide上进行烧录,烧录失败,提示无法加载elf文件
Error message from debugger back end:
Load failed
无法加载elf文件
尝试1:修改reset mode
Run configuration->调试器(Debugger)->Reset Behaviour->Connect under reset
//--无效--//
尝试2:网上有关于芯片是克隆芯片,所以使用ST gdb无法识别的说法,可以在Run Configuration的调试器里修改调试探头为OpenOCD,再修改相关配置文件。
//--过于复杂,未尝试--//
尝试3:更换一块芯片
考虑芯片为克隆芯片/芯片损坏/芯片进行了读写保护
//--解决--//
2)Failed to bind to port 61234/详细信息提示检查st设备电源
尝试1:可能是pc接口给st-link的供电不足以支持板子带着外接设备运行。给板子独立供电
//--无效--//
尝试2:在debug设置中更改GDB端口号,61234->61236
//--仍无效--//
尝试3:修改launch文件的factory_id
<stringAttribute key="process_factory_id" value="org.eclipse.cdt.dsf.gdb.GdbProcessFactory"/>
//--解决--//
3)解决stm32串口接收不定长数据
使用串口空闲中断,开启DMA接收,当DMA接收完成后进入空闲中断,将DMA缓存区数据复制到指定数据接收数组。
串口空闲中断在串口无数据接收的情况下,是不会产生的,产生的条件是当清除空闲标志位后,必须有接收到第一个数据后,才开始触发,一旦接收的数据断流,没有接收到数据,即产生空闲中断。
代码:
//开启串口DMA接收
HAL_UART_Receive_DMA(&huart2, DMA_RX_BUF, DMA_RX_MAX_LEN); // 启动DMA接收
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); // 使能空闲中断
//串口2/数据收发口
void USART2_IRQHandler(void)
{
//串口2DMA空闲中断做数据转移
if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) != RESET) // 空闲中断标记被置位
{
__HAL_UART_CLEAR_IDLEFLAG(&huart2); // 清除中断标记
HAL_UART_DMAStop(&huart2); // 停止DMA接收
// 总数据量减去未接收到的数据量为已经接收到的数据量
esp8266.esp8266_fram_len = DMA_RX_MAX_LEN - __HAL_DMA_GET_COUNTER(huart2.hdmarx);
memcpy(esp8266.esp8266_fram_record,DMA_RX_BUF,esp8266.esp8266_fram_len);
HAL_UART_Receive_DMA(&huart2, DMA_RX_BUF, DMA_RX_MAX_LEN); // 重新启动DMA接收
}
}
在stm32f103c8t6上移植时遇到无法清空标志位的问题,翻阅数据手册发现清空标志位需要读取寄存器,写法如下
//开启串口DMA接收
HAL_UART_Receive_DMA(&huart2, DMA_RX_BUF, DMA_RX_MAX_LEN); // 启动DMA接收
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); // 使能空闲中断
//串口2/数据收发口
void USART2_IRQHandler(void)
{
//串口2DMA空闲中断做数据转移
if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) != RESET) // 空闲中断标记被置位
{
// __HAL_UART_CLEAR_IDLEFLAG(&huart2); // 清除中断标记
USART2->SR;
USART2->DR;
HAL_UART_DMAStop(&huart2); // 停止DMA接收
esp8266.esp8266_fram_len = DMA_RX_MAX_LEN - __HAL_DMA_GET_COUNTER(huart2.hdmarx); // 总数据量减去未接收到的数据量为已经接收到的数据量
memcpy(esp8266.esp8266_fram_record,DMA_RX_BUF,esp8266.esp8266_fram_len);
HAL_UART_Receive_DMA(&huart2, DMA_RX_BUF, DMA_RX_MAX_LEN); // 重新启动DMA接收
}
}
4)串口重定向,实现printf串口输出
添加代码:
//重定向printf至uart3
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int _io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__*/
/******************************************************************
*@brief Retargets the C library printf function to the USART.
*@param None
*@retval None
******************************************************************/
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart3, (uint8_t *)&ch,1,0xFFFF);
return ch;
}
同时勾选Project->Propeties->C/C++ Build->Settings->MCU Settings->Use float....
//--无效--//
新增尝试:在新版的cubeide中还需要增加如下代码改写_write函数才能够使用printf。在usart.c函数中增加如下代码段即可
#include "stdio.h"
// 重定向print start
int __io_putchar(int ch)
{
//具体哪个串口可以更改USART1为其它串口
while ((USART3->SR & 0X40) == 0); //循环发送,直到发送完毕
USART3->DR = (uint8_t) ch;
return ch;
}
//_write函數在syscalls.c中, 使用__weak定義, 所以可以直接在其他文件中定義_write函數
__attribute__((weak)) int _write(int file, char *ptr, int len)
{
int DataIdx;
for (DataIdx = 0; DataIdx < len; DataIdx++)
{
__io_putchar(*ptr++);
}
return len;
}
!!**需要注意,使用printf传输字符串需要在末尾加入\n换行符,否则传输将被阻塞,所有的printf都无法通过串口传输。
5)如何使用寄存器配置GPIO
可以调用库配置GPIO,但在工程中经常遇见寄存器方式配置GPIO,记录该方法的具体配置方式。
stm32的GPIO口每一组有16个口,0-7号口用端口配置低寄存器CRL/8-16号口用端口配置高寄存器CRH。如下图(CRL)可见每个GPIO口由4位二进制数分别控制MODE和CNF,这两个配置参数的定义也在表中列出。
举例,配置GPIOB1为通用推挽输出,最大速度2MHz
GPIOB->CRL&=0xFFFFFF0F;GPIOB->CRL|=8<<4;
解析:CRL控制0-7号口,0xFFFFFF0F转换为二进制后进行与操作可以将GPIOB1的4位置零,或操作的0x00000008转换为二进制再左移4位,即将1号口设置为1000(上下拉输入模式)。
6)JTAG及SWD接口图
7)cubeide中现场表达式类不显示
提示:data variable, no debug info
debug模式设置不对。Project->Propeties->C/C++ Build->Settings->MCU GCC Complier/MCU G++ Complier->Debugging->Debug level->Default -g
//--有效--//类名和定义的类的名字不能重名
8)FreeRTOS创建的任务不调度
FreeRTOS只调度并运行了最高优先级的任务,低优先级任务不运行。
尝试1:调整FreeRTOS堆栈大小,任务堆栈太小或FreeRTOS总堆栈大小过小,在FreeRTOSconfig.h中修改,同时在任务创建函数中修改任务的栈大小
#define configTOTAL_HEAP_SIZE ((size_t)8 * 1024) //此处设置堆栈大小为8K
//--无效--//
尝试2:FreeRTOSConfig.h 配置文件中没有配置SVC异常和PenSV异常的宏,重命名这两个异常的函数实现名字,xPortPendSVHandler 和 vPortSVCHandler 是FreeRTOS源码中port.h文件实现的,但是STM32 原始的异常函数名应该为PendSV_Handler 和 SVC_Handler,所以将其重命名即可。在文件中添加下列代码
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
//--无效--//
尝试3:任务调度及优先级抢占问题
该小节问题出在任务管理写法,由于低优先级任务获取信号量是通过高优先级任务释放信号量来使得任务进入运行状态的,而高优先级中的延时函数使用了HAL_Delay,这使得高优先级不断抢占,导致低优先级任务始终无法运行。
翻阅FreeRTOS手册后发现,将HAL_Delay函数改为vTaskDelay函数,并按照如下写法,可以让高优先级任务进入休眠状态,使得低优先级任务能够运行。
const TickType_t xDelay100ms = pdMS_TO_TICKS( 100UL );
vTaskDelay( xDelay100ms );
时间片划分算法仅作用于同优先级间轮流执行时。
另外一种信号量释放(任务管理)的写法为,开启一个定时器,定时释放信号量,未运行的任务等待信号量时进入阻塞状态,使得其他任务可以运行。该任务管理写法不会遇到此小节描述的问题。
//--解决--//
尝试4:即使是低优先级任务也一直在运行,高优先级任务无法抢占
由于高优先级任务一直在等待低优先级任务的信号量,一直处于阻塞
而低优先级任务一直处于运行状态,即使在低优先级任务中释放了信号量,也不能及时在RTOS的系统调度中进行任务切换,因为高优先级目前处于阻塞态所以不会抢占低优先级的任务。必须在低优先级任务中执行osDelay()使低优先级任务处于阻塞态后,系统调度此时判断高优先级任务获取了信号量处于就绪态才会开启高优先级任务。
9)串口未做连接及任何操作,却不停进入接收中断/串口中断处理函数usart_handler()不进入
在usart.c文件中修改RX口的GPIO为上拉输入
//--解决--//
不进入usart_handler(),需要在cubemx里打开对应usart的global interrupt。
//--解决--//
10)使用FreeRTOS,程序卡死在HAL_Delay函数(与7)同)
由于FreeRTOS占用系统始终SysTick,所以系统定时器需要配置为其他定时器。
调整系统定时器为TIM1后,此时,定时器中断优先级默认为最低15,导致在其它高于此优先级的中断中,使用HAL_Delay()函数时,程序卡死。
在cubemx中调整TIM1的时钟中断优先级为最高(0)。
//--无效--//
解决方法:在FreeRTOS系统中避免使用HAL_Delay,改用osDelay,特别是涉及到任务切换时的延时函数,在一些明确可以占用CPU内存资源进行循环延时的地方依然可以使用HAL_Delay。
原因:HAL_Delay函数中调用了定时器tim结构体,该结构体在TIM初始化时定义了,并在HAL_TIMEBASE_IT文件中定义了具体用法,但读取定时器的值需要的tim结构体在rtos任务文件中没有定义,如果需要调用HAL_Delay函数则需要在任务文件中extern定义HAL_Delay中使用到的全局变量。更直接的解决方法是,在任务文件中直接包含声明了HAL_Delay函数的头文件。。。
实则是一个全局变量与局部变量同名的问题
11)使用定时器HAL库函数自己编写一个与freertos兼容的微秒级延时函数
8MHz晶振,预分频8-1=7,打开定时器如图:
代码:
--------------------------------函数声明-----------------------------------------
//延时函数
#define DLY_TIM_Handle (&htim2) // Timer handle
void delay_us(uint16_t nus);
--------------------------------函数实现-----------------------------------------
//使用定时器2实现微妙延时
void delay_us(uint16_t nus)
{
__HAL_TIM_SET_COUNTER(DLY_TIM_Handle, 0); // set the counter value a 0
__HAL_TIM_ENABLE(DLY_TIM_Handle); // start the counter
// wait for the counter to reach the us input in the parameter
while (__HAL_TIM_GET_COUNTER(DLY_TIM_Handle) < nus);
__HAL_TIM_DISABLE(DLY_TIM_Handle); // stop the counter
}
12)CubeMX配置ADC通道(带DMA)HAL库代码(转载)
基于STM32F103C8T6--通过HAL库实现STM32的ADC+DMA_stm32c8t6的ad转换器_秃突兔兔突秃的博客-CSDN博客
13)ESP01S基于TLS连接MQTT服务器方法
//--esp01s为1MB(8Mbit)flash模组,故只能选择1471号固件
安信可官网选择1030号固件并自行修改ca证书后合并烧录,参考
ESP8266 MQTT AT固件对接国外亚马逊云笔记_+tcmqttconn:fail_安信可科技的博客-CSDN博客
//--但该篇文章基于linux编译,且模组为32Mbitflash,似乎并不相关--//
将ca证书转化为bin文件并烧录到模组对应地址的教程
ESP32 AT指令连接AWS亚马逊云_爱出名的狗腿子的博客-CSDN博客
//--看着靠谱--//
//--软件参考官方数据手册MQTT AT 命令集 - ESP32 - — ESP-AT 用户指南 latest 文档
最后还是选择了1471号固件,不校验证书进行TLS连接,但遇到一进行MQTT连接模组就重启的问题
可能是空间不足导致的重启,但输入AT+CIPSSLSIZE=4096返回相应error
//--基本得出结论,ESP01S无法使用MQTT固件TLS连接MQTT服务器,改换ESP12S模组--//
14)stm32_FreeRTOS_HAL库_串口空闲中断收数据导致卡死
同一个串口发数据使用库函数HAL_UART_Transmit(&huart3,transfer,sizeof(transfer),100);
基于串口空闲中断DMA接收数据,用法同 目录2 。
尝试1:提高串口的优先级
//--无效--//
尝试2:串口中断函数需要使用C编译!!!!串口中断函数的原声明写在了
#ifdef __cplusplus
using namespace std;
void USARTx_IRQHandler(void);
#endif
需要使用C编译
#ifdef __cplusplus
extern "C"{
#endif
void USARTx_IRQHandler(void);
#ifdef __cplusplus
}
#endif
//--注意!!!!若在头文件中对串口中断函数进行了宏定义,仍然需要在宏定义的前面对串口中断函数进行函数声明!!!--//
15)Debug发现程序卡死在prvportStartFirstTask()
基本可以确定是由于某一个任务的任务栈给小了,导致任务调度函数跑飞。
//--有效--//(在STM32F103C8T6项目中修改FreeRTOSConfig.h中的堆栈总大小并合理分配任务堆栈后问题解决)
在STM32F767项目中依旧遇到同类问题,但卡死在WWDG_IRQHandler()
排查后发现,在初始化时声明了定时器TIM2,ioc中未选择自动生成TIM2_IRQHandler(),在文件中未定义TIM2的中断服务函数,(但编译时没有报错)导致硬件在触发定时器中断时找不到中断服务函数,进入看门狗中断,但看门狗中断在ioc中未开启,故程序进入死循环。
定义TIM2中断服务函数后问题解决
//--有效--//
16)FreeRTOS/xQueuePeek和xQueueReceive的区别
xQueuePeek不把队列中的数据删除,xQueueReceive获取后会把队列中的数据删除,其中的“等待时间形参”若队列中没有数据了,则会等待形参设置的时间
17)region `FLASH' overflowed by 1088 bytes
爆栈了,即代码放入闪存flash超出了芯片的容量
解决方法1:修改C++build中编译器GCC Compliler的Optimization项
解决方法2:在烧录时选择release,即优化过的程序
18)卡死在uint16_t n;for(uint8_t i = 0;i<n;i++)循环内,一个很傻但值得记录的问题
由于uint8_t的最大值为128,但n为一个16字节的数,当n大于128时,i不断++会在0-128间循环,导致始终小于n,for循环卡死
19)FreeRTOS任务栈大小的选择
因为程序运行起来实际占用heap的空间计算不准,可以借助freertos的API来准确的得出空闲的heap空间和用的最多时候的空闲值。
xPortGetFreeHeapSize()。这个函数可以获取调用时堆中空闲内存的大小,以字节为单位
xPortGetMinimumEverFreeHeapSize()。此函数返回FreeRTOS应用程序开始运行之后曾经存在的最小的未被分配的存储空间的字节数。它的返回值指示了应用程序离将要耗尽堆空间的接近程度。
堆空间的大小容易估算,但是任务栈空间具体占用多少想计算出来可是复杂很多的。比如任务运行过程中函数调用的圧栈,局部变量等都存在任务的栈空间上,所以我们一开始也只能尽量分配个大一点的值,之后再来调整。
unsigned portBASE_TYPE uxTaskGetStackHighWaterMark(xTaskHandle xTask )。
参数名
xTask 被查询任务的句柄,如果传入 NULL 句柄,则是自己任务的栈剩余
返回值
返回从任务启动栈空间具有的最小剩余量这个值越是接近 0,说明这个任务快溢出了。
20)DMA接收数据不全,128个字节的数据只能往缓存数组里放1个字节
HAL的坑,属于底层bug,项目初始化时mx自动先初始化串口再初始化dma,导致dma时钟后打开串口只能接收到dma的最后一个字节数据。
将dma_init()函数调到uart_init()函数之前即可解决。
修改为
//--解决--//
21)freertos下,基于DMA的串口传输数据不完整
在reertos任务中执行基于DMA的串口传输,发现传出的数据不完整,缺失几位。
可能的原因是任务在未恢复时一直在等待信号量进入运行状态,在刚接收到信号量,任务就执行数据传出指令,可能是任务从阻塞态进入运行态的过程中需要初始化一些系统的参数,这个步骤需要一定的运行时间,在数据传出指令前加入一段延时osDelay即可解决问题。
22)esp32-12s串口回传给stm32的通信问题
1、串口dma接收esp32模块的返回数据时,总有一个长度为1的数据夹在正常数据中间,怀疑是dma把esp32的换行符'\r'当作了一帧数据,因此会把换行符赋给消息处理数组导致数据错误;
2、esp32的'busy...'属于一条消息,如果esp32正在处理上一条指令,而这时又发送了新的指令给esp32,则esp32会返回'busy...',故需要确保每一条指令得到回传的数据才发送下一条指令;
---------------------------------------------------------------------------------------------------------------------------------
C/C++语法_bug_log
1)函数的形参是字符指针char*,而传参是一个字符串string
C++ forbids converting string to char*
可以将string类型强制转换为char*类型,即(char *)"hello world"
2)函数的实现在.cpp文件中,但该函数在.c文件中调用了,在两个文件及头文件中均进行了函数声明
报错:conflicting declaration of 'void RTOS_Create()' with 'C' linkage
需要在.cpp文件中对应函数前加上extern "C" 关键字,声明该函数使用C编译,若使用了该关键字,则在头文件中不需要对该函数进行声明,但在.c的调用文件处需要使用extern对该函数进行声明。
*)常用语法及函数
1)string类/char数组
string.size()/string.length() 返回字符串的长度
string.resize(int num) 改写字符串大小,若比原大则扩容,若比原小则截断后面的字符
string.reverse() 翻转字符串
reverse(string.begin(),string.end()) 若上述报错,使用algorithm库进行翻转
string.find(str(,pos,end)) 查找str在string(的[pos,end])中第一次出现的位置,不存在则返回最后一个值 string.rfind() 为反向查找
string.empty() 判断是否为空
往string中插入及删除的写法:
string.insert(pos,str) 往string的pos处添加字符串str
string.insert(pos,str,c,n) 往string的pos处添加字符串str从c到n的字符
string.insert(pos,n,'ch') 往string的pos处添加n个字符ch
string.append(str) 往string的末尾添加字符串str
string.erase() 全部清空
string.erase(c,n) 删除c到n的字符(从0开始)
遍历string的写法:
string("example");
for(auto x:s)
{
cout<<x<<endl;
}
复制一个相同的字符串的写法:
string s("example");
string s1(s);
大小写转换函数
char c = 'A';
tolower(c);//此时c为'a'
toupper(c);//此时c为'A'
memcpy写法:
char *s = "example";
char d[20];
memcpy(d,s,strlen(s)); //把s赋给d
其他类型数据转string:
//以整型为例
int i = 1;
string s = to_string(i);
//此时s为"1"
2)容器vector/哈希表容器unordered_set/字典unordered_map
字典map用法,unordered_map底层使用哈希桶实现不记录键值顺序:
map<string,int> dict{{"key1",1},{"key2",2}};
dict["key3"] = 3; //创建并赋值
dict.erase("key1"); //删除键为key1的值
//输出
cout<<dict["key1"]<<endl;
//迭代器遍历
//从前向后遍历
auto maplit = dict.begin();
while(maplit != dict.end())
{
cout<<maplit->fist<<':'<<maplit->second<<endl;
maplit++;
}
//从后向前遍历
map<keytype,valuetype>::reverse_iterator it;
for(it = map.rbegin();it != map.rend();it++)
{
cout<<it->first<<':'<<it->second<<endl;
}
unordered_set常用于计算或查找组中是否存在某一元素,(注意!set或unordered_set中的元素只能出现一次,若往内塞入相同的元素,则只会保存一个,即set与map不同点在于只能存互不相同元素)由于其存储方式为哈希表,故检索复杂度大幅降低(哈希表原理见其他笔记)
//往set中添加元素
unordered_set.insert(1); //
//默认拷贝
set<int> set2(set1); //将set1拷贝给set2
//count函数
unordered_set.count(1); //返回unordered_set中1的个数
如果要统计某一个容器中的元素的数目,需要使用map,键值key为元素,值value即为该元素出现的次数。
vector的常用函数:
//初始化一个m*n大小的二维vector
vector<vector<int>> temp(m,vector<int>(n));
//创建一个大小为n的一维vector,且初值为1
vector<int> temp(n,1);
//使用copy函数对vector进行拷贝
vector<int> temp1 = {1,2,3,4,5};
vector<int> temp2(5);
copy(temp1.begin(),temp1.end(),temp2.begin());
//使用assign函数进行容器拷贝
new_vector.assign(old_vector.begin(),old_vector.end());
//使用find函数获取元素索引,需要algorithm库
vector<T> vec;
vector<T>::iterator it;
it = find(vec.begin,vec.end(),element);
int pos = it - vec.begin();
//若查找不到,则it = vec.end();
//vector删除操作
vector.pop_back(); //删除最后一个元素
vector.erase(vector.begin()+pos) //删除pos处的元素
vecotr.erase(vector.begin()+i,vector.begin()+j) //删除i到j的元素
//删除重复元素
auto new_end = unique(vector.begin(),vector.end());
vector.erase(new_end,vector.end());
//vector插入函数(往第i个位置插入int值)
vector.emplace(vector.begin()+i, int)
3)查找最大最小值(vector/数组)
//对数组
int a[10];
a = {...};
//找数值
int maxnum = *max_element(a,a+10);
//找位置
int maxpos = max_element(a,a+10) - a;
//对容器
vector<int> a(10);
a() = {...};
int maxnum = *max_element(a.begin(),a.end());
int maxpos = max_element(a.begin(),a.end()) - a.begin();
4)优先队列priority_queue(自动按照对头到队尾从大到小顺序排序)
priority_queue不同于queue,一般有三个传递函数,一般可以忽略后两个形参,其内部元素(若为二元组则比较first元素大小)从队头到队尾按照从大到小排序
常用函数及用例如下
q.size() //返回队列大小
q.emplace(k) //往队尾加入元素
q.top() //返回队头元素
q.pop() //删除对头元素
priority_queue< pair<int,int> > q;
q.emplace( 1,5 );q.emplace( 2,4 );
cout<<q.top().first<<endl; //输出结果为2
//默认的初始化创建,大堆顶
priority_queue<int> q;
//等同于
priority_queue<int,vector<int>,less<int>> q;
//若要从小到大排序,即小堆顶
priority_queue<int,vector<int>,greater<int>> q;
//完整三传参写法,取三元组数据的第三个值的大值
//先定义比较规则函数,auto返回值,形参auto&表示左值引用,即e1/e2是指针,const表示常变量只可读不可写
auto tupleCmp = [](const auto& e1, const auto& e2)
{
//auto&&表示右值引用
auto&& [x1, y1, d1] = e1;
auto&& [x2, y2, d2] = e2;
return d1 > d2;
};
//优先队列放入三元组,队列实现底层容器为vector容器,比较规则为tupleCmp
priority_queue<tuple<int,int,int> , vector<tuple<int,int,int>> , decltype(tupleCmp)> q(tupleCmp);
//获取优先队列的队头会报decomposition,故必须用auto作复制
auto temp = q.top();
5)单调栈(stack)用于构建最长单调非增或非减数组
单调栈可以在时间复杂度为O(n)情况下,找到某一个元素左侧或右侧第一个大于他的第一个元素,或构建左侧或右侧单调非增或非减数组。
单调栈(单调非递增)代码:
//---------------伪代码
//此处一般需要给数组最后添加结束标志符
//从底往顶非递增,即st[i+1]>=st[i]
stack<int> st;
vector<int> vec;
for (遍历这个数组)
{
if (栈空 || 栈顶元素大于当前比较元素)
{
入栈;
}
else
{
while (栈不为空 && 栈顶元素小于等于当前元素)
{
栈顶元素出栈;
更新结果;
}
当前数据入栈;
}
}
//-----------------------
//-----------------代码模板1
for(int num:vec)
{
if(st.empty() || st.top() > num)
{
st.emplace(num);
}
else
{
while( !st.empty() || st.top() <= num)
{
st.pop();
}
st.emplace(num);
}
}
//-------------------------
//-----------------代码模板2
for(int num:vec)
{
while(!st.empty() && num > st.top()) st.pop();
更新操作
st.emplace(num);
}
//-------------------------
6)动态定义一个由变量决定大小的数组(new/delete/memset)
int *a = new int[n]; //用new开辟一个动态空间定义一个数组指针a,数组大小为n
memset(a,0,n*sizeof(int)); //给数组a初始化
//****
****//
delete[] a; //使用结束后需要删除动态空间
a = NULL; //指针指向空,防止其仍然指向某一个地址
7)lamda表达式
//lambda表达式的标准形式
[capture捕获列表] (params参数) opt函数选项 -> ret返回值类型 {body函数体}
//--------------------
* [ ] 方括号用于向编译器表明当前是一个 lambda 表达式,其不能被省略;
*capture捕获列表: 在方括号内部,可以注明当前 lambda 函数的函数体中可以使用哪些“外部变量”;
* (params参数): 和普通函数的定义一样,lambda 匿名函数也可以接收外部传递的多个参数。和普通函数不同的是,如果不需要传递参数,可以连同 () 小括号一起省略;
*opt函数选项:mutable、noexcept/throw() 可以省略;
*-> ret返回值类型:指明 lambda 匿名函数的返回值类型。值得一提的是,如果 lambda 函数体内只有一个 return 语句,或者该函数返回 void,则编译器可以自行推断出返回值类型,此情况下可以直接省略-> 返回值类型;
*函数体:和普通函数一样,lambda 匿名函数包含的内部代码都放置在函数体中。该函数体内除了可以使用指定传递进来的参数之外,还可以使用指定的外部变量以及全局范围内的所有全局变量。
//----------------------
lambda 表达式无法修改通过复制方式捕获的外部变量。若要修改外部变量,需要通过引用的方式。捕获列表中的语法规则如下
[] 不截取任何变量
[&] 截取外部作用域中所有变量,并作为引用在函数体中使用,表示以引用传递的方式导入所有外部变量;
[=] 截取外部作用域中所有变量,并拷贝一份在函数体中使用,表示以值传递的方式导入所有外部变量;
[=, &foo] 截取外部作用域中所有变量,并拷贝一份在函数体中使用,但是对foo变量使用引用;
[foo] 截取foo变量并且拷贝一份在函数体重使用,同时不截取其他变量;
[this] 截取当前类中的this指针。如果已经使用了&或者=就默认添加此选项。捕获this的目的是可以在lambda中使用当前类的成员函数和成员变量。
//定义lambda表达式的外部变量
int a = 0, b = 1;
//
//error,未截取外部变量,却在函数体内使用外部变量
auto f1 = [] {return a; };
//
//ok,引用捕获所有外部变量,并对a执行自加运算
auto f2 = [&] {return a++; };
//
//error,以复制方式捕获a,无法对a进行修改
auto f4 = [=] { return a++; };
//
//ok,捕获所外部变量,并返回a
auto f3 = [=] { return a; };
//
//error,没有捕获到变量b
auto f5 = [a] { return a + b; };
//
//ok,捕获到a的赋值和b的引用,并对b做自加运行
auto f6 = [a, &b] {return a + (b++); };
//
//ok,拷贝形式捕获到所有的外部变量并对b进行引用捕获,对b做自加运算
auto f7 = [=, &b] {return a + (b++); };
//
8)二进制枚举法的一些笔记(__builtin_popcount(n)函数)
使用二进制枚举法可以很方便地枚举“是否选择该个”的问题。
如现在一行有3列,用1表示选择该列,0表示不选择,则可以用000,001,010,011,100,101,110,111表示所有选择的情况。
若任意选择其中2列,则可以使用__builtin_popcount()编译器内置函数检测一个32位整型数的二进制形式中包含几个1来进行筛选,代码示例:
//掩码表示法
//该行的1的二进制表示
for(int i=0;i<3;i++)
{
mask += row[i] << (3 - 1 - i);
}
//如,该行row = {1,1,1};则mask = 100+010+001 = 111;
//当前的二进制数
int cur = 0;
//循环终止位,左移位数为总列数,因为左移该位后当二进制全1时即进1位
int limit = (1<<3);
//枚举,终止条件为“cur未产生超出总列数的进位”
while((++cur)<limit)
{
//使用__builtin_popcount函数检验1的数量是否等于需要选择的列数
if( __builtin_popcount(cur) != 2 ) continue;
//否则执行相关操作,如检验该行中被选择的列是否为该行全部的1,可以使用与操作完成
if( mask & cur == mask ) break;
}
9)模运算
(a+b)%m=(r1+r2)%m=(a%m+b%m)%m
在一个超长字符串,如123456789判断能否被数字m=3整除,可以从1开始遍历,temp初始为0,每一位数字为d,更新计算量为
temp = (temp*10 + d) % m,对1234而言,1234 % m = (123 % m * 10 + 4) % m
10)二分查找的库函数
for(const auto& v : vec)
{
//用一个迭代器找target在v中的位置,若没找到返回v.end()
auto it = lower_bound(v.begin(),v.end(),target);
//若在v中找到了target返回true
if(it != v.end() && *it == target) return true;
}
*)输入函数
//将输入的一行字符串赋给s,遇到换行符/n结束
string s
getline(cin,s);
//将输入的一行字符赋给s,遇到','结束
string city,state;
getline(cin,city,',');
getline(cin,state);
输入Beijing,China
则city = Beijing, state = China;
--------------------------------------------------------------------------------------------------------------------------------
硬件设计bug_log
1)电路板啸叫且负载电流达不到要求值
1、负载电流过大。DC/DC芯片内部有一个限流保护电路,当负载超过IC内部的开关MOS管的最大电流时,限流电路检测电路就会调整芯片内部的占空比,或者停止工作,直到检测负载电流在标准范围内时,再重新启动正常的工作开关。如果从停止开关到重启开关的时间周期正好是20~20kHz的频率范围内,就会导致电感的的震动频率被人听到。
2、负载电压过高。如果负载为空或轻负载,就会触发过压保护,停止开关,如果电压降下来,就会重启工作,从停止到重启的时间容易落在音频范围内。
3、电路产生自激。电源电路在设计时,稳定性不够,当负载产生变化时,容易自激,当自激的频率落在音频范围内,将引起啸叫。
4、穿越频率落在20~20kHz的频率范围内。穿越频率通常为开关频率的1/5至1/20,设计时需要撇开音频的频率范围,否则,将无法解决啸叫问题。
5、开关切换过冲较大。DC/DC上管和下管切换的过程中,由于寄生电容、寄生电感的存在,将在上管开启的瞬间产生过冲,且幅度较大,导致在电感上施加周期性的过冲电压,从而引起啸叫声变大。
电感啸叫)
由于负载不稳定、轻载过载或者感值容值不合适等因素,开关电源自我调节,不同芯片有不同处理方式:有的降低频率,有的周期性丢脉冲即表现为间歇工作;导致电感的phase不稳定,输出开关电流的频率落入音频范围,或者周期性方波群的周期频率落入音频范围,形成啸叫。
解决方法:
1- 找到啸叫电感
通过观察,排除等方法,找到具体啸叫的电感和对应电路模块;
2- 查询DCDC手册
重点查看DCDC功率与负载是否匹配;
DCDC是否具有节能模式,节能模式一般分为DCM(Discontinuous Conduction Mode)非连续导通模式和CCM(Continuous Conduction Mode)连续导通模式。
如果有需要将IC的模式设置为PWM only,然后去看电感phase端的波形。
3- 检查电路和参数
计算感值和容值参数是否合适,电路是否与模式匹配;
提高输入开关电流的频率;频率提高,电感和电容的参数可适当降低;
当输入输出差值较大,周期性丢弃脉冲的芯片来说,可以降低频率;
反馈电阻并联电容,调整零点极点;
确定电感的饱和电流和热电流足够应付负载功率,超过的话电感值会下降;
降低负载电流或者更换大功率电源;
稳定占空比,确保控制环路小信号不被干扰,占空比在正常范围;
4- 改善电感工艺
选择带屏蔽,浸漆功率电感。
2)如何设计DC-DC电路满足负载电流要求
首先考虑DC-DC芯片输入输出的功率是否匹配,如通用Typec供电5V2A10W电源输入,若升压至12V输出,最高也就800mA电流,即使升压芯片的给定最大输出电流较大,但功率不满足,此时需要考虑更换输入电源,如使用C数较大的动力型锂电池,C数表示电池最大放电电流为C*电池容量。
--------------------------------------------------------------------------------------------------------------------------------
杂项bug_log
1)打开网页显示隐私设置错误,证书失效
网站证书失效,需要重新安装证书
点击“证书无效”下载网站发布的证书,分别打开下载的文件及点击浏览器的”设置->安全->管理证书->导入“导入到”受信任的根证书颁布机构“
//--无效--//
2)改用Edge浏览器
//--有效--//
另发现一个现象,chrome浏览器收藏夹中打开该网址,能进去,使用百度搜索搜索该网站进不去,可能是因为百度那边没更新对应域名