ATSAMD21J18A USART sporadic garbage

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

So I have a weird bug I am trying to figure out that is inconsistent. I am running SERCOM0 at 500kBaud (rs485) for a custom sensor and on sometimes on startup I get false data. No Frame errors, it thinks it is getting correct data. I have verified the data via oscilloscope on the UART lines post transceiver, checked timing, and tried a dozen tricks on hardware and firmware. I have tried slowing the Baud to 250k thinking maybe I was just pushing it too hard. I am running LSB first and my circular receive buffer seems to get filled with bad data in the MSB range. This problem only seems to happen at startup and occasionally. resetting the controller will usually fix the problem. 

I have attached what I think is relevant. Forgive some of the ugly code.....

Example of what I am seeing:

UART lines on O-scope read 167 yet the data read in is 79. The weirdest part is when it gets in this mode it can be extremely consistent, like it is distorting only specific range of bits.

Any help would be appreciated. Thanks in advanceOscope window of comm frame

 

void SERCOM0_Handler(void)
{
	uint8_t temp = 0;
	if(REG_SERCOM0_USART_INTFLAG & (1<<1))//tx complete
	{
		REG_PORT_OUTCLR0 = PORT_PA06;//TX DISABLE
		message_requested = true;
	}
	else//rx complete
	{
		if(REG_SERCOM0_USART_STATUS & 0b110)//check for frame and buffer overflow error
		{
			//dummy read skip this data point
			temp = REG_SERCOM0_USART_DATA;
			REG_SERCOM0_USART_CTRLB &= ~(1<<17); //disable receiver
			//REG_SERCOM0_USART_STATUS |= 0b110; //clear errors, write '1' to any error
			Hall_Reset = true;
		}
		else if(message_requested == false)
		{
			temp = REG_SERCOM0_USART_DATA;
		}
		else
		{
			Hall_index++;
			Hall_index = Hall_index % sizeof_Hall_buffer;
			Hall_data[Hall_index] = REG_SERCOM0_USART_DATA;
			Plant.lastfeedback = Plant.feedback;
			Plant.feedback = Hall_data[Hall_index];
			Plant.new_feedback = true;
		}
	}
	REG_SERCOM0_USART_INTFLAG = 0xff;//clear interrupts
}

void ADC_Handler(void)//every 910us
{
		while(REG_ADC_STATUS);
		REG_ADC_INTFLAG = 0xf;	
		ADC_index++;
		ADC_index = ADC_index % sizeof_ADC_buffer;
		while(REG_ADC_STATUS);
		ADC_data[ADC_index] = REG_ADC_RESULT;
		Current.feedback = ADC_data[ADC_index];
		Current.new_feedback = true;
		while(REG_DAC_STATUS);
		REG_DAC_DATA = Plant.feedback*775/256;	
}

void TC3_Handler(void)//1ms timer
{
	if(REG_TC3_INTFLAG & (1<<4))//CC0 or 1ms loop
	{
		message_requested = false;
		if(startupcounter < 16000) startupcounter++;
		if(Plant.new_feedback == true)
		{
			if(Current.new_feedback == true)
			{
				updateControl = true;
				Current.new_feedback = false;
				Plant.new_feedback = false;
				Turn_HallComm_Error_signal_off();
				Hall_error_counter = 0;
			}
		}
		else if (startupcounter > 15000)//don't throw error during startup
		{
			Hall_error_counter++;
			if(Hall_error_counter > 3)
			{
				Turn_HallComm_Error_signal_on();
				Hall_error_counter = 0;
			}
			updateControl = false;
			
		}
	}
	else if(REG_TC3_INTFLAG & (1<<5))//CC1 ~60us before 1ms loop
	{
		REG_PORT_OUTSET0 = PORT_PA06;//TX ENABLE
		REG_SERCOM0_USART_DATA = 'n';		
	}
	REG_TC3_INTFLAG = 0xFF; //clear all interrupts flags
}

int main(void)
{
	__disable_irq();
	atmel_start_init();
	USART0Configure();
	USARTConfigure();
	ADC_Configure();
	DAC_Configure();
	Timer_Configure();
	Turn_HallComm_Error_signal_off();
	__enable_irq();
	display_Intro(4000);
	initiliaze_PID_data();
	init_levitation();
	
	while (1) 
	{
		//check_errors();
		if(Hall_Reset)
			{
				Hall_Reset = false;
				REG_SERCOM0_USART_CTRLA = 0;//disable
				while(REG_SERCOM0_USART_SYNCBUSY);
				REG_SERCOM0_USART_CTRLA = (1<<2) | (1<<30) | (1<<20) | (1<<7) | (2<<22); //internal clock mode, LSB transmitted first,	PAD1(PA5) for RX PAD0(PA4) for TX
				REG_SERCOM0_USART_BAUD = 60070;
				REG_SERCOM0_USART_INTENSET = (1<<2);//receive complete
				NVIC_EnableIRQ(SERCOM0_IRQn);
				NVIC_SetPriority(SERCOM0_IRQn,0);
				while(REG_SERCOM0_USART_SYNCBUSY);
				REG_SERCOM0_USART_CTRLB = (1<<16) | (1<<17); //enable rx and disable tx
				while(REG_SERCOM0_USART_SYNCBUSY);
				REG_SERCOM0_USART_STATUS = 0;
				REG_SERCOM0_USART_INTFLAG = 0xff;
				REG_SERCOM0_USART_CTRLA |= (1<<1); //enable	
				while(REG_SERCOM0_USART_SYNCBUSY);	
			}
		if(updateControl == true)// 1ms loop
		{
			updateControl = false;		
			switch (state)
			{
				case levoff :
					set_duty_cycle(0);
					gpio_set_pin_level(PA20,false);
					check_user_commands();
					Turn_stable_signal_off();
					foundTop = false;
					foundBottom = false;
					top_pos = 0;
					bottom_pos = 0;
					if( is_ON_Signal() || Levitate_Command == true)
					{
						state = findlimits;
						init_levitation();
					}
				break;
				case findlimits:
					find_hardstops();
				break;
				case softstart :
					step_from_rail();
				break;
				case softstop :
					step_to_rail();
				break;
				case levitate:
					if(is_OFF_Signal() && (Levitate_Command == false))
					{
						state = softstop;
					}
					
					if(!isInRange((bottom_pos + emergency_lev_window),(top_pos - emergency_lev_window),Plant.feedback))
					{
						bounce_counter++;
					}
					else bounce_counter = 0;
					if(bounce_counter > 60)
					{
						bounce_counter = 0;
						Turn_stable_signal_off();
					}
					PI_Current_Control(PID_Plant_Control(Plant.setpoint));
					adjust_Pos_Setpoint();
					//user_display_Runtime();
				break;
				case fault:
				if(is_OFF_Signal())
				{
					state = levoff;
					clear_errors();
				}
				break;
				default:
				state = levoff;
			}
		}
	}
}

void Timer_Configure(void)
{
	GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_TCC2_TC3_Val;    // select TC3 peripheral channel
	GCLK->CLKCTRL.reg |= GCLK_CLKCTRL_GEN_GCLK0;        // select source GCLK_GEN[0]
	GCLK->CLKCTRL.bit.CLKEN = 1;            // enable TC3 generic clock
	REG_PM_APBCMASK |= (1<<11);//enable bus clock to TC3
	REG_TC3_COUNT16_CC0 = 188;//1ms
	REG_TC3_COUNT16_CC1 = 158;// before 1ms timer//170
	while(REG_TC3_STATUS & (1<<7));
	REG_TC3_CTRLA |= (1<<12) | (0x6<<8) | (0x1<<5);//set presync on prescaler, prescaler/256, matched frequency
	while(REG_TC3_STATUS & (1<<7));
	REG_TC3_INTENSET = (1<<4) | (1<<5) | (1<<0);//enable match & overflow interrupt
	REG_TC3_INTFLAG = 0xFF; //clear all flags
	NVIC_EnableIRQ(TC3_IRQn);
	NVIC_SetPriority(TC3_IRQn,1);
	REG_TC3_CTRLA |= (1<<1); //Enable	
}
void USART0Configure(void)
{
	//Clocks
	GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_SERCOM0_CORE_Val;    // select SERCOM0 peripheral channel
	GCLK->CLKCTRL.reg |= GCLK_CLKCTRL_GEN_GCLK0;        // select source GCLK_GEN[0]
	GCLK->CLKCTRL.bit.CLKEN = 1;            // enable SERCOM0 generic clock
	REG_PM_APBCMASK |= (1<<2);//enable bus clock to SERCOM0
	GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_SERCOMX_SLOW_Val;    // select SERCOM0 peripheral channel
	GCLK->CLKCTRL.reg |= GCLK_CLKCTRL_GEN_GCLK1;        // select source GCLK_GEN[0]
	GCLK->CLKCTRL.bit.CLKEN = 1;            // enable SERCOM0 generic clock
	//IO for PA5(RX) and PA4(TX)
	PORT->Group[0].PINCFG[5].bit.PMUXEN = 1;
	REG_PORT_DIRCLR0 = PORT_PA05; //set as input
	PORT->Group[0].PINCFG[4].bit.PMUXEN = 1;
	REG_PORT_DIRSET0 = PORT_PA04; //set as output
	REG_PORT_PMUX2 =  (0x3<<4) | (0x3<<0);//set both alternate functions
	//Peripheral
	//internal clock mode, LSB transmitted first,	PAD1(PA5) for RX PAD0(PA4) for TX, IBON enabled
	REG_SERCOM0_USART_CTRLA = (1<<2) | (1<<30) | (1<<20) | (1<<7) | (1<<8) | (2<<22); 
	REG_SERCOM0_USART_BAUD = 60070;//60070 for 250k//54607 for 500k
	REG_SERCOM0_USART_INTENSET = (1<<2) | (1<<1);//receive complete and transmit complete
	NVIC_EnableIRQ(SERCOM0_IRQn);
	NVIC_SetPriority(SERCOM0_IRQn,0);
	while(REG_SERCOM0_USART_SYNCBUSY);
	REG_SERCOM0_USART_CTRLB = (1<<16) | (1<<17); //enable rx and enable tx
	while(REG_SERCOM0_USART_SYNCBUSY);
	REG_SERCOM0_USART_CTRLA |= (1<<1); //enable
	while(REG_SERCOM0_USART_SYNCBUSY);
	
}

 

This topic has a solution.

Engineering is the truest form of art

Last Edited: Wed. Oct 21, 2020 - 08:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One thing I forgot to mention. Changing SAMPA in SERCOM0.CTRLA seems to have some real effects. I was trying to move it out of the area of ringing and the error happens much less often. 

I have checked the errata for anything helpful but haven't come up with anything from that perspective. 

Thanks,

Engineering is the truest form of art

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

You've probably already noticed this, but it looks like the receiver is losing the first bit (or the transmitter is failing to send it):

 

Should be 167 = 0xA7 = 10100111

Getting    79 = 0x4f =  01001111

 

Is that helpful?

 

 

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

I have noticed some patterns. Since it is running LSB first I alway seem to be getting the MSB(end of frame) incorrect. It would appear to be on the ATSAMD21 side as my UART (post transceiver) is showing good Oscope data.

Weirdest problem.....

Engineering is the truest form of art

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

Has you checked the RS485 turn-around time? (assuming you're running half duplex).

Other RS485 related issues are:

turning of tx too early (before the last few bits have been sent)

no pullup on the rxd pin (max485 only actively drives this when the rx is enabled)

RS485 is properly biased. Different RS485 chips do different things on an undriven bus.

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

I am running Half duplex.

  1. My turn around time (transition from transmitting to receiving right?) is twice what the transceiver datasheet says it needs. transceiver says it needs max of 9us and it is getting 20-30 us
  2. tx is managed by interrupt and only turns off once transmit complete interrupt is thrown.
  3. I have a 10k pull up on the UART side.
  4. I do not have any biasing on the rs485 transceiver as it's Vit+ is so low and its Vhyst is sufficient to give me a high noise tolerance.

 

Some pertinent information. I have a unique scenario as this is not being run on twisted pair (i know....bad) and they want the solution without having to make the change to twisted pair. When the event happens i notice that the noise on the signal is way higher than the noise tolerance of my transceiver. (800mv VNpp whereas I only have 140mv VNpp tolerance). We are running about 30ft at 500kbaud. 

 

Engineering is the truest form of art

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

but why would the O-scope pick up error free characters (post transceiver) but the controller get garbage?

 

Engineering is the truest form of art

Last Edited: Mon. Sep 21, 2020 - 03:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My clock speed is changing.....what!

It appears that sometimes on start up my smallest bit rate goes from 2us to 1.72us!!!! How!! I should have just went with an external crystal.......I thought that the DFLL would have been accurate enough.

 

Engineering is the truest form of art

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

Vazaha wrote:
I should have just went with an external crystal..

Where serial comms is involved - always!

 

does the DFLL take time to "settle" ... ?

 

does it have  a status to check when it's valid ?

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Turned out switching to the DPLL made the problem go away. There are ways to check, I was using ASF so maybe they were doing something wrong internal, but I was cued onto this solution on some other forums so someone else was struggling with this and it doesn't appear in the errata so I am guessing it is the ASF. I started using the ASF and now 90% of it has been abandoned and rewritten....I do not trust software frameworks....

Engineering is the truest form of art