当前位置:   article > 正文

STM32 踩坑笔记_嵌入式读stm地址的时候错误

嵌入式读stm地址的时候错误

1.使用rtthread时,bootloader跳转后就跳转到错误函数中,无法正常运行,debug发现,问题是时钟再次被配置引起,解决方案:将app的时钟配置函数注释,只在bootloader中调用一次时钟配置。

2.使用C++打印异常信息只能打印出 std::exception,无法打印具体异常,解决方法:使用引用即可正常打印

  1. catch(exception& e){
  2. const char* errorMessage = e.what();
  3. printf("初始化发生异常:%s\n",errorMessage);
  4. }

3.异常出现std::bad_alloc,表示new 操作错误,堆栈不够大,调整Heap_Size解决

4.使用bootloader下载程序到内部flash后,总是校验不通过,发现有的扇区的数据不正确,最终问题原因:#define FLASH_MASK                        0x7FF  这个值从0x3ff改为0x7ff后下载正常

5.串口中断溢出解决:

在错误函数中调用SR 和DR 清除中断标志,并且重新开启中断

  1. void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
  2. {
  3. __HAL_UART_CLEAR_IDLEFLAG(huart);
  4. rx=huart->Instance->SR;
  5. rx=huart->Instance->DR;
  6. HAL_UART_Receive_IT(huart, &rx,1);
  7. }

6.STM32 内部flash 读写错误:

首先需要擦除扇区才能进行写入,否则会写入失败返回HAL_ERROR,并且存放的位置必须不能跨扇区存放,否则就要多擦除一个扇区。

  1. uint32_t PageError = 0;
  2. FLASH_EraseInitTypeDef pEraseInit;
  3. pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
  4. pEraseInit.PageAddress = LOSS_ADDR & ~(FLASH_PAGE_SIZE - 1);
  5. pEraseInit.NbPages = 1;
  6. HAL_FLASHEx_Erase(&pEraseInit, &PageError);

首先擦除,然后进行写入,PageAddress直接填入地址,不用计算扇区!

  1. if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,LOSS_ADDR+i, data)!=HAL_OK){
  2. printf("写入失败add:%x",LOSS_ADDR+i);
  3. //return 1;
  4. };

读取:

  1. HAL_FLASH_Unlock();
  2. memcpy(buffer,(uint8_t*)ADDR,size);
  3. HAL_FLASH_Lock();

7.串口接收一段时间后就无法接收到数据了,解决方法:

在串口错误中断函数里清除中断标志后,加入重新开启HAL_UART_Receive_IT(huart, &rx,1);即可

8.编码器测速脉冲数量和实际不同

问题解决:

  1. htim2.Instance = TIM2;
  2. htim2.Init.Prescaler = 3;
  3. htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  4. htim2.Init.Period = 0xFFFF;
  5. htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  6. htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  7. sConfig.EncoderMode = TIM_ENCODERMODE_TI12;//正交编码器模式 T1T2
  8. sConfig.IC1Polarity = TIM_ICPOLARITY_BOTHEDGE;
  9. sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  10. sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
  11. sConfig.IC1Filter = 0;
  12. sConfig.IC2Polarity = TIM_ICPOLARITY_BOTHEDGE;
  13. sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  14. sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
  15. sConfig.IC2Filter = 0;
  16. if (HAL_TIM_Encoder_Init(&htim2, &sConfig) != HAL_OK)
  17. {
  18. Error_Handler();
  19. }

其中Prescaler 是设为0时获得的数量除以实际的数量比值,比如0是256,实际是64,那么Prescaler设为3。这个值是指脉冲数量的分频而不是时钟的分频!

Period 为最大计数值,而不是触发脉冲的装填值

IC1Polarity  和IC2Polarity 要设置为TIM_ICPOLARITY_BOTHEDGE 脉冲数量更准

9.配置完唤醒脚之后,休眠立即自动唤醒:

解决方法:添加最后两行代码解决

  1. __HAL_RCC_GPIOC_CLK_ENABLE();
  2. GPIO_InitTypeDef GPIO_InitStruct = {0};
  3. GPIO_InitStruct.Pin = GPIO_PIN_5;
  4. GPIO_InitStruct.Pull = GPIO_PULLUP;
  5. GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  6. HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  7. HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN5);//按键唤醒
  8. GPIO_InitStruct.Pin = GPIO_PIN_13;
  9. GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  10. GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
  11. HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  12. HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN2);//设备唤醒
  13. //响应3 ,抢占0
  14. __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
  15. // //暂停滴答时钟,防止通过滴答时钟中断唤醒
  16. HAL_SuspendTick();

10.SSD1306 OLED硬件SPI无法驱动:

解决方法:spi配置中,必须设置以下几项:

 hspi3.Init.Direction = SPI_DIRECTION_2LINES;//之前无法驱动就是因为设置成了1LINES

hspi3.Init.DataSize = SPI_DATASIZE_8BIT;//数据位是8位

hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;//波特率可以设置高一些,设置2,4,8等都可以驱动

11.串口DMA无法收发:

解决方法:DMA初始化中,忘了加入响应DMA通道的中断,所以无法收发数据,必须加上才行

  1. HAL_NVIC_SetPriority(DMA2_Channel6_IRQn, 0, 0);
  2. HAL_NVIC_EnableIRQ(DMA2_Channel6_IRQn);
  3. /* DMA2_Channel2_IRQn interrupt configuration */
  4. HAL_NVIC_SetPriority(DMA2_Channel7_IRQn, 0, 0);
  5. HAL_NVIC_EnableIRQ(DMA2_Channel7_IRQn);

12.nfc通信中途断开:

解决方法:将26改成27增加连接等待延迟

  1. /*! Maximum Frame Waiting Time = ((256 * 16/fc) * 2^FWImax) = ((256*16/fc)*2^14) = (67108864)/fc = 2^26 (1/fc) */
  2. #define RFAL_ISODEP_MAX_FWT ((uint32_t)1U<<27)

13.程序运行死机,发现串口中断一直进入,断点无法命中。

解决方法:在初始化的时候错误启用了串口空闲中断,必须关闭它才可以完成初始化

__HAL_UART_ENABLE_IT(&LOAD_UART, UART_IT_IDLE);

14.DMA接收数据不进入rx完成中断函数:

这是由于DMA接收必须数量达到调用时填入的数量才会触发中断。要采用空闲中断触发的方式才能进入中断。

15.用C++创建智能指针后,出现崩溃

  1. //错误代码,没有保存unique对象会导致其创建后立即销毁,从而导致model是一个空指针,继续使用从而引起异常
  2. auto model = std::make_unique<PersonModel>().get();

16.使用指针发送数据时,发送的数据不正确

解决方法:这里在if里面再次定义了buffer,而send函数是在if外面,于是使用了外面定义的空指针,从而导致了数据不正常。正确写法应该是将if里的类型去掉直接写 buffer = get_buffer();

  1. uint8_t* buffer;
  2. if(...){
  3. uint8_t* buffer = get_buffer();
  4. }
  5. send_buffer(buffer,1024);

17.DMA串口发送的时候,数据不正常,原因:DMA发送函数只是把数组指针告诉了dma发送器,而函数退出后,数组被释放,数据也就不正常了,因此需要把发送数组设为全局或等待发送完成才能退出函数。

18.使用L431擦除页的时候,擦除不完整导致数据写入错误,校验不通过

解决方法:擦除页面只需要计算页地址和结束地址,而不需要一页一页的擦除,支持多页擦除,因此可以使用多页擦除的方式

  1. //将原来的一页擦除方式改成由尾页-首页得到页面的总数量,然后将页数赋值给NbPages,就可以实现一次擦除多个页,原来的一页擦除方式需要计算页地址和擦除次数,因此是错误的
  2. // do
  3. {
  4. //BSP_WDGReboot();
  5. EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
  6. EraseInitStruct.Banks = bank;
  7. EraseInitStruct.Page = FirstPage;
  8. EraseInitStruct.NbPages = GetPage(start+size) - FirstPage + 1;
  9. //EraseInitStruct.NbPages = 1;
  10. if(HAL_FLASHEx_Erase(&EraseInitStruct, &page_error) != HAL_OK)
  11. result = HAL_ERROR;
  12. // FirstPage = GetPage(start);
  13. // page_size = FLASH_PAGE_SIZE;
  14. // start += page_size;
  15. // size = (size>=page_size)?(size-page_size):0;
  16. HAL_Delay(1);
  17. }
  18. // while(size);

19.给人脸模块发送串口数据时,经常发送失败,并且人脸模块发生阻塞,无法再收到数据:

经过分析,人脸模块最大缓冲只有4k,当错误数据超过4k后就无法收到任何数据了,那么就是数据发送有问题,怀疑是数据间隔过大。解决方法:将中断禁用然后发送数据就不会被干扰了

  1. __disable_irq();
  2. HAL_UART_Transmit(&huart3, txbuffer, txlen, 0xffff);
  3. __enable_irq();

20.使用内部硬件看门狗休眠后立即被唤醒

解决方法:内部看门狗只有复位才能关闭,因此需要定义noinit变量,复位重启后判断该变量直接进入休眠

21.单片机在开机时无法进入休眠:

  1. void enter_stop(){
  2. //修改noinit变量
  3. (*(uint32_t*)0x2000FFFC)=25;
  4. ...
  5. //然后重启
  6. }

解决方法:并且休眠函数必须在HAL_init函数调用之后调用,如果在HAL_Init()之前调用,则无法唤醒

  1. HAL_Init();
  2. if((*(uint32_t*)0x2000FFFC)==25){
  3. (*(uint32_t*)0x2000FFFC)=0;
  4. awak_pin_init();
  5. __disable_irq();
  6. HAL_PWR_EnterSTANDBYMode();
  7. }
  8. MX_GPIO_Init();

22.定义的noinit地址变量总是被修改:

解决方法:在应用中将地址变量赋值后重启进入到bootloader内部,此时执行了HAL_Init();而这个函数可能会在最结尾的内存地址上连续写入,将会覆盖掉0x2000FFF8以后的内容,因此导致了变量无效。booltloader中可以将内存大小进一步减少:限制其内存范围

这样bootloader启动时就不会将0x2000FFF8的内容覆盖。

23.使用LVGL时发生硬件错误,原因是带数据的事件发送速度过快,LVGL分配内存释放速度跟不上,导致分配出错。

解决方法:

发送事件之间增加10ms延迟,仅仅能够降低出错的概率,并不能完全解决问题

24.bootloader每次启动时随机发生死机,但是将串口线插上就不会发生死机,且不同板子死机概率不同,代码程序都完全相同。

解决方法:在串口1中发现串口定义没有使用上拉电阻,怀疑是串口受到了干扰,并且启动到串口开启中断时就会导致死机。将串口上拉电阻加上就解决了

  1. GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
  2. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  3. GPIO_InitStruct.Pull = GPIO_PULLUP;
  4. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  5. GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
  6. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

25.使用lvgl时在显示画面文字时偶尔发生死机,发生段错误

解决方法:经过排查,lvgl在设置文本时会分配内存,并且lv_task_handle和lv_tick是在中断中调用的,LVGL默认线程不安全,因此要增加线程锁,如果是在rtos环境中使用多线程修改控件内容,那么该操作也必须增加线程锁,如果只是在lvgl内部事件回调中修改控件,那么就是执行在lv_task_handle函数里,可用不加锁

  1. lv_lock();
  2. lv_tick_inc(1);//这两个函数在中断中调用会发生资源竞争
  3. lv_unlock();
  4. lv_lock();
  5. lv_task_handler();//这两个函数在中断中调用会发生资源竞争
  6. lv_unlock();
  7. void lv_lock(){
  8. if(lv_mutex==1)
  9. return;
  10. lv_mutex=1;
  11. }
  12. void lv_unlock(){
  13. lv_mutex=0;
  14. }

26.系统随机发生死机现象,发现是内存泄漏导致

	//printf("写入人脸特征:%s\n",Hex2Str(face_buffer,1024).c_str());

打印这段代码也发生内存泄漏,分析原因,可能是此函数内创建了过多的字符串对象,而无法申请到内存导致内存泄漏,并且函数使用c_str,可能导致返回的string对象提前释放内存,而导致%s访问到越界的字符串,从而导致卡死。

解决方法:使用返回string的字符串要先保存对象,再访问

auto feature_str=Hex2Str(feature_buffer.get(),1024);

并且将Hex2Str内改为使用append,此函数可以在原来对象上增加字符串,而不是频繁创建string对象

  1. string Hex2Str(uint8_t *sSrc, int nSrcLen )
  2. {
  3. int i;
  4. string sDest="";
  5. char szTmp[3];
  6. for( i = 0; i < nSrcLen; i++ )
  7. {
  8. sprintf( szTmp, "%02X",sSrc[i] );
  9. sDest.append(string(szTmp));//此处改为使用append ,而不是使用sDest+=string(szTmp)
  10. }
  11. //printf("fece:%s\n",sDest.c_str());
  12. return sDest;
  13. }

27.使用LVGL随机发生死机现象,发现是lv_handle_task在中断中调用发生了重入,将其放在循环内调用可以解决,或者在中断中增加停止中断的代码

  1. void TIM7_IRQHandler(void)
  2. {
  3. if(lv_task_run==false){
  4. lv_task_run=true;
  5. HAL_TIM_Base_Stop_IT(&htim7);
  6. lv_task_handler();
  7. HAL_TIM_Base_Start_IT(&htim7);
  8. HAL_TIM_IRQHandler(&htim7);
  9. lv_task_run=false;
  10. }
  11. }

28.串口发送数据发生死机,每次都是死在 HAL_UART_Transmit (&hlpuart1,(uint8_t*)&pack,sizeof(RWTdata_system),0xffff);

后面将HAL_UART_Transmit 改成HAL_UART_Transmit_DMA问题解决,问题根本原因是串口上焊接了电容,导致发送时产生了同时收发电压,引起串口错误

29.LVGL在定时器中调用lv_task_handle 后,在循环中使用lv_event_send,偶尔出现死机,并且发送事件频率越高,或者是定时器越慢死机概率越大,说明lv_task_handle执行越快,事件发送的可能就越低,而被打断的机会就越少,因此死机频率降低。

解决方法:定时器中要禁用定时器中断,防止lv_task_handle重入,第二使用mutex防止lv_event_send执行中被打断直接执行lv_task_handle,引起LVGL内部混乱,LVGL的各个函数只能顺序执行,不能交叉执行,一个函数必须运行完成才能执行下一个函数。

  1. //将发送事件函数封装加入互斥量
  2. void disp_event_send(lv_obj_t * obj, lv_event_code_t event_code, void * param){
  3. while(lv_task_run==true);
  4. lv_task_run=true;
  5. lv_event_send(obj,event_code,param);
  6. lv_task_run=false;
  7. }
  1. void TIM7_IRQHandler(void)
  2. {
  3. //加入互斥量
  4. if(lv_task_run==false){
  5. lv_task_run=true;
  6. HAL_TIM_Base_Stop_IT(&htim7);
  7. lv_task_handler();
  8. HAL_TIM_Base_Start_IT(&htim7);
  9. lv_task_run=false;
  10. }
  11. HAL_TIM_IRQHandler(&htim7);
  12. }

30.ABOV G123检测发送器缓冲寄存器死循环,解决方法:

UART1->LST_b.THRE 必须加上括号再进行!判断,如果直接写成

  1. while( !UART1->LSR_b.THRE);//在某些编译器下会造成无法获取到寄存器的值
  2. while( !(UART1->LSR_b.THRE));//必须加上括号

  1. //重定向fputc函数
  2. int fputc(int ch, FILE *f) //两个标准参数
  3. {
  4. //将要发送的数据通过串口1发送出来(可以用电脑上的串口调试软件接收)
  5. UART1->THR=ch;
  6. //等待发送是否完成
  7. while( !(UART1->LSR_b.THRE));
  8. //while( !( UART1->LSR & UARTn_LSR_THRE ) );
  9. return ch;
  10. }

31.关闭网口模块电源后,其串口发生中断并且无法退出导致程序死机在中断里,解决方法:

增加deinit串口的代码,可以将串口关闭

  1. #define bsp_set_wk_off() {\
  2. HAL_GPIO_WritePin(IO_INWK_GPIO_Port, IO_INWK_Pin, GPIO_PIN_RESET);\
  3. HAL_UART_DeInit(&huart6);\
  4. }
  5. #define bsp_set_wk_on() {\
  6. HAL_GPIO_WritePin(IO_INWK_GPIO_Port, IO_INWK_Pin, GPIO_PIN_SET);\
  7. HAL_UART_Init(&huart6);\
  8. }

32.使用 rtthread +网口串口发送数据后接收时,程序发生死机,屏蔽主函数循环后正常,解决方法:由于rt_event_send且没有接收事件的处理函数导致bug,取消事件发送解决

  1. void UART6_DMAReceved(uint16_t size)
  2. {
  3. usUART6_Length = size;
  4. HAL_UART_Receive_DMA(&huart6, (uint8_t*)acUART6_char, FINSH_BUF_SIZE);
  5. bsp_recive_data(acUART6_char,usUART6_Length);
  6. // rt_event_send(&event_task, TASK_EVENT_UART6);
  7. }

33.使用rtthread-studio 使用cubemax更新后,sdio不能使用了

解决方法:使用cubemax更新后文件被排除编译了,打开文件属性取消勾选将资源从构建中排除

34.接收数据时只要不加入printf("rx:%s  dma:%d\n",aucTCP232_Buffer,dma_num);buffer里的值就不正确,导致无法接收到网口数据

解决方法:是程序中有数组越界导致增加清除信号量代码

  1. bsp_uart_send(at,length);
  2. ret = rt_sem_take(&sem_uart, 1000);
  3. printf("rx:%s dma:%d\n",aucTCP232_Buffer,dma_num);
  4. if(usTCP232_Size && bTCP232_Echo)
  5. {
  6. aucTCP232_Buffer[usTCP232_Size] = 0;
  7. APP_TRACE("tcp232 rx: \"%s\"\n\n", aucTCP232_Buffer);
  8. }
  1. rt_sem_control(&sem_uart, RT_IPC_CMD_RESET, RT_NULL);
  2. bsp_uart_send(at,length);

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号