Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: STM8S. Пост печали и безысходности.
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Все остальные микроконтроллеры
Dark Simpson
Всем добрый вечер.
На днях столкнулся с такой проблемой — никак не могу разрулить, почему ничерта не работает.
Есть проект, собирается в IAR 1.30.1, библиотеки ST 2.1.0. Помимо всего прочего, в проекте используется софт-уарт и разносольный таймер, с которыми и возникает какая-то проблема.
Разносольный таймер, TIM4, настроен на генерацию прерывания каждую миллисекунду вот так:
Код
  // Timer 4 as system tick, f = 1 kHz
  CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER4, ENABLE);
  TIM4_TimeBaseInit(TIM4_PRESCALER_128, 124);
  TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);
  TIM4_Cmd(ENABLE);

и внутри него крутятся разные софт-таймеры и мигалки светодиодами, типа:
Код
INTERRUPT_HANDLER(TIM4_UPD_OVF_IRQHandler, 23)
{
  if (TIM4_GetFlagStatus(TIM4_FLAG_UPDATE))
  {
    // Process Delay1
    if (d1p > 0) d1p--;

    // Process SWUART RX delay counter
    if (dswrx > 0) dswrx--;

    IndicationTick();

    // Clear flar (rearm interrupt)
    TIM4_ClearFlag(TIM4_FLAG_UPDATE);
  }
}

Внутри IndicationTick(), собственно, мигание светодиодами и происходит, если необходимо.
Все это вполне себе нормально работает, казалось бы.

Софт-уарт устроен так. Используется два прерывания. Одно — прерывание от изменения состояния порта, ловит стартовый бит на прием. Другое — прерывание от TIM3, в котором происходит передача и прием. Порт работает полудуплексно (т.е. передаем команду — получаем ответ).
Вот код его модуля:
CODE
#include "swuart.h"

#include "timing.h"

#define SWUART_TXPORT GPIOD
#define SWUART_TXPIN GPIO_PIN_5
#define SWUART_RXPORT GPIOD
#define SWUART_RXPIN GPIO_PIN_6

// User must ensure calling service interrupt routine at exact intervals
// of 1 bit of the dedicated baud rate
#define BAUD 1667
#define HALF_BAUD 833

#define test_rx (SWUART_RXPORT->IDR & SWUART_RXPIN)
#define set_tx (SWUART_TXPORT->ODR |= SWUART_TXPIN)
#define clr_tx (SWUART_TXPORT->ODR &= ~SWUART_TXPIN)

#define transmit_in_progress 0x80 // in progress mark
#define receive_in_progress 0x08 // in progress mark
#define receive_error 0x04
#define receive_buffer_overflow 0x02
#define receive_buffer_full 0x01

#define test_status(a) (swu_sts & a)
#define set_status(a) (swu_sts |= a)
#define clr_status(a) (swu_sts &= ~a)

#define enable_rx_int {\
SWUART_RXPORT->CR2 |= (uint8_t)SWUART_RXPIN;\
}

#define disable_rx_int {\
SWUART_RXPORT->CR2 &= (uint8_t)(~(SWUART_RXPIN));\
}

const u8 MSK_TAB[8]= { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };

u8 rx_bit, // counter of received bits
tx_bit, // counter of transmited bits
rx_buff, // receive byte buffer
rx_data, // received byte register
tx_data, // transmited byte register
swu_sts; // SWUART status register

u8 swutxbuf[64];
u8 swutxlen, swutxpos = 0;

u8 swurxbuf[64];
u8 swurxpos = 0;

u8 swuart_tx_u8(u8 cool.gif;
u8 swuart_rx_u8(u8 *cool.gif;
void swuart_txrx_timing(void);
void swuart_rx_start(void);

/* ------------- Interrupts ---------------- */

INTERRUPT_HANDLER(TIM3_UPD_OVF_BRK_IRQHandler, 15)
{
if (TIM3_GetFlagStatus(TIM3_FLAG_UPDATE))
{
swuart_txrx_timing();

TIM3_ClearFlag(TIM3_FLAG_UPDATE);
}
}

INTERRUPT_HANDLER(EXTI_PORTD_IRQHandler, 6)
{
// There is only one interrupt at Port D — from SW UART RX
swuart_rx_start();
}

/* ------------- Exported ------------------ */

void SwUInit(void)
{
GPIO_Init(SWUART_TXPORT, SWUART_TXPIN, GPIO_MODE_OUT_PP_HIGH_SLOW);
GPIO_Init(SWUART_RXPORT, SWUART_RXPIN, GPIO_MODE_IN_PU_IT);

EXTI_SetExtIntSensitivity(EXTI_PORT_GPIOD, EXTI_SENSITIVITY_FALL_ONLY);

TIM3_TimeBaseInit(TIM3_PRESCALER_1, BAUD);
TIM3_ITConfig(TIM3_IT_UPDATE, ENABLE);
TIM3_UpdateRequestConfig(TIM3_UPDATESOURCE_REGULAR);
TIM3_Cmd(ENABLE);
}

u8 SwUTx(u8 len, u16 to)
{
u8 i = 0;
for (; i < len; i++)
{
dswrx = to;
while (swuart_tx_u8(swutxbuf[i]) == FALSE && dswrx > 0) {}
if (dswrx == 0)
break;
}
return i;
}

u8 SwURx(u16 to)
{
swurxpos = 0;
dswrx = to;
while (1)
{
while (swuart_rx_u8(&swurxbuf[swurxpos]) == FALSE && dswrx > 0) {}
if (dswrx == 0)
return swurxpos;
swurxpos++;
dswrx = 10;
}
}

/* ------------- Private ------------------- */

u8 swuart_tx_u8(u8 cool.gif
{
if (!test_status(transmit_in_progress))
{
tx_data = b; // YES - initiate sending procedure
tx_bit = 0;
set_status(transmit_in_progress);
return(TRUE);
}
else
return(FALSE); // NO - no action (transmition in progress)
}

u8 swuart_rx_u8(u8 *cool.gif
{
u8 res;
if (test_status(receive_buffer_full))
{
*b = rx_data;
res = swu_sts & 0x0F; // return only rx part of status
swu_sts &= ~0x0F; // clear all rx status bits
//
TIM4_Cmd(ENABLE); // WTF? Why?
//
enable_rx_int; // enable rx interrupt !!!
return(res);
}
else
return(FALSE);
}

void swuart_rx_start(void)
{
if (!test_status(receive_in_progress) & !test_status(transmit_in_progress))
{
disable_rx_int; // Disable GPIO interrupt
//
TIM4_Cmd(DISABLE); // WTF? Why?
//
TIM3_Cmd(DISABLE);
TIM3_SetCounter(HALF_BAUD); // Set timer 3 counter to half-baud
TIM3_Cmd(ENABLE);
rx_bit = 0;
set_status(receive_in_progress);
}
}

void swuart_txrx_timing(void)
{

if (test_status(transmit_in_progress)) // transmission is in progres now
{
switch (tx_bit) // begin of bit transmition
{
case 0:
clr_tx; //start bit transmition
break;
case 9:
set_tx; //stop bit(s) transmition
break;
case 10:
clr_status(transmit_in_progress);
break;
default:
if (tx_data & MSK_TAB[tx_bit-1])
set_tx; // data bits
else
clr_tx; // transmition
};
tx_bit++; // next bit to transmit
}

//

if (test_status(receive_in_progress)) // reception is in progres now
{
u8 rx_samp = test_rx;
if (rx_bit == 0) // start bit!
{
if (rx_samp == 0) // correctly received, continue
{
rx_bit = 1;
rx_buff = 0;
}
else // ? noise in start bit, find next one
{
enable_rx_int;
clr_status(receive_in_progress);
}
}
else
{
if (rx_bit <= 8) // bit <= 8, receive data bits
{
if (rx_samp)
rx_buff |= MSK_TAB[rx_bit-1]; // set one in place if received one
rx_bit++;
}
else // bit > 8, receive stop bit
{
if (rx_samp) // valid stop bit
{
if (!test_status(receive_buffer_full)) // end of receive
{
rx_data = rx_buff; // new byte in buffer
set_status(receive_buffer_full);
}
else
set_status(receive_buffer_overflow); // data overflow!
}
else
{
rx_data = 0x00;
set_status(receive_error);
}
enable_rx_int; // wait next start bit
clr_status(receive_in_progress);
}
}
}

}

Софт-уарт, сам по себе, вроде-бы тоже работает без сбоев. А вот дальше — веселие.
Когда софт-уарт и разносольный таймер начинают работать вместе, то при приеме уарта возникает куча ошибок и сбоев. Причем, опытным путем было выяснено, что чем больше и дольше выполняется обработчик прерывания разносольного таймера, тем больше сбоев возникает при работе уарта.
Далее, подумал, что видимо уарту мешает длинношеее прерывание TIM4. Ну может же быть такое. По этому поводу решил снизить его (TIM4) приоритет вот таким образом:
Код
  // Disable interrupts
  __disable_interrupt();

  // Deinit all GPIOs !!!
  DeInitGPIO();

  // Init indication
  InitIndication();
  // Init timing subsys
  InitTiming();
  // Init SW UART
  SwUInit();
  // Init config subsys
  InitConfig();
  // Init wiegand output
  InitWiegand();

  // Configure interrupt priorities
  ITC_SetSoftwarePriority(ITC_IRQ_TIM4_OVF, ITC_PRIORITYLEVEL_1);
  //ITC_SetSoftwarePriority(ITC_IRQ_TIM3_OVF, ITC_PRIORITYLEVEL_3);
  //ITC_SetSoftwarePriority(ITC_IRQ_PORTD, ITC_PRIORITYLEVEL_2);

  // Enable interrupts
  __enable_interrupt();

И нифига не помогло! Причем, ошибок и сбоев стало поменьше, но они не исчезли. Но КАК? Ведь с пониженным приоритетом прерывание от TIM4 никак не должно пагубно влиять на прерывания софт-уарта?
В результате, путем долгих экспериментов, прикрутил костыль, который вы можете видеть в коде софт-уарта. Он отключает прерывание от TIM4 на время приема символа и потом включает его обратно. В таком виде все работает.
Но что происходит я никак не могу понять.
Уже 2 дня бьюсь, и ничего... Может чей зоркий глаз или опыт поможет? Буду очень-очень признателен!
kovigor
Цитата(Dark Simpson @ Oct 16 2012, 20:59) *
Уже 2 дня бьюсь, и ничего... Может чей зоркий глаз или опыт поможет? Буду очень-очень признателен!

Была очень похожая проблема, но на другом МК. Два обработчика прерывания, один от Soft-UART, второй еще от чего-то. Первый обработчик терял данные, если в процесс приема байта вклинивался второй обработчик. Проблема была решена отказом от обоих обработчиков вообще и написанием одного обработчика от таймера, который вызывался достаточно часто и для того, чтобы не терялись принимаемые по UART данные, и для того, чтобы обслуживался тот процесс, ради которого в предыдущей реализации использовался второй обработчик прерывания. Благо, моя задача вполне допускала такой подход ...
Dark Simpson
Цитата(kovigor @ Oct 16 2012, 23:05) *
Проблема была решена отказом от обоих обработчиков вообще и написанием одного обработчика от таймера, который вызывался достаточно часто и для того, чтобы не терялись принимаемые по UART данные, и для того, чтобы обслуживался тот процесс, ради которого в предыдущей реализации использовался второй обработчик прерывания. Благо, моя задача вполне допускала такой подход ...


Увы и ах, есть подозрение, что на все это и еще кучу всего остального банально не хватит процессорного времени sad.gif
Да и дело-то даже не в этом. Просто хочется понять что за мистика происходит... Или же все дело в кривых руках? Но тогда где именно?
scifi
Цитата(Dark Simpson @ Oct 16 2012, 21:59) *
И нифига не помогло! Причем, ошибок и сбоев стало поменьше, но они не исчезли. Но КАК? Ведь с пониженным приоритетом прерывание от TIM4 никак не должно пагубно влиять на прерывания софт-уарта?

Предлагаю независимо подтвердить, что действительно включается вытесняющий режим прерываний. Например, так:
CODE
volatile char tim4_flag;
int port_count, tim3_count;

void port_isr(void)
{
if (tim4_flag)
{
port_count++;
}
}

void tim3_isr(void)
{
if (tim4_flag)
{
tim3_count++;
}
}

void tim4_isr(void)
{
tim4_flag = 1;
...
tim4_flag = 0;
}

Погонять программу и посмотреть на счётчики port_count, tim3_count. Если счётчики содержат 0, то вытеснения не было. Причём погонять при разных настройках приоритетов прерываний.
Dark Simpson
Цитата(scifi @ Oct 17 2012, 11:57) *
Предлагаю независимо подтвердить, что действительно включается вытесняющий режим прерываний.


Спасибо, хорошая, годная мысоль. Вечером попробую. Хотя, как мне кажется (и насколько я помню, вроде-бы, по колл-стэку), там вытеснение все-таки вытесняет. Тем более, заметна разница в поведении программы. Но на всякий случай проверю. Не помешает, конечно же!
Lagman
Я делал проект на stm8s105 (платка DISCOVERY), а потом перенес на stm8s003 (VLDISCOVERY) и тоже натолкнулся на подобную проблему, просто поставил запрет на прерывание в критических местах и все опять заработало, сильно разбираться не стал.
Можно еще в errate посмотреть.
Dark Simpson
Все. Вроде-бы решил, теперь работает без сбоев и логично себя ведет при смене приоритетов.
Я так до конца и не понял, что в сонно-пивном состоянии сделал, но дернуло меня переписать процедуры приема для использования с output compare. Заодно как бонус получил возможность полного дуплекса, т.к. теперь передача идет по прерыванию таймера, а прием по прерыванию с пина и дальше по прерыванию с output compare, таким образом они и получаются независимы друг от друга.

Вот код, может быть кому пригодится:

Цэ:
CODE
#include "swuart.h"

#include "timing.h"

#define SWUART_TXPORT GPIOD
#define SWUART_TXPIN GPIO_PIN_5
#define SWUART_RXPORT GPIOD
#define SWUART_RXPIN GPIO_PIN_6

// User must ensure calling service interrupt routine at exact intervals
// of 1 bit of the dedicated baud rate
#define BAUD 1667
#define HALF_BAUD 833

#define test_rx (SWUART_RXPORT->IDR & SWUART_RXPIN)
#define set_tx (SWUART_TXPORT->ODR |= SWUART_TXPIN)
#define clr_tx (SWUART_TXPORT->ODR &= ~SWUART_TXPIN)

#define transmit_in_progress 0x80 // in progress mark
#define receive_in_progress 0x08 // in progress mark
#define receive_error 0x04
#define receive_buffer_overflow 0x02
#define receive_buffer_full 0x01

#define test_status(a) (swu_sts & a)
#define set_status(a) (swu_sts |= a)
#define clr_status(a) (swu_sts &= ~a)

#define enable_rxpin_int {\
SWUART_RXPORT->CR2 |= (uint8_t)SWUART_RXPIN;\
}

#define disable_rxpin_int {\
SWUART_RXPORT->CR2 &= (uint8_t)(~(SWUART_RXPIN));\
}

#define enable_rxoc_int {\
TIM3->IER |= (uint8_t)TIM3_IT_CC1;\
}

#define disable_rxoc_int {\
TIM3->IER &= (uint8_t)(~(TIM3_IT_CC1));\
}

const u8 MSK_TAB[8]= { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };

u8 rx_bit, // counter of received bits
tx_bit, // counter of transmited bits
rx_buff, // receive byte buffer
rx_data, // received byte register
tx_data, // transmited byte register
swu_sts; // SWUART status register

u8 swutxbuf[64];
u8 swutxlen, swutxpos = 0;

u8 swurxbuf[64];
u8 swurxpos = 0;

u8 swuart_tx_u8(u8 cool.gif;
u8 swuart_rx_u8(u8 *cool.gif;
void swuart_tx_timing(void);
void swuart_rx_start(void);
void swuart_rx_timing(void);

/* ------------- Interrupts ---------------- */

INTERRUPT_HANDLER(TIM3_UPD_OVF_BRK_IRQHandler, 15)
{
if (TIM3_GetFlagStatus(TIM3_FLAG_UPDATE))
{
swuart_tx_timing();

TIM3_ClearFlag(TIM3_FLAG_UPDATE);
}
}

INTERRUPT_HANDLER(EXTI_PORTD_IRQHandler, 6)
{
// There is only one interrupt at Port D — from SW UART RX
swuart_rx_start();
}

INTERRUPT_HANDLER(TIM3_CAP_COM_IRQHandler, 16)
{
if (TIM3_GetFlagStatus(TIM3_FLAG_CC1))
{
swuart_rx_timing();

TIM3_ClearFlag(TIM3_FLAG_CC1);
}
}

/* ------------- Exported ------------------ */

void SwUInit(void)
{
GPIO_Init(SWUART_TXPORT, SWUART_TXPIN, GPIO_MODE_OUT_PP_HIGH_SLOW);
GPIO_Init(SWUART_RXPORT, SWUART_RXPIN, GPIO_MODE_IN_PU_IT);

EXTI_SetExtIntSensitivity(EXTI_PORT_GPIOD, EXTI_SENSITIVITY_FALL_ONLY);

TIM3_TimeBaseInit(TIM3_PRESCALER_1, BAUD);
TIM3_ITConfig(TIM3_IT_UPDATE, ENABLE);
TIM3_UpdateRequestConfig(TIM3_UPDATESOURCE_REGULAR);
TIM3_OC1Init(TIM3_OCMODE_TIMING, TIM3_OUTPUTSTATE_DISABLE, 0, TIM3_OCPOLARITY_LOW); // RX OC
TIM3_Cmd(ENABLE);
}

u8 SwUTx(u8 len, u16 to)
{
u8 i = 0;
for (; i < len; i++)
{
dswrx = to;
while (swuart_tx_u8(swutxbuf[i]) == FALSE && dswrx > 0) {}
if (dswrx == 0)
break;
}
return i;
}

u8 SwURx(u16 to)
{
swurxpos = 0;
dswrx = to;
while (1)
{
while (swuart_rx_u8(&swurxbuf[swurxpos]) == FALSE && dswrx > 0) {}
if (dswrx == 0)
return swurxpos;
swurxpos++;
dswrx = 10;
}
}

/* ------------- Private ------------------- */

u8 swuart_tx_u8(u8 cool.gif
{
if (!test_status(transmit_in_progress))
{
tx_data = b;
tx_bit = 0;
set_status(transmit_in_progress);
return(TRUE);
}
else
return(FALSE);
}

u8 swuart_rx_u8(u8 *cool.gif
{
u8 res;
if (test_status(receive_buffer_full))
{
*b = rx_data;
res = swu_sts & 0x0F; // return only rx part of status
clr_status(receive_error);
clr_status(receive_buffer_full);
clr_status(receive_buffer_overflow);
return(res);
}
else
return(FALSE);
}

void swuart_rx_start(void)
{
if (!test_status(receive_in_progress))
{
// Disable GPIO interrupt
disable_rxpin_int;
// Init reception, set "receive in progress" status
rx_bit = 0;
set_status(receive_in_progress);
// Configure output compare point
uint16_t cnt = TIM3_GetCounter();
TIM3_SetCompare1((BAUD-cnt > HALF_BAUD) ? cnt+HALF_BAUD : cnt-HALF_BAUD);
// Clear false pending OC interrupt (if exist)
TIM3_ClearFlag(TIM3_FLAG_CC1);
// Start output compare interrupts
enable_rxoc_int;
}
}

void swuart_tx_timing(void)
{
if (test_status(transmit_in_progress))
{
switch (tx_bit)
{
case 0:
clr_tx;
break;
case 9:
set_tx;
break;
case 10:
clr_status(transmit_in_progress);
break;
default:
if (tx_data & MSK_TAB[tx_bit-1])
set_tx;
else
clr_tx;
};
tx_bit++;
}
}

void swuart_rx_timing(void)
{
if (test_status(receive_in_progress))
{
u8 rx_samp = test_rx;
if (rx_bit == 0) // start bit!
{
if (rx_samp == 0) // correctly received, continue
{
rx_bit = 1;
rx_buff = 0;
}
else // noise in start bit, find next one
{
disable_rxoc_int;
clr_status(receive_in_progress);
enable_rxpin_int;
}
}
else
{
if (rx_bit <= 8) // bit <= 8, receive data bits
{
if (rx_samp)
rx_buff |= MSK_TAB[rx_bit-1];
rx_bit++;
}
else // bit > 8, receive stop bit
{
if (rx_samp) // valid stop bit
{
if (!test_status(receive_buffer_full))
{
rx_data = rx_buff;
set_status(receive_buffer_full);
}
else
set_status(receive_buffer_overflow);
}
else
{
rx_data = 0x00;
set_status(receive_error);
}
disable_rxoc_int;
clr_status(receive_in_progress);
enable_rxpin_int;
}
}
}
}


Хэ:
CODE
#ifndef __SW_UART_H
#define __SW_UART_H

#include "stm8s.h"

#define RX_ERROR 4
#define RX_BUFF_OVFL 2
#define RX_BUFF_FULL 1

extern u8 swutxbuf[64];
extern u8 swurxbuf[64];

void SwUInit(void);
u8 SwUTx(u8 len, u16 to);
u8 SwURx(u16 to);

#endif /* __SW_UART_H */
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.