【ch32v307】TIM1替代FreeRtos的Systick 发生问题

ch32v307中64位的Systick可以不回绕地一直计数,是系统很好的一个绝对时标,不必用中断而能提供阻塞式的和非阻塞式的延时。公版的Systick延时示例不是一个理想的应用方式,它会复位计数器,只适合单线程的应用,我们可以让它一直计时而不重载,要让它计满得几千年。

为了不浪费Systick,想用TIM1替代FreeRtos示例里的Systick,不过事情很奇怪,重新上电后的第一次运行总是出错,进行一次手工Reset复位后却能正常运行,而使用Systick不会发生。port.c中的代码如下:


#define CFGR0_PPRE2_Set_Mask? ? ? ?((uint32_t)0x00003800)


/* just for wch's systick,don't have mtime */

void vPortSetupTimerInterrupt( void )

{

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = {0};

NVIC_InitTypeDef NVIC_InitStructure = {0};


? ? /* set software is lowest priority */

? ? NVIC_SetPriority(Software_IRQn,0xf0);

? ? NVIC_EnableIRQ(Software_IRQn);


? ? RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);


? ? RCC_ClocksTypeDef RCC_ClocksStatus;

? ? RCC_GetClocksFreq(&RCC_ClocksStatus);


? ? uint32_t tmp = RCC->CFGR0 & CFGR0_PPRE2_Set_Mask;

? ? tmp = tmp >> 11;

? ? if(tmp != 0) RCC_ClocksStatus.PCLK2_Frequency *= 2;


? ? TIM_InternalClockConfig(TIM1);


? ? TIM_TimeBaseStructure.TIM_Prescaler = RCC_ClocksStatus.PCLK2_Frequency / 1000000 - 1;? ?//指定用于划分TIM时钟的预分频值,使其转化为微秒

? ? TIM_TimeBaseStructure.TIM_Period = 1000000 / configTICK_RATE_HZ - 1;? ? ? //指定下次更新事件时要加载到活动自动重新加载寄存器中的周期值

? ? TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;? ? ? ? ?//时钟分频因子

? ? TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;? ? ?//TIM计数模式,向上计数模式

? ? TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);? ? ? ? ? ? ? ? ?//根据指定的参数初始化TIMx的时间基数单位


? ? TIM_Cmd(TIM1, ENABLE);


? ? /* set TIM1 is lowest priority */

//? ? NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;? ? ? ? ? ? ?//TIM1中断

//? ? NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;? ? ? ?//设置抢占优先级1

//? ? NVIC_InitStructure.NVIC_IRQChannelSubPriority = 15;? ? ? ? ? ? ? //设置响应优先级7

//? ? NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;? ? ? ? ? ? ? ? ?//使能通道1中断

//? ? NVIC_Init(&NVIC_InitStructure);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//初始化NVIC


? ? /* set systick is lowest priority */

? ? NVIC_SetPriority(TIM1_UP_IRQn,0xf0);

? ? NVIC_EnableIRQ(TIM1_UP_IRQn);


? ? TIM_ITConfig( TIM1, TIM_IT_Update, ENABLE );

}


/*-----------------------------------------------------------*/

void TIM1_UP_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));

void TIM1_UP_IRQHandler( void )

{

? ? /* TIM Update event */

? ? if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)

? ? {

? ? ? ? GET_INT_SP();

? ? ? ? portDISABLE_INTERRUPTS();

? ? ? ? if( xTaskIncrementTick() != pdFALSE )

? ? ? ? {

? ? ? ? ? ? portYIELD();

? ? ? ? }

? ? ? ? portENABLE_INTERRUPTS();

? ? ? ? FREE_INT_SP();


? ? ? ? TIM_ClearITPendingBit( TIM1, TIM_IT_Update );

? ? }

}


TIM1的中断正常触发后,在执行到TIM1_UP_IRQHandler中的xTaskIncrementTick( )后,跟踪到task.c:


? ? ? ? const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1;


执行后直接触发HardFault_Handler 中断,发生错误,这问题在哪里呢?

临时的解决办法:

uint64_t CmpValueAdd;
/* just for wch's systick,don't have mtime */
void vPortSetupTimerInterrupt( void )
{
    /* set software is lowest priority */
    NVIC_SetPriority(Software_IRQn,0xf0);
    NVIC_EnableIRQ(Software_IRQn);
    /* set systick is lowest priority */
    NVIC_SetPriority(SysTicK_IRQn,0xf0);
    NVIC_EnableIRQ(SysTicK_IRQn);
    SysTick->CTLR= 0;
    SysTick->SR  = 0;
    SysTick->CNT = 0;
    /*  ********* */
    CmpValueAdd = configCPU_CLOCK_HZ/configTICK_RATE_HZ/8;    
    SysTick->CMP = CmpValueAdd;
    SysTick->CTLR= 0x3;
    /*  ********* */
}
/*-----------------------------------------------------------*/
void SysTick_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void SysTick_Handler( void )
{
    GET_INT_SP();
    portDISABLE_INTERRUPTS();
    SysTick->SR=0;
    /*  ********* */
    SysTick->CMP = SysTick->CNT + CmpValueAdd;
    /*  ********* */
    if( xTaskIncrementTick() != pdFALSE )
    {
        portYIELD();
    }
    portENABLE_INTERRUPTS();
    FREE_INT_SP();
}



我们让Systick不回零,始终向上计数,在Debug.c中

int pule_us = 0;
int pule_ms = 0;
/****************************************
      获得系统当前以1毫秒为单位的滴答计数
      类似于 STM32的函数
/****************************************
uint64_t HAL_GetTick()
{
    return SysTick->CNT / pule_ms ;
}
/***************************************
                  初始化
***************************************/
void Delay_Init(void)
{
    pule_us = SystemCoreClock / 8000000;
    pule_ms = (uint16_t)pule_us * 1000;
    SysTick->CTLR = (1 << 5) | 1;  // 计数器初始值更新=0, 启动计数器。 关中断、1/8时基、到比较值后继续、向上计数
}
/***************************************
               阻塞式的微秒延时
***************************************/
void Delay_Us(uint32_t n)
{
uint64_t Cmp = SysTick->CNT + (uint32_t)n * pule_us;
    while(SysTick->CNT < Cmp);
}
/***************************************
               阻塞式的毫秒延时
***************************************/
void Delay_Ms(uint32_t n)
{
uint64_t Cmp = SysTick->CNT + (uint32_t)n * pule_ms;
    while(SysTick->CNT < Cmp);
}



您好,你可以将中断函数声明按照下图方式修改一下试试,当运行RTOS时,中断函数声明建议采用__attribute__((interrupt))声明。

image.png


按您说的进行了修改,问题没有解决。

icon_rar.gifEXAM.rar

使用的沁恒的v307开发板,下载后,USB重新连接上电死机,按下Reset键后正常。


您好,请问你运行的是哪个工程?FreeRTOS那个么,我下载后上电正常运行,两个任务都在工作,LED正常闪烁,并没有出现死机现象,若方便,可以具体描述一下异常的现象和正常运行时的现象,也可通过邮箱和我沟通(lzs@wch.cn)


是AppTest。  Hex文件可能较老(另外它的起始地址在8000,有可能刷了后运行的并不是它),需要重新编译(设置里是生成bin),.ld文件已经把地址改为 0x00000000。

还有,写了bootloader 文件,通过CAN总线刷写APP到FLASH,有时候连续成功,有时候连续不成功。表现是刷写20k的bin到85%左右发现校验错误,即写入的内容与读出的内容不一致。写入过程很简单:收到4字节以WORD方式写入,成功后向主机继续请求下一个WORD,收到后再次写入。

/* Program 32bit data into flash ---------------------------------------------*/
bool Flash_Write32(uint32_t data)
{
    if((flash_ptr > (FLASH_END_ADDRESS - 3)) || (flash_ptr < FLASH_STARTADDRESS) )
    {
        FLASH_Lock();
        return false;
    }
    if(FLASH_ProgramWord(flash_ptr,(uint64_t) data) == FLASH_COMPLETE)
    {
        /* Check the written value */
        if(*(__IO uint32_t*)flash_ptr != data)
        {
            /* Flash content doesn't match source content */
            printf("Write Error at: %p\r\nWRITE: %x\r\nFLASH: %x\r\n", flash_ptr, data, *(__IO uint32_t*)flash_ptr);
            FLASH_Lock();
            return false;
        }
        /* Increment Flash destination address */
        flash_ptr += 4;
    }
    else
    {
        /* Error occurred while writing data into Flash */
        printf("Write Error\r\n", *(uint32_t*)flash_ptr, data);
        FLASH_Lock();
        return false;
    }
    return true;
}

发生错误时4个字节中有一个字节某些位不对,有时候是多0,有时候是多1

2022-05-27_171519.png



我为了构建84MHz的时钟(以前STM项目继承的,搞了一个奇怪的CAN波特率,要有3和7的倍数),只好使用了两级PLL。但是代码库中的RCC_GetClocksFreq()编写的太原始,未考虑PLL2的作用,导致串口时钟不对,打印输出乱码。我已经进行了修改,将获取SYSCLK的过程单独拿出来作为一个弱函数。期待官方后期继续完善。


您好,这边对AppTest例程进行下载测试,并在下载之前删除了原有的bin文件和hex文件,重新生成bin文件下载,下载之后看不到任何现象,串口也没有打印,包括重新上电复位之后,为了问题快速解决,建议你那边测试一下发一个可复现问题的例程过来


Flash写错误的问题找到了,是我少擦除了一部分。在此提醒厂家,希望例子代码更严谨些。

问题的发生就在于例子中擦除块的计算:

    NbrOfPage = (PAGE_WRITE_END_ADDR - PAGE_WRITE_START_ADDR) / FLASH_PAGE_SIZE;

很显然,总擦除字节数不是FLASH_PAGE_SIZE整倍数的时候,一定少擦除了。另外当PAGE_WRITE_START_ADDR并非以FLASH_PAGE_SIZE对齐的时候也一定不正确。

我们开发人员由于对芯片运行机制了解的不一定全面,经常会将厂方例子代码奉为经典,经常不敢轻易改动。而厂方测试代码编写人员经常以测试为目的,考虑的不够全面,默认用户会仔细阅读并修改代码,所以希望厂方提高思想认识,以用户角度思考问题,多替用户着想,简化处理的部分也要写出详细注释,说明需要修改的地方和方法。

不要嫌我多嘴,我是支持国产替代才提意见,如果不想用,干嘛多费唾沫。


您好,感谢你的反馈和建议,这边会和相关工程师反馈的。


只有登录才能回复,可以选择微信账号登录