(1)在使用FSMC作为LCD接口时,同时ENABLE了FreeRTOS,发现生成的工程文件,编译之后会出问题。
研究后发现问题出在“FreeRTOSConfig.h”这段代码:
/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
当我使用了FSMC并且ENABLE 了FreeRTOS,“__NVIC_PRIO_BITS”这个宏就会被定义,此时RTOS的宏
“configPRIO_BITS”会被定义为“__NVIC_PRIO_BITS”,这个宏在“stm32f205xx.h”里面:
#define __NVIC_PRIO_BITS 4U /*!< STM32F2XX uses 4 Bits for the Priority Levels */
也就是说“configPRIO_BITS”这个宏被定义为“4U”也就是类型为“unsigned int”的4。
我尝试在汇编函数里,直接将:
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
改为:
mov r0, #4U
编译出错,但如果将“4U”改为“4”是没问题的。
后记:在韦东山第一期加强版视频编写LED程序的视频有讲到,在汇编里面指令需要占据一定的位数,例如:
mov r0, #0x12345678
是不行的,因为“MOV”这个指令本身占据了一定的位数。而“4U“本身为32位,stm32为32位系统,所以”MOV“这个指令码被清零了。具体可以了解下机器码跟汇编。
(2)ADC采集一次信号后不再采集
问题可以参考这个链接:http://www.ing10bbs.com/forum.php?mod=viewthread&tid=194&extra=
除了RCC、SYS,只配置了ADC2的IN8,NVIC Settings打开ADC全局中断:
生成工程的改动很小,增加了一个ADC值获取的回调函数以及一个ADC启动的函数:
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
ADC_VAL = ADC2->DR;
}
ADC初始化里面添加了IT启动:
HAL_ADC_Start_IT(&hadc2);
然后进入仿真发现,ADC采样一次信号后,ADC2->DR寄存器里面的值不变了。研究了老半天,看了几遍参考手册中ADC寄存器的描述,怀疑是EOCS寄存器的问题。所以将:
hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
改为:
hadc2.Init.EOCSelection = ADC_EOC_SEQ_CONV;
很好,ADC连续采样了。但又产生一个新的问题,程序一直重复进入ADC中断,不进入while(1);大循环了。
由此猜测,是不是因为ADC采样转化太快了,导致未处理完一次中断又产生新的中断?
又进行修改:
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
改为:
sConfig.SamplingTime = ADC_SAMPLETIME_56CYCLES;
这次可以正常运行了。其实,最好在启动ADC之后,关闭不必要的中断源,例如只需要看门狗中断:
__HAL_ADC_DISABLE_IT(&hadc2, ADC_IT_EOC|ADC_IT_JEOC|ADC_IT_OVR);
后记:在MDK仿真下观察寄存器,发现关闭中断的指令,关闭的是”ADC_IT_OVR“这个中断源。
这个是”溢出中断“的中断源。当ADC以”ADC_SAMPLETIME_3CYCLES“进行转换时,当前中断尚未处理完便再次触发中断
就会造成中断溢出。所以将ADC转换速率降低,或者关闭”ADC_IT_OVR“程序可以正常运行。
(3)内部FLASH的操作
MCU是F205VCT6
1、写
//STM FLASH写函数
void STMFLASH_Write_NoCheck ( uint32_t WriteAddr, uint16_t * pBuffer, uint16_t NumToWrite )
{
uint16_t i;
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_WRPERR|FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);
for(i=0;i
写之前要去除FLASH的写保护,然后就是写函数中的清楚标志位问题。如执行了
HAL_FLASH_Unlock();
然后立即执行写函数,会发现第一个数据写不进FLASH,但是后续的数据是正常的。试过增加几十毫秒的延时,但是结果依旧错误。如果在写之前先清除错误标志位,则可以正常写入。
2、擦除
//STM FLASH擦除函数
void SectorErase(u32 SectorNum )
{
FLASH_EraseInitTypeDef eraseInit;
u32 error;
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_WRPERR|FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);
eraseInit.Sector = SectorNum;
eraseInit.TypeErase = FLASH_TYPEERASE_SECTORS;
eraseInit.NbSectors = 1;
eraseInit.VoltageRange = FLASH_VOLTAGE_RANGE_3;
if ( HAL_FLASHEx_Erase( &eraseInit, &error ) == HAL_OK )
{
}
HAL_FLASH_Lock();
}
跟写函数一样,解锁FLASH后立即擦除,需要先清除错误标志位。
另外,执行擦除函数,如果是擦除0~3扇区,则可以正常进入仿真(代码在扇区0,擦除扇区0会出现异常,但确实能进入仿真的)界面。
如果擦除4~5扇区,则出现以下问题
不能进入仿真,但是扇区擦除却是成功的。
尝试:
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |
FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
(4)晶振电路出错
仿真,发现进入“ SystemClock_Config();”后,进入“void _Error_Handler(char *file, int line);”
查看电路发现晶振的电容过小,导致外部晶振不起振。
(5)多个中断的使能
例如需要同时启用定时器3的捕获中断以及更新中断:
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_3); //TIM3开始捕获PWM,开启捕获中断
__HAL_TIM_ENABLE_IT(&htim3, TIM_IT_UPDATE); //使能TIM3更新中断
(6)中断中使用定时函数问题
在使用FLASH 模拟USB U盘的时候出现一个问题,我在USB调用的初始化设备函数中进行FLASH的初始化,
且初始化中有使用到延时函数:HAL_Delay(); 这导致了程序卡死在HAL_Delay(); 原因是此延时函数通过系统滴答
定时器实现,而USB初始化设备是在中断中进行的,优先级USB > 系统滴答定时器。
//初始化spi flash
void FLASH_Init(void)
{
FLASH_GPIOInit();
FLASH_SPIInit();
HAL_Delay(5);
}
//此函数在中断中调用
int8_t STORAGE_Init_FS(uint8_t lun)
{
FLASH_Init();
return (USBD_OK);
}
(7)FATFS+FreeRTOS移植失败记录
1、刚开始移植,尝试裸机FATFS,成功。第二次移植,加上Freertos,运行一直卡死在f_open();函数里面,进入仿真
发现最后是卡死在参数检测的语句:
BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
{
BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
configASSERT( pxQueue ); //卡死此处
.....................
这是stm32cubemx种,激活FATFS与FreeRTOS后,会自动启用互斥锁。这个函数是FreeRTOS二值信号量的Release部分函数。刚开始以为是pxQueue没有成功获取地址,仿真跟踪很久发现,地址是对的,在f_open();最后,
LEAVE_FF(dj.fs, res);
dj.fs是临时指针变量,指向我挂载器件的fs。地址是对的,fs的二值信号量指针变量sobj地址是0x20000000,传递进去之后直到:configASSERT(pxQueue);也是正确的,pxQueue = 0x20000000;
于是,怀疑是"0x20000000"这个地址本身出了问题。
2、仿真跟踪FATFS创建二值信号量代码,发现二值信号量在f_mount();中创建:
#if _FS_REENTRANT /* Create sync object for the new volume */
if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR;
#endif
此处,给fs->sobj进行赋值:
int ff_cre_syncobj ( /* TRUE:Function succeeded, FALSE:Could not create due to any error */
BYTE vol, /* Corresponding logical drive being processed */
_SYNC_t *sobj /* Pointer to return the created sync object */
)
{
int ret;
osSemaphoreDef(SEM);
*sobj = osSemaphoreCreate(osSemaphore(SEM), 1);
ret = (*sobj != NULL);
return ret;
}
在这里申请了一个二值信号量,且信号量句柄返回给了fs->sobj。观察此地址,发现并不是:0x20000000!!!
正确地址是:0x20005390
真相出来了,导致二值信号量释放卡死的原因是信号量地址被篡改!那么,地址是在哪里被篡改的呢?
继续仿真寻找,结果发现在这里地址被改成:0x20000000了:
#if _MAX_SS != _MIN_SS /* Get sector size (multiple sector size cfg only) */
if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK
|| SS(fs) < _MIN_SS || SS(fs) > _MAX_SS) return FR_DISK_ERR;
#endif
仿真进去:
#if _USE_IOCTL == 1
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
switch (cmd)
{
case GET_SECTOR_COUNT: *(DWORD*)buff = FLASH_Info.BLK_NBR; break;
case GET_SECTOR_SIZE: *(DWORD*)buff = FLASH_Info.BLK_SIZ; break;
case GET_BLOCK_SIZE: *(DWORD*)buff = 1; break;
}
return RES_OK;
}
#endif /* _USE_IOCTL == 1 */
#define SS(fs) ((fs)->ssize) /* Variable sector size */
仔细观察,fs的指针变量ssize,发现是WORD类型
但是在移植的时候,我用了这条语句:*(DWORD*)buff = FLASH_Info.BLK_NBR;这里用到了DWORD
这样,直接改变了fs结构体中ssize的值,以及它的下一个成员sobj的值。
以上为FATFS结构体成员定义。
3、综上所述,将disk_ioctl();中的“DWORD”改成"WORD",程序成功运行!!!
其实这个"DWORD是"参考了网上别人的代码放进去的。别人没出问题所以没发现这个错误,也有可能是以前的版本确实这个
ssize是“DWORD”类型。以后在移植的时候,搞清楚函数形参的类型是很有必要的。
(8)HAL库串口中断清标志位问题
如下代码,先清除标志位再执行arr[0]++; 数据是正常的,arr[0]每发送一次数据自增1
void USART1_IRQHandler(void)
{
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_TC);
arr[0]++;
}
但是改成如下,arr[0]则每次自增2。也就是说进入了2次串口1中断
void USART1_IRQHandler(void)
{
arr[0]++;
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_TC);
}