Помогите найти ошибку счётчика

крыша едет не спеша тихо шифером шурша.
stm32
получаю данные из порта в прерывании и добавляю в кольцевой буфер. Наблюдаю потерю данных.
решил дописать счётчик переполнения и ушёл в нирвану.

...
#define SIZERING 50U
...
typedef struct
{
	CANTX can[SIZERING];
	uint8_t inInd;
	uint8_t outInd;
	uint8_t max;
	bool fillmax;
} RINGTX;
RINGTX ring1TX = { .inInd = 0, .outInd = 0, .max = 0, .fillmax = false };
...
uint8_t RING_GetCount(RINGTX* buf)
{
	uint8_t retval = 0;
	if (buf->inInd < buf->outInd) retval = SIZERING + buf->inInd - buf->outInd;
	else retval = buf->inInd - buf->outInd;
	if (buf->max < retval) buf->max = retval;
	return retval;
}

void RING_Put_deep_copy(const CAN_RxHeaderTypeDef* header, const uint8_t* data, RINGTX* buf)
{
	if (RING_GetCount(buf) >= SIZERING - 1)
	{
		buf->fillmax = true;
		return;
	}
	uint8_t curpoz = buf->inInd++;
	if (buf->inInd >= SIZERING) buf->inInd = 0;
	...
}

void RING_Clear(RINGTX* buf)
{
	buf->inInd = 0;
	buf->outInd = 0;
}

void RING_Pop_addCount(RINGTX* buf)
{
	buf->outInd++;
	if (buf->outInd >= SIZERING) buf->outInd = 0;
}
...
void print_ring_status()
{
	int len = 0;
	len += sprintf((char*)vcpDataToSend + len, "ring1 max fill: %u 0x%X %s\r\n", ring1TX.max, ring1TX.max, ring1TX.fillmax ? "fillmax" : "norm");
	CDC_Transmit_FS(vcpDataToSend, len);
	ring1TX.max = 0;
}
...

дело в том, что функция печати выводит нереальные значения больше SIZERING и меньше 255. Не могу найти где я обосрался. ещё из наблюдения, если я ставлю SIZERING большим(например 230), то выхлоп более-менее правдоподобный ~ 7-9

предлагаете нам поиграть в угадайку - что же у вас сломалось?

Если хотите полезных ответов, постарайтесь изложить проблему ВНЯТНО .

После кода в первом сообщении озвучена проблема

У вас функция выводит только параметр max (смысла которого я вообще не улавливаю). Почему бы не пойти от первоисточника и не вывести для начала входной и выходной индексы?

В методе

RING_GetCount

вы какую-то хрень делаете…

У вас max всегда будет как минимум не меньше чем SIZERING, судя по этому выражению:

можете как-то подробней? inInd и outInd не могут быть более SIZERING-1. Вроде код об этом чётко говорит. Так как это выражение может быть больше SIZERING ?

ну я немного поторопился. Не “всегда больше”, а иногда может быть больше :slight_smile: Зависит от того, в каком порядке его вычислять.
Если вот так

и out > ind, то в скобке получится отрициальный результат. А поскольку все члены у вас беззнаковые, то он дополнится до 255 и, например, вместо -56 получится +200. Потом это будет прибавлено к SIZERING

Повторяю - я бы начал с вывода на печать значений индексов - так будет проще поймать ошибку.

спасибо большое за пинок, проверю на днях и обязательно отпишусь. цикличность печатает правильно, уже проверял, после 49 наступает 0 в обоих индексах.

дело не в проверке. Интересно посмотреть на комбинации индексов, на которых max начинает выдавать ерунду. То есть надо выводить все три параметра.

Не верю! Есть только две вещи от которых у мужика может поехать крыша и глючные кольцевые буферы к ним не относятся.

По делу же Вам правильно говорят, печатайте указатели начала и конца, без них Вы слепы.

А если не поможет, подготовьте нормальный пример демонстрирующий проблему. Не те огрызки, что Вы вложили, а полный пример, который я могу просто взять и запустить у себя без всяких танцев с бубнами и без дописывания чего-то. Если Вы это сделаете, то шанс получить вразумительную помощь увеличится кратно.

Приключение продолжается. Изменил :

if (buf->inInd < buf->outInd) retval = SIZERING + buf->inInd - buf->outInd;

на:

if (buf->inInd < buf->outInd) retval = (SIZERING + buf->inInd) - buf->outInd;

Реакции ноль.
Далее по совету:

uint8_t RING_GetCount(RINGTX* buf)
{
	uint8_t retval = 0;
	if (buf->inInd < buf->outInd) retval = (SIZERING + buf->inInd) - buf->outInd;
	else retval = buf->inInd - buf->outInd;
	if (buf->max < retval) buf->max = retval;
	
	if (retval > SIZERING)
	{
		int len = 0;
		len += sprintf((char*)vcpDataToSend + len, "ring in: %u\r\n", buf->inInd);
		len += sprintf((char*)vcpDataToSend + len, "ring out: %u\r\n", buf->outInd);
		len += sprintf((char*)vcpDataToSend + len, "ring retval: %u\r\n\r\n", retval);
		CDC_Transmit_FS(vcpDataToSend, len);
	}

	return retval;
}

На выхлопе получаю такую картину:

ring in: 0
ring out: 49
ring retval: 207

ring in: 0
ring out: 49
ring retval: 207

ring in: 0
ring out: 49
ring retval: 207

ring in: 0
ring out: 49
ring retval: 207

ring in: 0
ring out: 49
ring retval: 207

Но этот выхлоп есть только когда железка примонтирована к цели. То есть когда функция RING_Put_deep_copy вызывается в прерывании. Если я вызываю эту функцию для отладки в главном цикле, то переполнения нет, и retval всегда = 1.

К железке подключено 3 CAN. Использую HAL. Религия HAL заставляет использовать один калбек и проверять в нём от куда пришло прерывание, хотя по факту используется три разных прерывная. Возможно здесь кроется засада? Приоритет NVIC на каны выставлен везде в 0. Сама функция калбека:

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef* hcan)
{
	__HAL_TIM_SET_COUNTER(&htim6, 0); // обнуляем таймер выключения
	HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin); // моргаем для отладки
	if (hcan->Instance == CAN1)
	{
		if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader1, RxData1) == HAL_OK)
		{
			RxFlag1 = true;
			countCanRXAll++;
			if(can1RSniffNedet) countCanFrame++;
		}
		else Error_Handler();
		
		if (can2enable && can2proxy)
		{
			RING_Put_deep_copy(&RxHeader1, RxData1, &ring2TX);
		}
		if (can3enable && can3proxy)
		{
			RING_Put_deep_copy(&RxHeader1, RxData1, &ring3TX);
		}

		if (can1RSniffNedet)
		{
			PrintFrame(&RxHeader1, RxData1, '1', 'R');
		}
	}
	if (hcan->Instance == CAN2)
	{
		if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader2, RxData2) == HAL_OK)
		{
			RxFlag2 = true;
			countCanRXAll++;
			if (can2RSniffNedet) countCanFrame++;
		}
		else Error_Handler();
		if (can1enable && can1proxy)
		{
			RING_Put_deep_copy(&RxHeader2, RxData2, &ring1TX);
		}
		if (can3enable && can3proxy)
		{
			RING_Put_deep_copy(&RxHeader2, RxData2, &ring3TX);
		}
		if (can2RSniffNedet)
		{
			PrintFrame(&RxHeader2, RxData2, '2', 'R');
		}
	}
	if (hcan->Instance == CAN3)
	{
		if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader3, RxData3) == HAL_OK)
		{
			RxFlag3 = true;
			countCanRXAll++;
			if (can3RSniffNedet) countCanFrame++;
		}
		else Error_Handler();
		if (can1enable && can1proxy)
		{
			RING_Put_deep_copy(&RxHeader3, RxData3, &ring1TX);
		}
		if (can2enable && can2proxy)
		{
			RING_Put_deep_copy(&RxHeader3, RxData3, &ring2TX);
		}
		if (can3RSniffNedet)
		{
			PrintFrame(&RxHeader3, RxData3, '3', 'R');
		}
	}
}

Ну как я говорил - 207 это как раз дополнение числа 49 до полного байта, 256

Уважаемый, так что с этим можно сделать? почему ситуация возникает только при использовании прерываний.

Поздно уже, и я с телефона… не посоветую.

спасибо что откликнулись, вопрос решил после вашего пинка.
Проблема крылась в прерывании.
Главный цикл долбит вызов

RING_GetCount(ring1TX);

Я так пологаю, что если прерывание происходило между if - else , то счётчик уходил в нирвану. Если я не прав, то жду коментариев.
Решил так:

uint8_t RING_GetCount(RINGTX* buf)
{
	__disable_irq();
	uint8_t retval = 0;
	if (buf->inInd < buf->outInd) retval = (SIZERING + buf->inInd) - buf->outInd;
	else retval = buf->inInd - buf->outInd;
	if (buf->max < retval) buf->max = retval;
	__enable_irq();
	if (retval > SIZERING)
	{
		int len = 0;
		len += sprintf((char*)vcpDataToSend + len, "ring in: %u\r\n", buf->inInd);
		len += sprintf((char*)vcpDataToSend + len, "ring out: %u\r\n", buf->outInd);
		len += sprintf((char*)vcpDataToSend + len, "ring retval: %u\r\n\r\n", retval);
		CDC_Transmit_FS(vcpDataToSend, len);
	}

	return retval;
}

Наверное экономней будет сначала атомарно перегрузить значения волатильных переменных в локальные, а потом уже ветвится по алгоритму.

Но, да - неатомарность на волатиле способно ввести в запой.

А можно пример простой? читаю про LDREX и STREX на stm32 и не моду понять как их применять.

Пример простой: прерывания запретили, переменные из волатилей забрали, прерывания разрешили. Дальше хоть if, хоть while.

1 лайк

спасибо, немного переосмыслил, и понял, что трогать прерывания, вообще не нужно.

uint8_t RING_GetCount(RINGTX* buf)
{
	uint8_t retval = 0;
	volatile uint8_t inInd = buf->inInd; // для атомарности
	volatile uint8_t outInd = buf->outInd;
	if (inInd < outInd) retval = (SIZERING + inInd) - outInd;
	else retval = inInd - outInd;
	if (buf->max < retval) buf->max = retval;
	return retval;
}

в принципе можно ещё сократить, так как outInd ни когда не изменяется в прерывании

это тех кто пьёт, а тех кто не пьёт (как я)