Top.Mail.Ru

STM32 и протокол LIN. Настройка и обмен данными. STM32CubeMx.

Приветствую всех на нашем сайте! Выходит в свет вторая часть из обещанных трех, посвященных работе с протоколом LIN. Сегодня вооружимся микроконтроллером STM32 и реализуем связь по LIN в разных режимах работы с использованием аппаратных средств. Напоминаю, что в третьей статье будем писать свой драйвер LIN с нуля на голом UART'е.

Подготовительный этап будет протекать как обычно - открываем STM32CubeMx и настраиваем нужную периферию. Для этого проекта мы будем использовать два модуля USART, настроенных на работу в режиме LIN, соединенных между собой. Вообще, строго говоря, подключение необходимо выполнять через микросхемы LIN-трансиверов:

Шина LIN.

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

Схема для теста LIN.

USART1 и USART2 настраиваем абсолютно одинаково, не забываем включить прерывания:

Настройка LIN в STM32CubeMx.
USART прерывания в STM32CubeMx.

Кроме того, как обычно, конфигурируем тактирование микроконтроллера:

Настройки тактирования STM32.

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

Итак, в чем же заключается аппаратная поддержка LIN в STM32. На самом деле, функционал фактически минимальный. Для Master'а есть возможность аппаратно сгенерировать поле Break, используя функцию:

HAL_LIN_SendBreak(UART_HandleTypeDef *huart)

Для Slave есть возможность отловить все тот же брейк в прерывании. В общем-то вот и все...

Давайте чуть подробнее разберем механизм обнаружения поля Break подчиненным устройством. Стоит отметить, что в HAL по какой-то причине решили не делать никаких функций для этого, поэтому будем работать "руками". В общем, то нам нужно будет всего лишь включить прерывание по обнаружению брейка:

__HAL_UART_ENABLE_IT(&huart2, UART_IT_LBD);

В данном случае у меня использован USART2.

И далее в прерывании, после стандартной обработки прерывания HAL'ом, отлавливаем событие обнаружения брейка:

uint32_t isrflags = huart2.Instance->SR;

if ((isrflags & USART_SR_LBD) != RESET)
{
	CLEAR_BIT(huart2.Instance->SR, USART_SR_LBD);
	
	// .....
}

Все, на этом аппаратные возможности заканчиваются, все остальное мы сейчас сделаем самостоятельно. На USART1 у нас будет Master, соответственно, на USART2 - Slave. И начнем с более сложного, а именно с кода подчиненного устройства. Но прежде всего прочего нужно обсудить, каков будет план.

Итак, реализуем два режима работы:

  • Master отправляет заголовок (header) пакета с определенным PID, после чего Slave переходит в режим приема данных.
  • Master отправляет заголовок с идентификатором, который сигнализирует Slave'у о том, что надо ответить ведущему порцией данных. Соответственно, Master после отправки заголовка должен встать на прием.

Пусть значения PID для примера будут такими:

  • 0x3A - соответствует первому случаю, то есть Master отправляет заголовок и данные.
  • 0x3B - это уже второй случай из перечисленных.

Важное дополнение - как вы помните, байт PID помимо 6-ти битов идентификатора включает в себя еще два бита четности. Для примера же будем просто использовать вышеупомянутые значения, без учета четности.

Добавляем в наш проект эти значения и пару переменных (ссылку на полный проект я обязательно добавлю в конце статьи):

#define LIN_TX_ID                                                         0x3A
#define LIN_RX_ID                                                         0x3B
uint8_t linTxId = LIN_TX_ID;
uint8_t linRxId = LIN_RX_ID;

Кроме того, объявляем массивы для хранения данных ведущего и подчиненного:

#define LIN_DATA_BYTES_NUM                                                9
uint8_t linMasterData[LIN_DATA_BYTES_NUM];
uint8_t linSlaveData[LIN_DATA_BYTES_NUM];

В проекте все находится в разных файлах, здесь же для наглядности я буду комбинировать код чуть иначе. Принимать и отправлять мы будем по 8 байт, то есть максимально возможное количество байт в одном фрейме. Но обратите внимание, что в массивах по 9 байт - еще 1 байт мы выделили под прием и отправку контрольной суммы.

И раз уж об этом зашла речь, то вот функция для расчета контрольной суммы (будем использовать классический алгоритм):

uint8_t LIN_CalcCheckSum(uint8_t *data, uint8_t len)
{
	uint16_t sum = 0;
	
	for (uint8_t i = 0; i < len; i++)
	{
		sum += data[i];
	}
  
	while(sum > 0xFF)
	{
		sum -= 0xFF;
	}
  
	sum = 0xFF - sum;
  
	return (uint8_t)sum;
}

И для систематизации работы программы добавим возможные режимы, в которых может находиться Slave:

typedef enum
{
	LIN_RECEIVING_BREAK     = 0x01,
	LIN_RECEIVING_SYNC      = 0x02,
	LIN_RECEIVING_ID        = 0x03,
	LIN_RECEIVING_DATA      = 0x04,
	LIN_SENDING_DATA        = 0x05,
} LIN_State;

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

LIN_State slaveState = LIN_RECEIVING_BREAK;

Код приема брейка, как мы уже обсудили, поместим в обработчик прерывания:

/**
	* @brief This function handles USART2 global interrupt.
	*/
void USART2_IRQHandler(void)
{
	/* USER CODE BEGIN USART2_IRQn 0 */

	/* USER CODE END USART2_IRQn 0 */
	HAL_UART_IRQHandler(&huart2);
	/* USER CODE BEGIN USART2_IRQn 1 */

	if (slaveState == LIN_RECEIVING_BREAK)
	{
		uint32_t isrflags = huart2.Instance->SR;
    
		if ((isrflags & USART_SR_LBD) != RESET)
		{
			CLEAR_BIT(huart2.Instance->SR, USART_SR_LBD);
			uint16_t temp = huart2.Instance->DR;
			slaveState = LIN_RECEIVING_SYNC;
			HAL_UART_Receive_IT(&huart2, &rxByte, 1);
		}
	}
        
  /* USER CODE END USART2_IRQn 1 */
}

Первым делом проверяем, что Slave находится в ожидании поля Break, затем проверяем, не вызвано ли прерывание как раз-таки приемом этого поля, и, если да, то переводим Slave в режим ожидания поля Sync. Тут есть небольшой нюанс в виде чтения регистра данных USART'а, это нужно для корректного приема последующих байт:

uint16_t temp = huart2.Instance->DR;

Вся остальная логика у нас будет в callback-функциях по окончанию приема и передачи. Начинаем с приема и двигаемся по всем возможным состояниям Slave устройства:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if (huart == &huart2)
	{
		uint8_t checkSum = 0;

		switch(slaveState)
		{
			case LIN_RECEIVING_SYNC:
				if (rxByte == LIN_SYNC_BYTE)
				{
					slaveState = LIN_RECEIVING_ID;
					HAL_UART_Receive_IT(&huart2, &rxByte, 1);
				}
				else
				{
					slaveState = LIN_RECEIVING_BREAK;
				}
				break;
        
			default:
				break;
		}
	}
}

Проверяем принятый байт, если значение верное - 0x55 - встаем на прием PID, иначе возвращаемся в исходную точку. Добавляем ветку для приема идентификатора:

case LIN_RECEIVING_ID:
	if (rxByte == LIN_RX_ID)
	{
		slaveState = LIN_SENDING_DATA;

		for (uint8_t i = 0; i < LIN_DATA_BYTES_NUM - 1; i++)
		{
			linSlaveData[i] = 0x30 + i;
		}

		linSlaveData[LIN_DATA_BYTES_NUM - 1] = LIN_CalcCheckSum(linSlaveData, LIN_DATA_BYTES_NUM - 1);

		HAL_UART_Transmit_IT(&huart2, linSlaveData, LIN_DATA_BYTES_NUM);
	}
	else
	{
		if (rxByte == LIN_TX_ID)
		{
			slaveState = LIN_RECEIVING_DATA;
			HAL_UART_Receive_IT(&huart2, linSlaveData, LIN_DATA_BYTES_NUM);
		}
		else
		{
			slaveState = LIN_RECEIVING_BREAK;
		}
	}
	break;

Здесь все чуть сложнее, но, в принципе, тоже довольно прозрачно. Проверяем PID и по его значению определяем, что нам следует делать в дальнейшем. И тут два варианта:

  • Ожидать приема байт данных от Master'а
  • Или отправить свою порцию данных

Для случая передачи данных Slave'ом заполняем массив тестовыми значениями и дополняем его 9-м байтом - контрольной суммой. А если PID не соответствует тем 2-м значениям, которые мы определили для этого примера, то возвращаем устройство в начальное положение, а именно в состояние ожидания брейка.

И, наконец, последняя часть callback-а по приему:

case LIN_RECEIVING_DATA:
	checkSum = LIN_CalcCheckSum(linSlaveData, LIN_DATA_BYTES_NUM - 1);
        
	if (linSlaveData[LIN_DATA_BYTES_NUM - 1] == checkSum)
	{
		linSlaveRxCnt++;
	}
        
	slaveState = LIN_RECEIVING_BREAK;          
	break;

Как мы обсудили ранее, принимаем 9 байт, то есть 8 байт данных и байт контрольной суммы. И когда все данные приняты - рассчитываем контрольную сумму по первым 8-ми байтам и сравниваем ее значение с 9-ым байтом массива данных. В случае успеха инкрементируем переменную linSlaveRxCnt. Этот счетчик будет для нас сигналом успешно принятых данных.

Но в зависимости от принятого идентификатора Slave мог также перейти в режим передачи данных, поэтому этот случай нам также нужно обработать. И делаем мы это в коде callback-функции по окончанию передачи данных:

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	if (huart == &huart2)
	{
		switch(slaveState)
		{        
			case LIN_SENDING_DATA:
				linSlaveTxCnt++;
				slaveState = LIN_RECEIVING_BREAK;
				break;
        
			default:
				break;
		}
	}
}

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

При работе Master'а в режиме отправки данных Slave'у:

for (uint8_t i = 0; i < LIN_DATA_BYTES_NUM - 1; i++)
{
	linMasterData[i] = 0x40 + i;
}
      
HAL_LIN_SendBreak(&huart1);

HAL_UART_Transmit(&huart1, &linSync, 1, 10);
HAL_UART_Transmit(&huart1, &linTxId, 1, 10);

linMasterData[LIN_DATA_BYTES_NUM - 1] = LIN_CalcCheckSum(linMasterData, LIN_DATA_BYTES_NUM - 1);

HAL_UART_Transmit(&huart1, linMasterData, LIN_DATA_BYTES_NUM, 10);

linMasterTxCnt++;
HAL_Delay(1000);

Заполняем данные тестовыми значениями и последовательно отправляем:

  • Break
  • Sync
  • PID
  • Данные
  • Контрольную сумму

Все четко по формату пакета протокола LIN. Для примера я сделал отправку раз в секунду. В режиме, когда Master отправляет в шину заголовок и ожидает данные от подчиненного все практически идентично:

for (uint8_t i = 0; i < LIN_DATA_BYTES_NUM; i++)
{
	linMasterData[i] = 0x00;
}
        
HAL_LIN_SendBreak(&huart1);

HAL_UART_Transmit(&huart1, &linSync, 1, 10);
HAL_UART_Transmit(&huart1, &linRxId, 1, 10);

HAL_UART_Receive(&huart1, linMasterData, LIN_DATA_BYTES_NUM, 10);

uint8_t checkSum = LIN_CalcCheckSum(linMasterData, LIN_DATA_BYTES_NUM - 1);

if (linMasterData[LIN_DATA_BYTES_NUM - 1] == checkSum)
{
  linMasterRxCnt++;
}

HAL_Delay(1000);

Последний аргумент функций HAL_UART_Transmit() и HAL_UART_Receive() - это величина таймаута в миллисекундах. Если функция не закончит свое выполнение за это время, то произойдет возврат из функции с кодом ошибки HAL_TIMEOUT.

Разница только в том, что после отправки PID устройство встает на прием данных, а не начинает передачу, плюс по окончанию приема проверяем контрольную сумму. Для индикации работоспособности здесь также будем пользоваться обычными счетчиками пакетов linMasterRxCnt и linMasterTxCnt.

Переключение между режимами работы в проекте осуществляется переменной linMasterTask. Master отправляет:

uint8_t linMasterTask = LIN_MASTER_TX;

Master принимает:

uint8_t linMasterTask = LIN_MASTER_RX;

Собираем, прошиваем, запускаем, проверяем.

Для случая, когда ведущий отправляет данные подчиненному смотрим на счетчики linMasterTxCnt и linSlaveRxCnt. А также на значения в массивах linMasterData и linSlaveData. Под отладчиком можно наблюдать как счетчики параллельно инкрементируются, то есть отправляемые пакеты успешно принимаются:

Программа для передачи по интерфейсу LIN.

Аналогично и в режиме, когда Master получает данные от Slave:

Программа для приема по интерфейсу LIN.

Здесь уже вступают в игру счетчики linMasterRxCnt и linSlaveTxCnt. Ну и кроме того, значения в массивах принимаемых и отправляемых данных в точности совпадают.

И на этой позитивной ноте заканчиваем сегодняшнюю статью, спасибо за внимание 🤝 При возникновении любых вопросов, смело пишите в комментарии, во всем разберемся.

Ссылка на проект - MT_LIN_HW_Example.

Подписаться
Уведомить о
guest

44 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
Алексей
Алексей
4 лет назад

Привет. Спасибо за статью, по Lin шине, почему то, очень мало информации в сети. Но, к сожалению, повторить проект у меня, пока что, не получилось. Хотел сделать мастера, который будет просто кидать информацию в лин шину, для проверки концепции. Подключить плату с микросхемой tja1021 к лин анализатору. В кубе сделал один юарт, в прошивке main добавил код, который мастером отправляет данные слейву (тогда же ни какие колбеки не нужны? ). Залил и стал дебагом смотреть. В результате, если не подключать к tja1021 +12, то в дебаге linMasterTxCnt++ значение растет. Если подключать +12, то все валиться с ошибками по uart. Пока застрял, дальше не знаю что делать

Алексей
Алексей
Ответ на комментарий  Aveal
4 лет назад

SPL_N подтянуто резистором к +5в. На ней 4.9 вольта. На лин, скорее всего не будет ни какого сигнала, Lin анализатор ловит что то только в момент подачи напряжение на контроллер. Замеры на выводах сделаю чуть позже. Сам проект тут https://yadi.sk/d/BVPZubR2hxtQWg .
Stm32f105+tja1021 . Cube Mx v6, keil 5.

Алексей
Алексей
Ответ на комментарий  Aveal
4 лет назад

На выводе lin нет ничего. На выводе Tx, если не подавать +12 видно что идут пакеты данных. На выводе Rx ничего нет. Если подключить +12, то данные в Tx идти перестают. Если убрать +12 данные не появляются, пока не сделать перезагрузку.

Алексей
Алексей
Ответ на комментарий  Aveal
4 лет назад

разобрался с передачей мастера. С ноля собрал проект и он заработал. https://yadi.sk/d/Et1czQsDfCLTVg
Буду разбираться с приемом. Спасибо.

Алексей
Алексей
4 лет назад

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

Алексей
Алексей
4 лет назад

Что то не разобрался в вопросе приема. На шине стоит прибор из кан хакера, который отвечает на запросы лин. Написал в main

HAL_LIN_SendBreak(&huart2);
HAL_UART_Transmit(&huart2, &linSync, 1, 10);
HAL_UART_Transmit(&huart2, &linTxId2, 1, 10);
HAL_UART_Receive(&huart2, linMasterData, LIN_DATA_BYTES_NUM, 10); //id 33
uint8_t checkSum = LIN_CalcCheckSum(linMasterData, LIN_DATA_BYTES_NUM - 1);
if (linMasterData[LIN_DATA_BYTES_NUM - 1] == checkSum)
{
  //linMasterRxCnt++;
	HAL_LIN_SendBreak(&huart2);
HAL_UART_Transmit(&huart2, &linSync, 1, 10);
HAL_UART_Transmit(&huart2, &linTxId, 1, 10); //Id 3A
linMasterData[LIN_DATA_BYTES_NUM - 1] = LIN_CalcCheckSum(linMasterData, LIN_DATA_BYTES_NUM - 1);
HAL_UART_Transmit(&huart2, linMasterData, LIN_DATA_BYTES_NUM, 10);
//linMasterTxCnt++;
HAL_Delay(1000);
}
HAL_Delay(1000);

Как я это понимал - после передачи запроса с id 33 стм будет бесконечно ждать ответа. По факту оказалось, что он ответа вообще не ждет. запросы идут один за одним с одинаковыми интервалами. В результате lin хакер показывает что идет запрос с ID 33 с интервалами в 1013 мс. На ответы lin хакера, прикидывающемся слейвом не реагирует. А, по моей задумке, должен полученное отправить мастером с id 3A.

Алексей
Алексей
4 лет назад

Еще вопрос назрел. В статье написано " Проверяем PID и по его значению определяем, что нам следует делать в дальнейшем. И тут, как вы помните, два варианта

  • Ожидать приема байт данных от Master’а
  • Или отправить свою порцию данных

"

Мне нужно создать лин фильтр и фильтровать одно значение. Как мне по ID понять мне надо будет отвечать на запрос мастера или ждать от него данные.
В этом примере будем мы отвечать или нет зависит от принятого ID, который мы указали ранее в прошивке.
Правильно я понмаю, что в шине слейв по одному и тому же id не может и слушать и отвечать.
То есть для того, что б отфильтровать значения надо сначала прослушать весь трафик и понят по каким ID слейвы отвечают, а по каким слушают? создавать внутри прошивки список ID которые слушают и которые отвечают и исходя из этого списка решать что делать?

Алексей
Алексей
4 лет назад

А мастером слать данные можно только из Main()? у меня затея - lin1 посылает данные в lin2. Lin2 ловит данные и эти данные кидает lin1, но уже с другим ID. Пытаюсь фильтр сделать. В коллбеке после приема данных сразу вставил код

     if (rxByte == LIN_TX_ID)
     {
      slaveState = LIN_RECEIVING_DATA;
      HAL_UART_Receive_IT(&huart2, linSlaveData, LIN_DATA_BYTES_NUM);
						HAL_Delay(2); 
						
			HAL_LIN_SendBreak(&huart1);
   HAL_UART_Transmit(&huart1, &linSyncSend, 1, 10);
   HAL_UART_Transmit(&huart1, &linBCMId, 1, 10);
    
   HAL_UART_Transmit(&huart1, linSlaveData, LIN_DATA_BYTES_NUM, 10);
     }
     else

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

Алексей
Алексей
Ответ на комментарий  Aveal
4 лет назад

https://yadi.sk/d/_fzaKzL0ycNs5A

почти полностью Ваш пример.
когда добавляю в колбеке

HAL_LIN_SendBreak(&huart1);

мк зависает. Если убираю - мк работает.

Алексей
Алексей
Ответ на комментарий  Aveal
4 лет назад

пока тесты идут без автомобиля и разбираюсь как это работает. А так надо будет порвать лин и корректировать некоторые данные. В колбеке приняли данные от мастера в лин2. Пока, временно, мастером выступает лин1. Позже там будет мастер автомобиля. В лин 2 приняли данные, и надо их перекинуть в лин 1 (в примере что б не путаться хотел их слать с другим ID, ну и что б не уйти в бесконечный цикл). Хотел реализовать это в колбеке сразу.

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

HAL_LIN_SendBreak(&huart1);

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

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

Алексей
Алексей
4 лет назад

Это опять я. Что то какая то фигня у меня с приемом данных.

HAL_UART_Receive(&huart1, linMasterData, LIN_DATA_BYTES_NUM, 10);

что то оно как то криво принимает.

Делаем запрос мастером в первую лин шину. Вторая лин шина слейвом отвечает. После этого полученные данные кидаем мастером в первую лин шину

код из майна

        HAL_Delay(20);

            HAL_LIN_SendBreak(&huart1);
    
        HAL_UART_Transmit(&huart1, &linSync, 1, 10);
        HAL_UART_Transmit(&huart1, &linRxId, 1, 10);
        
        HAL_UART_Receive(&huart1, linMasterData, LIN_DATA_BYTES_NUM, 10);
    
        uint8_t checkSum = LIN_CalcCheckSum(linMasterData, LIN_DATA_BYTES_NUM - 1);
        HAL_Delay(20);
        

                HAL_LIN_SendBreak(&huart1);
        HAL_UART_Transmit(&huart1, &linSync, 1, 10);
        HAL_UART_Transmit(&huart1, &linBCM1Id, 1, 10);
            linMasterData[LIN_DATA_BYTES_NUM - 1] = LIN_CalcCheckSum(linMasterData, LIN_DATA_BYTES_NUM - 1);
            
        HAL_UART_Transmit(&huart1, linMasterData, 9, 10);

слейв присылает данные

40 41 42 43 44 45 46 47 crc E1

мастер их принимает и шлет с другим ID, но шлет со сдвигом

E1 40 41 42 43 44 45 46 crc 47

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

Алексей
Алексей
Ответ на комментарий  Aveal
4 лет назад

это я так отметил контрольную сумму. CRC как разделитель. Я все контролирую через лин хакера, у него контральная сумма вынесена в отдельный столбец. то есть символы crc это отметка, что дальше идет байт контрольной суммы.

Алексей
Алексей
Ответ на комментарий  Aveal
4 лет назад

вот скриншот

2020-11-28_22-32-40.png
Алексей
Алексей
Ответ на комментарий  Aveal
3 лет назад

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

Алексей
Алексей
Ответ на комментарий  Алексей
3 лет назад

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

Павел
Павел
Ответ на комментарий  Алексей
3 лет назад

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

Андрей
Андрей
Ответ на комментарий  Алексей
3 лет назад

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

Влад
Влад
3 лет назад

Здравствуйте! Не могу скачать архив проекта. Спасибо.

Влад
Влад
Ответ на комментарий  Aveal
3 лет назад

Все нормально. Спасибо!

Андрей
Андрей
3 лет назад

Здравствуйте! Статья очень хорошая.А можете заделать такую же, но с DMA? Несколько смущает, что после отправки команды на чтение/запись LIN-шины нужно ждать, пока отправится/придёт каждый байт, и в это время ничего нельзя делать. Если единственная цель устройства - это приём/передача данных по LIN, то проблем нет. Если же у устройства есть другие дела, то лучше доверить приём/передачу DMA.
Собственно в своём проекте я так и поступил. Но столкнулся с интересным поведением LIN-шины - когда Master передаёт данные на LIN-чип (использую MCP2003B) через лапку Tx, выход LIN приходит в движение, как и полагается шлёт данные, которые ему скармливает мастер. Однако выход LIN возвращает эти же данные обратно мастеру через лапку Rx. Получается, что мастер слушает себя. На вход он получает и сигнал начала передачи, и биты синхронизации и байты сообщения.
Можно ли как-то на аппаратном уровне "фильтровать" байты, отправляемые мастером, и принимать только байты от других устройств?

Андрей
Андрей
Ответ на комментарий  Андрей
3 лет назад

тоже заметил, что все, что пришло на TX, появляется на ножке RX , видимо так работает трансивер TJA1020

Андрей
Андрей
Ответ на комментарий  Андрей
3 лет назад

вот мастер запрос, видно полное дублирование

Андрей
Андрей
Ответ на комментарий  Андрей
3 лет назад

а вот мастер запрос и ответ слейва

Эммануил Лаперузович
Эммануил Лаперузович
2 лет назад

У вас не совсем верное представление о шине LIN. Tё аппаратная суть предполагает горячую подстройку частоты приема ведомым. Скорость там понятие условное, соответствует некоторым рамкам. Но поскольку главным девизом шины является "дешево и сердито" предполагается, что ведомое устройство может даже не иметь кварца. Поэтому в т.н. заголовке, после паузы идет меандр. По сути 0х55 по которому ведомый, аппаратно замеряя тайминг перехода от 0 к 1 и усредняя результат, корректирует частоту приема. И соответственно ответа мастеру. Если таковой оговорен. Без этого возможен уход частоты обмена (например по температуре) со всеми вытекающими.

Александр
Александр
Ответ на комментарий  Aveal
1 год назад

Здравствуйте!
Спасибо за ваши статьи, очень познавательные.
Для работы с LIN я использую микроконтроллер STMG0xx. Как и во многих других микроконтроллерах от ST, в нем есть возможность автоматического определения скорости передачи данных. В режимы обычного UART (Automatic baud rate detection modes 0x55 character frame) auto baud rate работает прекрасно (пример пакета 0x55 0x31 0x32..), но при работе в режиме LIN-UART , автоматически скорость передачи данных не определяется, мое предположение, что это происходит из-за того, что перед 0х55 мастер отправляет поле BREAKE (0x0 0x55 0x3A......), микроконтроллер воспринимает breake, как ошибку кадра и в результате не происходит определение скорости (ABRF = 0 ABRE = 1). Может вы подскажете, как можно реализовать определение скорости передачи данных ведомым устройством в режиме LIN UART?

Александр
Александр
Ответ на комментарий  Aveal
1 год назад

К сожалению не помогает, я уже пробовал, autobaud начинает работать с самого начала кадра. Я думаю, что этот момент не был учтен инженерами ST и нужно писать, что то свое. Нигде не могу найти рабочий пример, как можно определить скорость передачи данных UART.
Теорию я понимаю, что, после получения beake, во время приема байта синхронизации, нужно запустить процедуру подсчета времени изменения уровня сигнала на ножке Rx с низкого на высокий, на основании этих измерений вычислить baud rate, но у меня не хватает опыта программно реализовать этот алгоритм. У вас случайно не было в планах написать статью по определению скорости передачи данных UART ведомым устройством?

44
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x