Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: stm32+ FreeRtos семафоры для Ethernet и ADC
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы > FreeRTOS
Acvarif
Несколько раз перечитал мануал по FreeRTOS в части бинарных семафоров и так до конца и не понял, если имеются два прерывания (например ETH_IRQHandler и ADC_IRQHandler) то для каждого из них нужен свой бинарный семафор или бинарный семафор может быть только один на всех (или вообще только один)?

Суть задачки: Имеется готовый пример использования (FreeRTOS+LwIP) для STM32F217 http://electronix.ru/forum/index.php?showt...98347&st=30 в котором неплохо работает прием по UDP по прерываниям с использованием бинарного семафора
CODE
void ETH_IRQHandler(void)
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

/* Frame received */
if ( ETH_GetDMAFlagStatus(ETH_DMA_FLAG_R) == SET)
{
/* Give the semaphore to wakeup LwIP task */
xSemaphoreGiveFromISR( s_xSemaphore, &xHigherPriorityTaskWoken );
}

/* Clear the interrupt flags. */
/* Clear the Eth DMA Rx IT pending bits */
ETH_DMAClearITPendingBit(ETH_DMA_IT_R);
ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS);

/* Switch tasks if necessary. */
if( xHigherPriorityTaskWoken != pdFALSE )
{
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
}
}

сам таск выглядит так
void ethernetif_input( void * pvParameters )
{
struct pbuf *p;

for( ;; )
{
if (xSemaphoreTake( s_xSemaphore, emacBLOCK_TIME_WAITING_FOR_INPUT)==pdTRUE)
{
p = low_level_input( s_pxNetIf );
if (ERR_OK != s_pxNetIf->input( p, s_pxNetIf))
{
pbuf_free(p);
p=NULL;
}
}
}
}



Имеется необходимость передавать по Ethernet выборки ADC1 (каждую выборку или накапливать небольшой буфер пока неясно)
Наверняка выгодно для этой цели создать task в заторможенном состоянии которая будет разблокироваться прерыванием
по ADC1
Код
void ADC_IRQHandler(void)
{
  portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

  /* ADC1 received */
  if (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == SET)
  {
    /* Give the semaphore to wakeup ADC1 task */
    xSemaphoreGiveFromISR( s_xSemaphore, &xHigherPriorityTaskWoken );  
  }
    
  /* Clear the interrupt flag. */
  ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
}
и затем уже в таске, которая будет разбужена семафором передавать UDP пакет с выборкой.

Вот и непонятно можно ли использовать один и тот же бинарный семафор для разных тасков? Если да то как будет выглядеть таск который будет выполнен по прерыванию ADC1? Может так?
CODE
void AdcUdpSend(void * pvParameters)
{
struct netconn *connn;
struct netbuf *buf1;
struct ip_addr addr;
static char text[2];

// create a new connection
connn = netconn_new(NETCONN_UDP);
// set up the IP address of the remote host
IP4_ADDR(&addr, DIP_ADDR0, DIP_ADDR1, DIP_ADDR2, DIP_ADDR3);
// connect the connection to the remote host
netconn_connect(connn, &addr, 7);
// create a new netbuf
buf1 = netbuf_new();

while(1)
{
test = xnetif.ip_addr.addr;
//check if IP address assigned
if (test !=0)
{
for( ;; )
{
if (xSemaphoreTake( s_xSemaphore, emacBLOCK_TIME_WAITING_FOR_INPUT)==pdTRUE)
{
text[1] = ADC1ConvertedValue;
text[0] = ADC1ConvertedValue >> 8;

netbuf_ref(buf1, text, sizeof(text));
// послать Eth пакет
netconn_send(connn, buf1);
}
}
}
}
}

В этом случае
emacBLOCK_TIME_WAITING_FOR_INPUT определен как
#define emacBLOCK_TIME_WAITING_FOR_INPUT ( ( portTickType ) 100 )
(оставил так же как и для таска приема Eth пакетов)
Как лучше определить блокировку для этой задачи - задачи передачи Eth пакетов по прерыванию ADC1?
Поскольку прерывания от ADC будут приходить довольно часто (~ 10 мкс) не будет ли это тормозом для Ethernet приемника и вобще для всего приложения?
unkier
насколько я понял когда разбирался с lwip, s_xSemaphore отпускается в прерывании от сетевухи когда пришли данные, стек ловит этот семафор и принимает пакет. какой смысл отпускать этот семафор в прерывании от adc ?
Acvarif
Цитата(unkier @ Feb 7 2012, 06:19) *
насколько я понял когда разбирался с lwip, s_xSemaphore отпускается в прерывании от сетевухи когда пришли данные, стек ловит этот семафор и принимает пакет. какой смысл отпускать этот семафор в прерывании от adc ?

Так оно и есть - s_xSemaphore отдает прерывание по приему Ethernet. Берет его таск стека, который до того находится в заторможенном состоянии.
Согласен нет смысла этот семафор использовать и для ADC (хотя хочется)
А как тогда быть?

Прерывания ADC идут через ~ 10 мкс. Они должны вызывать таск который будет засовывать данные в некоторый буфер. При достижении нужного количества данных они должны быть выплюнуты по Lwip UDP на комп.
Если не использовать семафор от lwip то как быть, как технически постоить такую операцию не мешая стеку ловить пакеты на прием и в то же время по прерываниям ADC накапливать данные для пакета на передачус последующей их передачей?
unkier
несколько буферов для данных. прерывание по ADC пишет в буфер до тех пор пока не наберет нужное количество, после этого переключает буфер в который она будет писать и отпускает семафор (отдельный). задачка (отдельная) висит на ожидании семафора (который отпускается в прерывании от ADC) берет данные из буфера и посылает.
Acvarif
Цитата(unkier @ Feb 7 2012, 14:41) *
несколько буферов для данных. прерывание по ADC пишет в буфер до тех пор пока не наберет нужное количество, после этого переключает буфер в который она будет писать и отпускает семафор (отдельный). задачка (отдельная) висит на ожидании семафора (который отпускается в прерывании от ADC) берет данные из буфера и посылает.


Спасибо.
Понял.
Должно получиться.
Для уточнения - никак не могу уяснить действия с семафорами
Как я понимаю.
1. Семафор на прием LwIP остается сам по себе как и был. Я его не трогаю.
2. Для отправки данных по LwIP создаю отдельную задачку и отдельный семафор, который сработает тогда, когда на очередном прерывании ADC, заполнится нужным количеством данных один из буферов. Правильно ли я понял?
Сколько бинарных семафоров можно создавать в одной и той же программе, один или (грубо говоря) можно для каждой задачки, которая например должна срабатывать по какому-либо прерыванию, свой семафор?
unkier
понял вроде правильно. количество семафоров (как впрочем и любых других ресурсов) определяется только возможностями конкретного железа и настройками оси.
kan35
Цитата(unkier @ Feb 7 2012, 18:37) *
понял вроде правильно. количество семафоров (как впрочем и любых других ресурсов) определяется только возможностями конкретного железа и настройками оси.

Можно свести количество семафоров к тому - сколько позволяет ОЗУ.

По поводу "как сделать": я советую сделать работу ADC по DMA в циклическом режиме. И при заполнении буфера на 50% отправлять (из прерывания HT DMA) в queue первую половину буфера DMA, а при наполнении до 100% (из TC DMA) - вторую, затем опять первую половину и так далее. Размер пакета по умолчанию у TCP-ethernet - 1500 байт, по UDP - не знаю. Вот и прикидывайте, чтобы одна посылка примерно столько занимала.
При этом можно сделать queue на несколько буферов, чтобы избежать переполнения и потерь данных.
Acvarif
Цитата(kan35 @ Feb 8 2012, 07:42) *
Можно свести количество семафоров к тому - сколько позволяет ОЗУ.

По поводу "как сделать": я советую сделать работу ADC по DMA в циклическом режиме. И при заполнении буфера на 50% отправлять (из прерывания HT DMA) в queue первую половину буфера DMA, а при наполнении до 100% (из TC DMA) - вторую, затем опять первую половину и так далее. Размер пакета по умолчанию у TCP-ethernet - 1500 байт, по UDP - не знаю. Вот и прикидывайте, чтобы одна посылка примерно столько занимала.
При этом можно сделать queue на несколько буферов, чтобы избежать переполнения и потерь данных.


Спасибо. Про DMA тоже была мысль. Если хоть как-то заработает на двух буферах попробую и с DMA.
Пока пытаюсь сделать сделать на двух буферах. Типа так
Прерывания по ADC1 (~ 7 мкс)
CODE

extern xSemaphoreHandle d_xSemaphore;

extern unsigned char AdcBuff1[100];
extern unsigned char AdcBuff2[100];
extern uint8_t AdcIntCount = 0;
extern uint8_t BuffIntCount = 0;
extern __IO uint16_t ADC1ConvertedValue;

void ADC_IRQHandler(void)
{
STM_EVAL_LEDToggle(LED2);

// Заполнение AdcBuff1
if(BuffIntCount == 0)
{
if(AdcIntCount%2 == 0)
AdcBuff1[AdcIntCount] = ADC1ConvertedValue;
else
AdcBuff1[AdcIntCount] = ADC1ConvertedValue >> 8;
// Инкремент счетчика байт
AdcIntCount++;
}
// Заполнение AdcBuff1
else if (BuffIntCount == 1)
{
if(AdcIntCount%2 == 0)
AdcBuff2[AdcIntCount] = ADC1ConvertedValue;
else
AdcBuff2[AdcIntCount] = ADC1ConvertedValue >> 8;
// Инкремент счетчика байт
AdcIntCount++;
}

if(AdcIntCount == 100)
{
// Обнуление счетчика байт
AdcIntCount = 0;
// Смена признака буфера
if(BuffIntCount == 0) BuffIntCount = 1;
else BuffIntCount = 0;

portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

/* ADC1 received */
if (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == SET)
{
/* Give the semaphore to wakeup ADC task */
xSemaphoreGiveFromISR( d_xSemaphore, &xHigherPriorityTaskWoken );
}

/* Clear the interrupt flag. */
ADC_ClearFlag(ADC1, ADC_FLAG_EOC);

/* Switch tasks if necessary. */
if( xHigherPriorityTaskWoken != pdFALSE )
{
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
}
}
}

А это задачка которая должна получать семафор d_xSemaphore и выплевывать по UDP 100 байт данных из очередного буфера
CODE
#define UDPADC_THREAD_PRIO ( tskIDLE_PRIORITY + 4 )

static struct netconn *conn1;
static struct netbuf *buf1;
static struct ip_addr addr1;
//static unsigned short port;

unsigned char AdcBuff1[100];
unsigned char AdcBuff2[100];
extern uint8_t BuffIntCount;

xSemaphoreHandle d_xSemaphore;

/*-----------------------------------------------------------------------------------*/
static void udpadc_thread(void *arg)
{
LWIP_UNUSED_ARG(arg);

// create a new connection
conn1 = netconn_new(NETCONN_UDP);
// set up the IP address of the remote host
IP4_ADDR(&addr1, DIP_ADDR0, DIP_ADDR1, DIP_ADDR2, DIP_ADDR3);
// connect the connection to the remote host
netconn_connect(conn1, &addr1, 7);
// create a new netbuf
buf1 = netbuf_new();

for( ;; )
{

// vTaskDelay(1000);
STM_EVAL_LEDToggle(LED2);

if (xSemaphoreTake( d_xSemaphore, ( portTickType ) 100)==pdTRUE)
// if (xSemaphoreTake( d_xSemaphore, portMAX_DELAY )==pdTRUE)
{
if(BuffIntCount == 1)
netbuf_ref(buf1, AdcBuff1, sizeof(AdcBuff1));
else
netbuf_ref(buf1, AdcBuff2, sizeof(AdcBuff2));
// послать Eth пакет
netconn_send(conn1, buf1);
netbuf_delete(buf1);
}
}
}

/*-----------------------------------------------------------------------------------*/
void udpadc_init(void)
{
sys_thread_new("udpadc_thread", udpadc_thread, NULL, DEFAULT_THREAD_STACKSIZE,UDPADC_THREAD_PRIO );
}


Но реально в железе такое впечатление, что на этой задачке все виснет.
Гляньте please, кому не лень, где тут хомут.
Acvarif
Цитата(Acvarif @ Feb 8 2012, 12:55) *
Но реально в железе такое впечатление, что на этой задачке все виснет.
Гляньте please, кому не лень, где тут хомут.


Упустил само создание семафора
Код
  /* create binary semaphore used for informing ethernetif of frame reception */
  if (d_xSemaphore == NULL)
  {
    d_xSemaphore= xSemaphoreCreateCounting(20,0);
  }

Теперь задачка запускается но данные по UDP не идут.
Не идут потому, что не входит сюда
Код
...................
    if (xSemaphoreTake( d_xSemaphore, ( portTickType ) 100)==pdTRUE)
    {
      STM_EVAL_LEDToggle(LED2);
..................
..передача по UDP
    }

Здесь совсем не понятно.
Прерывание должно отдать семафор, а задачка взять его.
Получается одно из двух или прерывание семафор не дает или задачка его не берет (на этом предложении дурные мысли в голову лезут...).
В чем же дело?
kan35
JTAG/SWD что показывает? - доходит до части где взводится семафор?
Acvarif
Цитата(kan35 @ Feb 8 2012, 21:15) *
JTAG/SWD что показывает? - доходит до части где взводится семафор?

Спасибо.
Запустил через SWD с точками останова и только тогда увидел ошибки.
Функция прерывание не взводила семафор. Вот (предварительно) верный вариант
CODE
void ADC_IRQHandler(void)
{

// Заполнение AdcBuff1
if(BuffIntCount == 0)
{
if(AdcIntCount%2 == 0)
AdcBuff1[AdcIntCount] = ADC1ConvertedValue;
else
AdcBuff1[AdcIntCount] = ADC1ConvertedValue >> 8;
// Инкремент счетчика байт
AdcIntCount++;
}
// Заполнение AdcBuff1
else if (BuffIntCount == 1)
{
if(AdcIntCount%2 == 0)
AdcBuff2[AdcIntCount] = ADC1ConvertedValue;
else
AdcBuff2[AdcIntCount] = ADC1ConvertedValue >> 8;
// Инкремент счетчика байт
AdcIntCount++;
}
// Если заполнился один из буферов
if(AdcIntCount == 100)
{
// Обнуление счетчика байт
AdcIntCount = 0;
// Смена признака буфера
if(BuffIntCount == 0) BuffIntCount = 1;
else BuffIntCount = 0;
// установка xHigherPriorityTaskWoken в pdFALSE
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

/* ADC1 received */
// if (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == SET)
// {
/* Give the semaphore to wakeup ADC task */
xSemaphoreGiveFromISR( d_xSemaphore, &xHigherPriorityTaskWoken );
// }

/* Switch tasks if necessary. */
if( xHigherPriorityTaskWoken != pdFALSE )
{
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
}
}

/* Clear the interrupt flag. */
ADC_ClearFlag(ADC1, ADC_FLAG_EOC);

}

Немного не понятно назначение этого
Код
    /* Switch tasks if necessary. */    
    if( xHigherPriorityTaskWoken != pdFALSE )
    {
      portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
    }

и в самой задачке неясно как лучше делать так (периодически разбуживать задачку)
Код
     if (xSemaphoreTake( d_xSemaphore, (250)) == pdPASS)
или так (не будить до прихода семафора)
Код
      if (xSemaphoreTake( d_xSemaphore, portMAX_DELAY )==pdTRUE)

LightElf
QUOTE
Немного не понятно назначение этого
CODE
    /* Switch tasks if necessary. */    
    if( xHigherPriorityTaskWoken != pdFALSE )
    {
      portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
    }

Это нужно, чтобы таск пробудился немедленно, а не ждал до очередного прерывания системного таймера. В общем, все функи с приставкой fromISR не переключают задачи непосредственно, а только меняют внутреннее состояние RTOS. Непосредственно переключение задачи происходит в макросе portEND_SWITCHING_ISR.

QUOTE
и в самой задачке неясно как лучше делать так (периодически разбуживать задачку)
CODE
     if (xSemaphoreTake( d_xSemaphore, (250)) == pdPASS)
или так (не будить до прихода семафора)
CODE
      if (xSemaphoreTake( d_xSemaphore, portMAX_DELAY )==pdTRUE)

В первом варианте задача просыпается, когда взведен семафор (и функция xSemaphoreTake возвращает pdTRUE) или когда пройдут 250 тиков (и возвращается pdFAIL). Во втором варианте задача просыпается только по появлению семафора.
Acvarif
Цитата(LightElf @ Feb 9 2012, 14:44) *
Это нужно, чтобы таск пробудился немедленно, а не ждал до очередного прерывания системного таймера. В общем, все функи с приставкой fromISR не переключают задачи непосредственно, а только меняют внутреннее состояние RTOS. Непосредственно переключение задачи происходит в макросе portEND_SWITCHING_ISR.

В первом варианте задача просыпается, когда взведен семафор (и функция xSemaphoreTake возвращает pdTRUE) или когда пройдут 250 тиков (и возвращается pdFAIL). Во втором варианте задача просыпается только по появлению семафора.


Большое спасибо. Стало немного яснее.
Получается, что в моем случае (код ниже) макрос portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
не выполнится поскольку xHigherPriorityTaskWoken = pdFALSE?
А значит таск будет разбужен только по системному тику+взведенный семафор d_xSemaphore
Код
    portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
  
      /* Give the semaphore to wakeup ADC task */
      xSemaphoreGiveFromISR( d_xSemaphore, &xHigherPriorityTaskWoken );  
    
    /* Switch tasks if necessary. */    
    if( xHigherPriorityTaskWoken != pdFALSE )
    {
      portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
    }

Хотя в мануале по FreeRTOS говорится, что xSemaphoreGiveFromISR( d_xSemaphore, &xHigherPriorityTaskWoken );
может установить xHigherPriorityTaskWoken в pdTRUE но для этого приоритет таска куда передается семафор должен быть высоким.
Вот каким, по сравнению с другими тасками, тут я не врубился.
Bass
Цитата(Acvarif @ Feb 9 2012, 18:52) *
Большое спасибо. Стало немного яснее.
Получается, что в моем случае (код ниже) макрос portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
не выполнится поскольку xHigherPriorityTaskWoken = pdFALSE?
А значит таск будет разбужен только по системному тику+взведенный семафор d_xSemaphore
Код
    portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
  
      /* Give the semaphore to wakeup ADC task */
      xSemaphoreGiveFromISR( d_xSemaphore, &xHigherPriorityTaskWoken );  
    
    /* Switch tasks if necessary. */    
    if( xHigherPriorityTaskWoken != pdFALSE )
    {
      portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
    }

Хотя в мануале по FreeRTOS говорится, что xSemaphoreGiveFromISR( d_xSemaphore, &xHigherPriorityTaskWoken );
может установить xHigherPriorityTaskWoken в pdTRUE но для этого приоритет таска куда передается семафор должен быть высоким.
Вот каким, по сравнению с другими тасками, тут я не врубился.

Если приоритет задачи, которой ты пытаешься выдать семафор будет выше той, которую прервали прерыванием, то в xSemaphoreGiveFromISR будет установлена xHigherPriorityTaskWoken в pdTRUE, и тогда portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ) при выходе из прерывания попадет не в прерванную задачу, а в задачу с более высоки приоритетом.
Вроде так.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.