CCP operation bug detection (just share experience)

Go To Last Post
5 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is a function to set TCD0 of ATTINY814:

void ledBreathingBegin(EbreathMode bm){

	uint8_t cmpEN = TCD0.FAULTCTRL & ~(TCD_CMPAEN_bm | TCD_CMPBEN_bm); //先全部禁止

	breathSign = 0;

	switch(bm){
		case ebmNone: break;
		case ebmBlue: breathSign = blueBP; break;
		case ebmRed:  breathSign = redBP;  break;
		case ebmBlueAndRed: breathSign = (blueBP | redBP);
	}

	if(breathSign & blueBP){
		cmpbsetInc = false;
		cmpbset = 0;
		TCD0.CMPBSET = 2000;//cmpValue[cmpbset];
		cmpEN |= TCD_CMPBEN_bm;
	}

	if(breathSign & redBP){
		cmpasetInc = false;
		cmpaset = 0;
		TCD0.CMPASET = 2000;//cmpValue[cmpaset];
		cmpEN |= TCD_CMPAEN_bm;
	}

	while ((TCD0.STATUS & TCD_ENRDY_bm) == 0); //关闭TCD
	TCD0.CTRLA &= ~TCD_ENABLE_bm;

	CPU_CCP = CCP_IOREG_gc;  //CCP保护
	TCD0.FAULTCTRL = cmpEN;

	while ((TCD0.STATUS & TCD_ENRDY_bm) == 0); //启动TCD
	TCD0.CTRLA |= TCD_ENABLE_bm;

	RTC_INT_ENABLE;  //启动 RTC
}

TCD0.FAULTCTRL will not be set as thought, it will keep no change.

If you debug in step, it will be right: changed as thought.

Bug exists even you add delay codes after 

TCD0.FAULTCTRL = cmpEN;

If you don't think of time limit of CCP_IOREG_gc, you will never find the reason.

The bug will be triggered randomly,though for my case it triggered nearly every time.

 

【wrong,see followed post】The reason is : before execute "TCD0.FAULTCTRL = cmpEN;",interrupt happened ,time over for the CCP operation.

You have to forbidden interrupt to get rid of this bug:

void ledBreathingBegin(EbreathMode bm){

	uint8_t cmpEN = TCD0.FAULTCTRL & ~(TCD_CMPAEN_bm | TCD_CMPBEN_bm); //先全部禁止

	breathSign = 0;

	switch(bm){
		case ebmNone: break;
		case ebmBlue: breathSign = blueBP; break;
		case ebmRed:  breathSign = redBP;  break;
		case ebmBlueAndRed: breathSign = (blueBP | redBP);
	}

	if(breathSign & blueBP){
		cmpbsetInc = false;
		cmpbset = 0;
		TCD0.CMPBSET = 2000;//cmpValue[cmpbset];
		cmpEN |= TCD_CMPBEN_bm;
	}

	if(breathSign & redBP){
		cmpasetInc = false;
		cmpaset = 0;
		TCD0.CMPASET = 2000;//cmpValue[cmpaset];
		cmpEN |= TCD_CMPAEN_bm;
	}

	while ((TCD0.STATUS & TCD_ENRDY_bm) == 0); //关闭TCD
	TCD0.CTRLA &= ~TCD_ENABLE_bm;

	uint8_t oldSREG = SREG;
	cli();
	CPU_CCP = CCP_IOREG_gc;  //CCP保护
	TCD0.FAULTCTRL = cmpEN;
	SREG = oldSREG;

	while ((TCD0.STATUS & TCD_ENRDY_bm) == 0); //启动TCD
	TCD0.CTRLA |= TCD_ENABLE_bm;

	RTC_INT_ENABLE;  //启动 RTC
}

 So,it is safer to do in the same way for all CCP operation.

This topic has a solution.
Last Edited: Mon. Jan 24, 2022 - 08:29 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The CCP register summary for avr0/1/similar says interrupts are disabled for the 4 cycles after writing to CCP so there is no need to disable interrupts. If that is not the case, then all avr0/1 users are in trouble because I doubt anyone is disabling interrupts for any CCP usage (because no reason to, says the datasheet).

 

I don't use TCD, but it appears to me that you are not checking the enable bit before writing to the FAULTCTRL register. You are checking the enable ready bit, disabling tcd so you can then change a 'static' register, then jumping right into trying to change a 'static' register even though the enable bit still may not have synced up yet. Most likely all you are doing with your added code is delaying long enough for the enable bit to sync up, and it then looks like that is the solution. Wait for the enable bit in CTRLA to clear before attempting to write to FAULTCTRL.

Last Edited: Thu. Jan 13, 2022 - 05:40 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are right!

 

I add the ENRDY check before CCP operation, it is OK now.

So it means that TCD does not turn off yet before change FAULTCTRL.

 

I don't remember that CCP will disable interrupt itself.

 

Thank you.

 

right code:

void ledBreathingBegin(EbreathMode bm){

	uint8_t cmpEN = TCD0.FAULTCTRL & ~(TCD_CMPAEN_bm | TCD_CMPBEN_bm); //先全部禁止

	breathSign = 0;

	switch(bm){
		case ebmNone: break;
		case ebmBlue: breathSign = blueBP; break;
		case ebmRed:  breathSign = redBP;  break;
		case ebmBlueAndRed: breathSign = (blueBP | redBP);
	}

	if(breathSign & blueBP){
		cmpbsetInc = false;
		cmpbset = 0;
		TCD0.CMPBSET = 2000;//cmpValue[cmpbset];
		cmpEN |= TCD_CMPBEN_bm;
	}

	if(breathSign & redBP){
		cmpasetInc = false;
		cmpaset = 0;
		TCD0.CMPASET = 2000;//cmpValue[cmpaset];
		cmpEN |= TCD_CMPAEN_bm;
	}

	while ((TCD0.STATUS & TCD_ENRDY_bm) == 0); //关闭TCD
	TCD0.CTRLA &= ~TCD_ENABLE_bm;
	while ((TCD0.STATUS & TCD_ENRDY_bm) == 0); //wait to finish sync

	CPU_CCP = CCP_IOREG_gc;  //CCP保护
	TCD0.FAULTCTRL = cmpEN;
	
	while ((TCD0.STATUS & TCD_ENRDY_bm) == 0); //启动TCD
	TCD0.CTRLA |= TCD_ENABLE_bm;

	RTC_INT_ENABLE;  //启动 RTC
}

 

Last Edited: Thu. Jan 13, 2022 - 08:56 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do NOT write to CCP yourself. Let the _PROTECTED_WRITE() macro do it for you. It is written in Asm so that no matter what the optimisation setting it will always complete within the 4 cycles.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I nearly change it directly all time :)

But replaced it now.

Thank you.