Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: CANFestival и STM32F103
Форум разработчиков электроники ELECTRONIX.ru > Интерфейсы > Форумы по интерфейсам > Controller Area Network (CAN)
Minti
Здраствуйте!

Так как я с МК и КЭНом(CANFestival) дело не имел, пытаюсь перепесать фаил timer.c от AVR(./drivers/AVR/timer_AVR.c) для STM32. Застрял на 2 defines(для начало wink.gif):
CODE
// Define the timer registers
#define TimerAlarm OCR3B
#define TimerCounter TCNT3

какие регистры соответствуют на STM32F103?
Minti
Помучившись с даташит от разных МК я пришёл к такому решению проблемы:
CODE
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_InitStructure;
#define TIM2_IRQChannel ((u8)0x1C) /* TIM2 global Interrupt (alte Bibliothek)*/

#define TimerAlarm TIM2->CCMR1
#define TimerCounter TIM_GetCounter(TIM2)

#define TIMER_INTERRUPT_LEVEL 1

void timer_can_irq_handler(void);


/************************** Modul variables **********************************/
// Store the last timer value to calculate the elapsed time
static TIMEVAL last_time_set = TIMEVAL_MAX;

void initTimer(void)
/******************************************************************************
Initializes the timer, turn on the interrupt and put the interrupt time to zero
INPUT void
OUTPUT void
******************************************************************************/
{
//TODO: check
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //TIM2 mit einem Takt versorgen

NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQChannel; //Interrupt Kanal auswählen. Hier TIM2
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //Interrupt Priorität festlegen
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //Interrupt Unterpriorität festlegen
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //Aktivieren des Interrupts
NVIC_Init(&NVIC_InitStructure); //Konfiguration übernehmen

TIM_InitStructure.TIM_Prescaler = 2000; // Clock Teiler 72MHz / 2000 = 36kHz
TIM_InitStructure.TIM_Period = 36000; // Anzahl Durchläufe bis Überlauf Interrupt 36kHz / 36000 = 1s
TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //Keine Clock Division
TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up; //Hochzählender Timer Modus
TIM_TimeBaseInit(TIM2, &TIM_InitStructure); //Konfiguration Übernehmen
TIM_Cmd(TIM2, ENABLE); //Timer Aktivieren

TIM_ClearFlag(TIM2, TIM_FLAG_Update); //Interrupt Flag von TIM2 löschen
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //Interrupt für TIM2 Aktivieren
TIM_ClearFlag(TIM2, TIM_FLAG_Update); //Interrupt Flag für TIM2 Löschen
//End check


}

void setTimer(TIMEVAL value)
/******************************************************************************
Set the timer for the next alarm.
INPUT value TIMEVAL (unsigned long)
OUTPUT void
******************************************************************************/
{
TimerAlarm += value; // Add the desired time to timer interrupt time
}

TIMEVAL getElapsedTime(void)
/******************************************************************************
Return the elapsed time to tell the stack how much time is spent since last call.
INPUT void
OUTPUT value TIMEVAL (unsigned long) the elapsed time
******************************************************************************/
{
unsigned int timer = TimerCounter; // Copy the value of the running timer
// Calculate the time difference
return timer > last_time_set ? timer - last_time_set : last_time_set - timer;
}


//*----------------------------------------------------------------------------
//* Function Name : timer_can_irq_handler
//* Object : C handler interrupt function by the interrupts
//* assembling routine
//* Output Parameters : calls TimeDispatch
//*----------------------------------------------------------------------------
//void timer_can_irq_handler(void)
void TIM2_IRQHandler(void)
{
last_time_set = TimerCounter;
TimeDispatch(); // Call the time handler of the stack to adapt the elapsed time
}


правильно ли выбраны регистры? Какие параметры для TIM_InitStructure, чтобы

Цитата
graph
Scheduler can handle short clock value ranges limitation found on some
µC. As an example, value range for a 16bit clock counter with 4µs tick is
crossed within 0.26 seconds... Long alarms must be segmented.
Chronogram illustrate a long alarm (A) and a short periodic alarm (cool.gif,
with a A value > clock range > B value. Values t0...t8 are successive setTimer
call parameter values. t1 illustrates an intermediate call to TimeDispatch,
caused by a delay longer than clock range. Because of long alarm segmentation,
at the end of t1, TimeDispatch call will not trig any alarm callback.
syoma
А какой профиль делаете?
Minti
CiA 402
пока проблема с прерываниями(interrupts): TIM2 и Heartbeat с 50ms съедают все ресурсы. а может быть собака в другом месте зарыта?
Forger
Цитата(Minti @ Oct 12 2011, 12:33) *
CiA 402
пока проблема с прерываниями(interrupts): TIM2 и Heartbeat с 50ms съедают все ресурсы. а может быть собака в другом месте зарыта?

Конечно в другом!
Для сравнения в моей реализации CANopen на STM32F1xx (тактовая 24МГц) загрузка не превышает 20% (внутри стоит RTOS).
В среднем же загрузка около 2..3% в типовых задачах (профиль 401), если по уму настроить PDOсы самого узла.


Minti
такое ощущение, что таймер перебевает "инфинити луп" и второе при этом теряет данные.
Forger, какой тик у вашего таймера?
Forger
Цитата(Minti @ Oct 14 2011, 00:10) *
Forger, какой тик у вашего таймера?

У RTOS 1 мс, для CANopen тоже (потом будет 0,1 мс).
Таймер CANopen пока используется лишь для Hearbeat Consumer/Producer и светодиодов (по стандарту).
Все остальное - по событию.
0,1 мс нужен для правильной отработки Inhibit timer (PDO) и EMCY объектов.
Поэтому пока что для RTOS и CANopen используется один и тот же таймер.
syoma
Цитата
Heartbeat с 50ms

Чего-то очень часто. Вам действительно необходимо определение потери узла так быстро? Обычно это время от 0,5с и выше.
У меня на STM32F TIM2 крутится с шагом 8µS, а как системный таймер я настроил SysTick на 1мс. У вас помоему TIM2 неправильно настроен. CANFestival считает, что он должен увеличиваться каждые 8µS, и прерывание должно по сравнению генерироваться, а у вас немного другое получается.
Я уже выкладывал свои исходники на порт - посмотрите, как там таймер настроен.
http://permalink.gmane.org/gmane.comp.embe...canfestival/507

ПС. Хммм, только дров таймера там как-раз и нету. Вечером тогда выложу.

Цитата
0,1 мс нужен для правильной отработки Inhibit timer (PDO) и EMCY объектов.

Кстати, в CanFestival мне нравится как это дело реализовано. Благодаря 8µS шагу - все эти дела обрабатываются очень четко.
Forger
Цитата(syoma @ Oct 14 2011, 10:52) *
Кстати, в CanFestival мне нравится как это дело реализовано. Благодаря 8µS шагу - все эти дела обрабатываются очень четко.

А почему не 1мкс, не 100мкс, а именно 8? Зачем там такая точность?

Minti
Цитата(syoma @ Oct 14 2011, 08:52) *
Чего-то очень часто. Вам действительно необходимо определение потери узла так быстро? Обычно это время от 0,5с и выше. У меня на STM32F TIM2 крутится с шагом 8µS

настроил Heartbeat на 0,5с. ТIM2 настроеть с шагом меньше чем 1ms не получается, interrupt USART2 теряется.

Цитата(syoma @ Oct 14 2011, 08:52) *
, а как системный таймер я настроил SysTick на 1мс. У вас помоему TIM2 неправильно настроен. CANFestival считает, что он должен увеличиваться каждые 8µS, и прерывание должно по сравнению генерироваться, а у вас немного другое получается.

А вот тут пожалуйста поподробнее. Зачем SysTick настраевать(если что извените за нубский вопрос)? Что неправельно в настроиках TIM2?

Цитата(syoma @ Oct 14 2011, 08:52) *
ПС. Хммм, только дров таймера там как-раз и нету. Вечером тогда выложу.

Буден очень благодарен!

RTOS нету.
syoma
Вот мои текущие реализации драйверов таймера и CAN- контроллера для CANfestival и STM32F105. F103 должен быть вроде такой же.
В качестве библиотеки использовалась STM32F10x Standard Peripherals Library версия 3.5.0 с сайта ST.
Цитата
Зачем SysTick настраевать(если что извените за нубский вопрос)?

Systick я настроил на прерывание каждые 1мс чисто для своих нужд - например каждую мс входы опрашивать и т.д. в Canfestival он не учавствует.

Цитата
А почему не 1мкс, не 100мкс, а именно 8? Зачем там такая точность?

Я это дело не разрабатывал, а использовал как есть. Поэтому не знаю. Оно так работает, а мне больше и не надо.
Minti
syoma, большое спасибо за исходный текст! Есть ещё парочку вопросов

Ваша текущая реализация драйверов таймера:
CODE
...
/* Compute the prescaler value APB1 divided by two, but this is fixed by timer itself */
PrescalerValue = (uint16_t) ((SystemCoreClock) / 125000) - 1;
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 65535;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

/* Prescaler configuration */
TIM_PrescalerConfig(TIM2, PrescalerValue, TIM_PSCReloadMode_Immediate);
...

Какое значение у SystemCoreClock? 72MHz?
формула вычисления шага:
Tick := SystemCoreClock / (TIM_Prescaler+1) / TIM_Period
Как я понял TIM_Period у вас в формулу не берется. Это из-за "Output Compare Timing Mode configuration"?
Цитата
Systick я настроил на прерывание каждые 1мс чисто для своих нужд - например каждую мс входы опрашивать и т.д. в Canfestival он не учавствует.

Тогда где у вас в программе этот блок стоит:

if (canReceive(&m)) canDispatch(Obj_Data, &m);

в "инфинити луп"?


Запустил МК с вашими реализациами. USART2 interrupt работает стабильно, а вот функция ConfigureSlaveNode(тут находится CanFestival-3\examples\DS401_Master\TestMasterMicroMod.c) теряется. Обнаружил вот такой феномен: ставлю ТIМ2 на большой шаг ~2с, выполняю ConfigureSlaveNode(доходит до конца), ставлю ТIМ2 шаг обратно и колёсики начинают крутится. Можно ли решить эту проблему по другому?
syoma
SystemCoreClock - 72MHz.
Мне нужно было задать делитель так, чтобы таймер увеличивался каждые 8µs. Это получается если Prescaler=575. TIM_Period в этом случае нафиг не нужен. TIM_Period нужен только чтобы таймер проходил полный круг перед сбросом в 0.
Вы возможно путаете - каждые 8µS таймер должен увеличиваться, а не переполняться.
Короче правильность работы этого таймера проверяется легко - запускаете heartbeat generation например на 2с - и наблюдаете в любом CANанализаторе по таймстампингу насколько точно приходят сообщения. Если таймер настроен правильно, то должно быть 2+-0,001с

canDispatch стоит в основном инфинити луп, но не в функции, которая раз в 1мс вызывается. Это изза того, что за 1мс может много сообщений прийти и все их нужно успеть обработать. Если бы canDispatch только раз в мс вызывался, то сообщения терялись бы.
ConfigureSlaveNode я только сегодня тестить буду. Не ожидаю никаких проблем. Что у Вас теряется?
Minti
Цитата(syoma @ Oct 18 2011, 17:13) *
canDispatch стоит в основном инфинити луп, но не в функции, которая раз в 1мс вызывается.

Идею с SysTick'ом я теперь понял, спасибо. Вкручу для USART'а.
Цитата(syoma @ Oct 18 2011, 17:13) *
ConfigureSlaveNode я только сегодня тестить буду. Не ожидаю никаких проблем. Что у Вас теряется?

Программирую в Atollic TrueSTUDIO®/STM32 Lite, ставил в ConfigureSlaveNode breakpoints в case 1 и case 3, последний не достигался.
Потом увеличил ТIM2 Prescaler, примерно до case 16 дошёл. И так я Prescaler увеличивал пока полностью функтию не пробежал.

На днях вкручу SysTick, может этот феномен пропадёт.

PS: есть ещё один вопросик. Есть ли разниза между ними:
TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
TIM_PrescalerConfig(TIM2, PrescalerValue, TIM_PSCReloadMode_Immediate);
syoma
Посмотрел я ConfigureSlaveNode - в принципе мне эта функция совсем не нужна - я не собираюсь в словари слейвов с мастера залазить, для этого есть конфигурационные проги.
Но вроде у нее все ОК, так что ищите.
Minti
Залевши глубако в код CanFestival'я я понял, что функция canSend при "успехе" должна возвращять 0, а не 1. Хорошо это видно вот здесь:
CODE
//File: sdo.c
//function: _writeNetworkDict
//sendSDO -> return canSend
err = sendSDO(d, SDO_CLIENT, sdo);
if (err) {
MSG_ERR(0x1AD1, "SDO. Error while sending SDO to node : ", nodeId);
/* release the line */
resetSDOline(d, line);
return 0xFF;
}

Исправив это, функция ConfigureSlaveNode заработала на ура. зажимы «WAGO» включались/выключались, а вот колёса так и не крутились. Матерясь спаял "CAN-BUS" ввиде буквы Т, по концам D-Sub9(3шт.: для МК, для робота и 3й для CAN2USB-Interface, чтоб на компьютере мониторить).
Увидел следующие (ID: 1-WAGO; 3-Joystick; 4-MK (Master); A,B,C,D - 4 Мотора):
-Sync
-RPDOs от ID 1, 3, А-D (180h+NodeId)
-TPDOs от ID 1, 3 (200h+NodeId) <- ошибка А-D нету
-Heartbeat от ID 1, 3, 4, А-D

Отключил Joystick(3), запустил -> вместо него появился мотор А(см. скрин). Получается что МК только 2 TPDO обрабатывает(или успевает только 2).

Тут я вспомнил феномен с breakpont'ом. Дописал следующие чтоб отловить ТPDO с cobid 20Bh(2й мотор)
CODE
//File: pdo.c
//function: buildPDO
pdo->cob_id = (UNS16) UNS16_LE(*(UNS32*)TPDO_com->pSubindex[1].pObject & 0x7FF);
//TODO:DELETE AFTER TEST
if(pdo->cob_id == 523) //<-20Bh
prp_j = 0x00; //breakpoint line

И как следовало ожидать программа в этой строчке останавливается, а после в мониторинге появлается 20Bh один раз.
Вывод: МК обрабатывает только 2 TPDO, остальные теряются. При breakpoint'e Atollic TRUEStudio замечает что там что-то ещё есть.

Может мне кто-нибудь обяснить почему это так и как это исправить? может это "bug" в Atollic? или что-то интеррупты съедают?
Как вы вкрутили CanFestival в проэкт? как dll или просто .c/.h-фаилы импортировали? я импортировал, вот мои конфигурации:
CODE
//FILE: canfestival.h
#ifndef CANFESTIVAL_H_
#define CANFESTIVAL_H_

#include "applicfg.h"
#include "data.h"

// --------- to be called by user app ---------
void initTimer(void);
UNS8 canSend(CAN_PORT notused, Message *m);
UNS8 canChangeBaudRate(CAN_PORT port, char* baud);

#endif



//File: applicfg.h
#ifndef __APPLICFG_NONE__
#define __APPLICFG_NONE__

#include <string.h>
#include <stdio.h>

/* Integers */
#define INTEGER8 char
#define INTEGER16 short
#define INTEGER24 int
#define INTEGER32 int
#define INTEGER40 long long
#define INTEGER48 long long
#define INTEGER56 long long
#define INTEGER64 long long

/* Unsigned integers */
#define UNS8 unsigned char
#define UNS16 unsigned short
#define UNS32 unsigned int
#define UNS24 unsigned int
#define UNS40 unsigned long long
#define UNS48 unsigned long long
#define UNS56 unsigned long long
#define UNS64 unsigned long long

/* Reals */
#define REAL32 float
#define REAL64 double

#include "can.h"

/* Definition of error and warning macros */
/* -------------------------------------- */
#define MSG(...)

/* Definition of MSG_ERR */
/* --------------------- */
#define MSG_ERR(num, str, val)

/* Definition of MSG_WAR */
/* --------------------- */
#define MSG_WAR(num, str, val)

typedef void* CAN_HANDLE;
typedef void* CAN_PORT;

#endif



//File: config.h
#ifndef CONFIG_H_
#define CONFIG_H_

// Needed defines by Canfestival lib
#define MAX_CAN_BUS_ID 1
#define SDO_MAX_LENGTH_TRANSFERT 32
#define SDO_MAX_SIMULTANEOUS_TRANSFERTS 1
#define NMT_MAX_NODE_ID 128
#define SDO_TIMEOUT_MS 3000U
#define MAX_NB_TIMER 8

// CANOPEN_BIG_ENDIAN is not defined
#define CANOPEN_LITTLE_ENDIAN 1

//#define US_TO_TIMEVAL_FACTOR 8

#define REPEAT_SDO_MAX_SIMULTANEOUS_TRANSFERTS_TIMES(repeat)\
repeat
#define REPEAT_NMT_MAX_NODE_ID_TIMES(repeat)\
repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat \
repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat \
repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat \
repeat repeat repeat repeat repeat repeat repeat repeat repeat

#define EMCY_MAX_ERRORS 8
#define REPEAT_EMCY_MAX_ERRORS_TIMES(repeat)\
repeat repeat repeat repeat repeat repeat repeat repeat

#endif



//File: timerscfg.h
#ifndef __TIMERSCFG_H__
#define __TIMERSCFG_H__

/* Time unit : us */
#define TIMEVAL UNS32
#define TIMEVAL_MAX 0xFFFF

#define MS_TO_TIMEVAL(ms) (ms * 125)
#define US_TO_TIMEVAL(us) (us>>3)
#endif



//File: can_STM32.h
#ifndef CAN_STM32_H_
#define CAN_STM32_H_

#include "config.h"
//#include "can_drv.h"

// Canfestivals includes
#include "can.h"

/************************* To be called by user app ***************************/
unsigned char canInit(unsigned int bitrate);
unsigned char canSend(CAN_PORT notused, Message *m);
unsigned char canReceive(Message *m);
unsigned char canChangeBaudRate_driver( CAN_HANDLE fd, char* baud);
#endif

Minti
Forger, syoma, как вы подсоединили CanFestival к проэкту? как CanFestival-3.dll или фаилы вставили?
Minti
Привет
Ошибку нашёл месяц назад, забыл запостить тут.

в методe canSend заменил строчку

if (CAN_Transmit(CAN1,&mess)!=CAN_NO_MB)

на

while (CAN_Transmit(CAN1,&mess) == CAN_NO_MB)

return 0 //всегда


Проблема была в том, что МК генерировал 5 messages(SYNC и 5 TPDOs) сразу, а CAN-модуль имеет только 3 transmission mailboxes. Поэтому SYNC и 2 TPDOs проходили, а остальные "шли лесом". Это и объясняет появление узла А когда узел 3 был отключен. Теперь всё работает на ура.
syoma
Я так понял - это камень в мой огород? Ситуация с if сделана сознательно. Про грабли с 3-мя мейлбоксами я с самого начала знал. Прикол в том, что если будет while - и все боксы полны, прога будет ждать, пока бокс не освободится.
А при определенных условиях, например оборваной линии или отсутствии других CAN-узлов это может никогда не произойти. В итоге выша прога в этом месте мертво зависнет. Можете сами проверить выдернув CAN штекер.
Тут надо либо таймаут добавлять, либо еще что-то. Простым while не обойдетесь.
Minti
Цитата(syoma @ Feb 16 2012, 17:12) *
Я так понял - это камень в мой огород? Ситуация с if сделана сознательно. Про грабли с 3-мя мейлбоксами я с самого начала знал. Прикол в том, что если будет while - и все боксы полны, прога будет ждать, пока бокс не освободится.
А при определенных условиях, например оборваной линии или отсутствии других CAN-узлов это может никогда не произойти. В итоге выша прога в этом месте мертво зависнет. Можете сами проверить выдернув CAN штекер.
Тут надо либо таймаут добавлять, либо еще что-то. Простым while не обойдетесь.

Нет - это не камень в ваш огород. Просто про грабли с 3-мя мейлбоксами я не знал. И долго не мог понять откуда ноги ростатут: проходят только 2 TPDOs , а остальные теряются. Вот и пришла идея заменить if на while и у меня всё заработало. Кстате CAN штекер выдёргивал, робот сразу останавливался, а вот состояния микроконтроллера я не проверял. Пока даже и не знаю как проверить это.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.