2016-06-09 05:20 AM
I am working with the Discovery Development Board and I would like to receive data over a software UART. In the code below I would expect to receive 8 timer interrupts if only 1 byte is transmitted, but 100 microseconds after the last bit I receive an extra timer interrupt. Does anybody know why? Other than that, the UART seems to works fine.
000 #define TEST_PIN GPIO_PIN_15
001 #define TEST_PORT GPIOC
002 #define TOGGLE_TEST_PIN_AT_SAMPLING() SET_PIN_HIGH(TEST_PORT, TEST_PIN); SET_PIN_LOW(TEST_PORT, TEST_PIN); \
003 SET_PIN_HIGH(TEST_PORT, TEST_PIN); SET_PIN_LOW(TEST_PORT, TEST_PIN)
004
005 #define Rx_PIN GPIO_PIN_11
006 #define Rx_PORT GPIOC
007 #define Rx_PIN_FLAG 0x0800
008 #define RECEIVE_EVENT_ARG LOW_LEVEL_EVENT__UART2_SW_BYTE_RECEIVED
009 #define EXTERNAL_INT EXTI15_10_IRQn
010 #define EXTERNAL_INT_HANDLER EXTI15_10_IRQHandler
011 #define TIMER_INSTANCE TIM4
012 #define TIMER_INT TIM4_IRQn
013 #define TIMER_INT_HANDLER TIM4_IRQHandler
014
015
016 #define CLEAR_EXT_INT_FLAG_ON_Rx() __HAL_GPIO_EXTI_CLEAR_IT(Rx_PIN)
017 #define ENABLE_EXT_INT_ON_Rx() HAL_NVIC_EnableIRQ(EXTERNAL_INT)
018 #define DISABLE_EXT_INT_ON_Rx() HAL_NVIC_DisableIRQ(EXTERNAL_INT)
019
020 #define START_TIMER() (TIMER_INSTANCE->CR1 |= TIM_CR1_CEN)
021 #define STOP_TIMER() (TIMER_INSTANCE->CR1 &= ~TIM_CR1_CEN)
022 #define TIMER_INT_FLAG (TIMER_INSTANCE->SR & 0x0001)
023 #define ENABLE_TIMER_INT()
do
{ TIMER_INSTANCE->DIER |= TIM_DIER_UIE; HAL_NVIC_EnableIRQ(TIMER_INT); }
while
(0)
024 #define DISABLE_TIMER_INT()
do
{ TIMER_INSTANCE->DIER &= ~TIM_DIER_UIE; HAL_NVIC_DisableIRQ(TIMER_INT); }
while
(0)
025 #define CLEAR_TIMER_INT_FLAG() (TIMER_INSTANCE->SR &= ~TIM_SR_UIF)
026 #define SET_TIMER_PERIOD(period) (TIMER_INSTANCE->ARR = period)
027 #define SET_COUNTER_VALUE(value) (TIMER_INSTANCE->CNT = value)
028 #define SET_PRE_SCALAR_1()
do
{ TIMER_INSTANCE->PSC = 0; TIMER_INSTANCE->EGR = TIM_EGR_UG; }
while
(0)
029
030 #define DUMMY_INT CAN2_RX1_IRQn
031 #define DUMMY_INT_HANDLER CAN2_RX1_IRQHandler
032 #define ENABLE_DUMMY_INT() (HAL_NVIC_EnableIRQ(DUMMY_INT))
033 #define CLEAR_DUMMY_INT_FLAG() (HAL_NVIC_ClearPendingIRQ(DUMMY_INT))
034 #define ISSUE_DUMMY_INT() NVIC->STIR = DUMMY_INT
035
036
037
static
EventCallback_t ByteReceivedCallback_ = (EventCallback_t)NULL;
038
static
uint32_t timerPeriod;
039
static
uint32_t timerPeriodTimesOnePointFive;
040
static
uint8_t idleState_;
041
042
043
void
UART2_SW_Configuration(uint8_t idleState)
044 {
045 idleState_ = idleState;
046
047 GPIO_InitTypeDef GPIO_InitStructure;
048 GPIO_InitStructure.Pin = Rx_PIN;
049
if
(idleState_ == 0)
050 {
051 GPIO_InitStructure.Pull = GPIO_PULLDOWN;
052 GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING;
053 }
054
else
055 {
056 GPIO_InitStructure.Pull = GPIO_PULLUP;
057 GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;
058 }
059 GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
060 GPIO_InitStructure.Alternate = 0;
061 HAL_GPIO_Init(Rx_PORT, &GPIO_InitStructure);
062
063 SET_PRE_SCALAR_1();
064 SET_TIMER_PERIOD(0xFFFFFFFF);
065
066 HAL_NVIC_SetPriority(DUMMY_INT, 2, 0);
067 HAL_NVIC_SetPriority(EXTERNAL_INT, 1, 0);
068 HAL_NVIC_SetPriority(TIMER_INT, 0, 0);
069
070 GPIO_InitStructure.Pin = TEST_PIN;
071 GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
072 GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
073 GPIO_InitStructure.Pull = GPIO_NOPULL;
074 GPIO_InitStructure.Alternate = 0;
075 HAL_GPIO_Init(TEST_PORT, &GPIO_InitStructure);
076 SET_PIN_LOW(TEST_PORT, TEST_PIN);
077
078 ENABLE_DUMMY_INT();
079 CLEAR_EXT_INT_FLAG_ON_Rx();
080 ENABLE_EXT_INT_ON_Rx();
081 }
082
083
084
void
UART2_SW_SetCallback(EventCallback_t ByteReceivedCallback)
085 {
086
if
(ByteReceivedCallback == (EventCallback_t)NULL)
087 {
088 ByteReceivedCallback_ = (EventCallback_t)NULL;
089 DISABLE_EXT_INT_ON_Rx();
090 }
091
else
092 {
093 ByteReceivedCallback_ = ByteReceivedCallback;
094 ENABLE_EXT_INT_ON_Rx();
095 }
096 }
097
098
099
void
UART2_SW_SetBaudRate(uint32_t baudRate)
100 {
101 timerPeriod = GET_TIMER_FREQ(TIMER_INSTANCE)/baudRate;
102 timerPeriodTimesOnePointFive = (3 * ((uint32_t)timerPeriod)) >> 1;
103 }
104
105
106
static
volatile
uint8_t bitCount = 0;
107
static
volatile
uint8_t receivedByte = 0;
108
static
volatile
uint8_t finishedReceivedByte = 0;
109
static
uint8_t sampledValue;
110
111
void
EXTERNAL_INT_HANDLER(
void
)
112 {
113 SET_COUNTER_VALUE(0);
114 START_TIMER();
115 DISABLE_EXT_INT_ON_Rx();
116 SET_TIMER_PERIOD(timerPeriodTimesOnePointFive);
117 CLEAR_TIMER_INT_FLAG();
118 ENABLE_TIMER_INT();
119 bitCount = 0;
120 }
121
122
123
void
TIMER_INT_HANDLER(
void
)
124 {
125 TOGGLE_TEST_PIN_AT_SAMPLING();
126 sampledValue = READ_PIN(Rx_PORT, Rx_PIN);
127 CLEAR_TIMER_INT_FLAG();
128
if
(bitCount == 0)
129 {
130 SET_TIMER_PERIOD(timerPeriod);
131 receivedByte = sampledValue;
132 bitCount++;
133 }
134
else
if
((1 <= bitCount) && (bitCount <= 6))
135 {
136 receivedByte |= (sampledValue << bitCount);
137 bitCount++;
138 }
139
else
140 {
141 STOP_TIMER();
142 DISABLE_TIMER_INT();
143 SET_TIMER_PERIOD(0xFFFFFFFF);
144 finishedReceivedByte = receivedByte | (sampledValue << 7);
145
if
(idleState_ == 0)
146 {
147 finishedReceivedByte = ~finishedReceivedByte;
148 }
149
if
(bitCount == 7)
150 {
151 ISSUE_DUMMY_INT();
152 }
153
if
(ByteReceivedCallback_ != (EventCallback_t)NULL)
154 {
155 CLEAR_EXT_INT_FLAG_ON_Rx();
156 ENABLE_EXT_INT_ON_Rx();
157 }
158 }
159 }
160
161
162
void
DUMMY_INT_HANDLER(
void
)
163 {
164
if
(ByteReceivedCallback_ != (EventCallback_t)NULL)
165 {
166 (
void
)ByteReceivedCallback_((uint32_t)RECEIVE_EVENT_ARG, (uint32_t)finishedReceivedByte);
167 }
168 }
2016-06-09 05:57 AM
It seems I get a ''bonus'' external interrupt after each byte so actually I get another 8 timer interrupts (16 in total) for each received byte. Does anybody know what it is in my code that triggers that extra external interrupt? The last transition on my UART data is the opposite of the start bit and since I only trigger on either falling or rising edge it can't be the last bit that triggers the extra internal interrupt.
2016-06-09 06:21 AM
I can get rid of the 8 extra timer interrupts by changing my external interrupt handler:
void
EXTERNAL_INT_HANDLER(
void
)
{
SET_COUNTER_VALUE(0);
START_TIMER();
if
((EXTI->PR & Rx_PIN_FLAG) == 0)
{
STOP_TIMER();
return
;
}
DISABLE_EXT_INT_ON_Rx();
SET_TIMER_PERIOD(timerPeriodTimesOnePointFive);
CLEAR_TIMER_INT_FLAG();
ENABLE_TIMER_INT();
bitCount = 0;
}
However, when I run this on my own PCB (which runs at 1,85 Volts and needs more flash latency and pre-fetch can't be enabled) then it becomes flaky because it can't cope with the transmitted data rate.
2016-06-09 06:47 AM
I use the following construction:
while (EXTI->PR AND (1 SHL FREQ_NOT_GOOD_PIN)) { // clear pending in EXTI controller EXTI->PR = (1 SHL FREQ_NOT_GOOD_PIN); } do { NVIC_ClearPendingIRQ(FREQ_NOT_GOOD_IRQn); } while (NVIC_GetPendingIRQ(FREQ_NOT_GOOD_IRQn)); I don't recall the details but certainly resulted from some painful experimentation, which might have left traces on this forum too. JW2016-06-10 04:38 AM
Your suggestion solved the problem. Thanks!
2016-06-14 01:34 AM
The problem is back (I might have changed the clock frequency), the code below gives me two external interrupts per received byte;
#define CLEAR_EXT_INT_FLAG_ON_Rx() while (EXTI->PR & Rx_PIN_FLAG) \
{
/* clear pending in EXTI controller */
\
EXTI->PR = Rx_PIN_FLAG; \
} \
do
{ \
NVIC_ClearPendingIRQ(EXTI15_10_IRQn); \
}
while
(NVIC_GetPendingIRQ(EXTI15_10_IRQn))
void
EXTERNAL_INT_HANDLER(
void
)
{
SET_COUNTER_VALUE(0);
START_TIMER();
DISABLE_EXT_INT_ON_Rx();
SET_TIMER_PERIOD(timerPeriodTimesOnePointFive);
CLEAR_TIMER_INT_FLAG();
ENABLE_TIMER_INT();
bitCount = 0;
}
void
TIMER_INT_HANDLER(
void
)
{
TOGGLE_TEST_PIN_AT_SAMPLING();
sampledValue = READ_PIN(Rx_PORT, Rx_PIN);
CLEAR_TIMER_INT_FLAG();
if
(bitCount == 0)
{
SET_TIMER_PERIOD(timerPeriod);
receivedByte = sampledValue;
bitCount++;
}
else
if
((1 <= bitCount) && (bitCount <= 6))
{
receivedByte |= (sampledValue << bitCount);
bitCount++;
}
else
{
STOP_TIMER();
DISABLE_TIMER_INT();
SET_TIMER_PERIOD(0xFFFFFFFF);
finishedReceivedByte = receivedByte | (sampledValue << 7);
if
(idleState_ == 0)
{
finishedReceivedByte = ~finishedReceivedByte;
}
if
(bitCount == 7)
{
ISSUE_DUMMY_INT();
}
if
(ByteReceivedCallback_ != (EventCallback_t)NULL)
{
CLEAR_EXT_INT_FLAG_ON_Rx();
ENABLE_EXT_INT_ON_Rx();
}
}
}
Does anybody know what's wrong? I don't get the extra external interrupt if I instead of using timer interrupts poll a timer:
void
EXTERNAL_INT_HANDLER(
void
)
{
SET_COUNTER_VALUE(0);
START_TIMER();
DISABLE_ALL_INTS_IF_NECESSARY();
DISABLE_EXT_INT_ON_Rx();
SET_TIMER_PERIOD(timerPeriodTimesOnePointFive);
CLEAR_TIMER_INT_FLAG();
while
(TIMER_INT_FLAG == 0) {};
// 1st data bit
sampledValue = READ_PIN(Rx_PORT, Rx_PIN);
CLEAR_TIMER_INT_FLAG();
SET_TIMER_PERIOD(timerPeriod);
receivedByte = sampledValue;
while
(TIMER_INT_FLAG == 0) {};
// 2nd data bit
sampledValue = READ_PIN(Rx_PORT, Rx_PIN);
CLEAR_TIMER_INT_FLAG();
receivedByte |= (sampledValue << 1);
while
(TIMER_INT_FLAG == 0) {};
// 3rd data bit
sampledValue = READ_PIN(Rx_PORT, Rx_PIN);
CLEAR_TIMER_INT_FLAG();
receivedByte |= (sampledValue << 2);
while
(TIMER_INT_FLAG == 0) {};
// 4th data bit
sampledValue = READ_PIN(Rx_PORT, Rx_PIN);
CLEAR_TIMER_INT_FLAG();
receivedByte |= (sampledValue << 3);
while
(TIMER_INT_FLAG == 0) {};
// 5th data bit
sampledValue = READ_PIN(Rx_PORT, Rx_PIN);
CLEAR_TIMER_INT_FLAG();
receivedByte |= (sampledValue << 4);
while
(TIMER_INT_FLAG == 0) {};
// 6th data bit
sampledValue = READ_PIN(Rx_PORT, Rx_PIN);
CLEAR_TIMER_INT_FLAG();
receivedByte |= (sampledValue << 5);
while
(TIMER_INT_FLAG == 0) {};
// 7th data bit
sampledValue = READ_PIN(Rx_PORT, Rx_PIN);
CLEAR_TIMER_INT_FLAG();
receivedByte |= (sampledValue << 6);
while
(TIMER_INT_FLAG == 0) {};
// 8th data bit
sampledValue = READ_PIN(Rx_PORT, Rx_PIN);
STOP_TIMER();
SET_TIMER_PERIOD(0xFFFFFFFF);
if
(idleState_ == 0)
{
finishedReceivedByte = ~(receivedByte | (sampledValue << 7));
}
else
{
finishedReceivedByte = receivedByte | (sampledValue << 7);
}
CLEAR_EXT_INT_FLAG_ON_Rx();
ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
ISSUE_DUMMY_INT();
if
(ByteReceivedCallback_ == (EventCallback_t)NULL)
{
DISABLE_EXT_INT_ON_Rx();
CLEAR_EXT_INT_FLAG_ON_Rx();
}
else
{
CLEAR_EXT_INT_FLAG_ON_Rx();
ENABLE_EXT_INT_ON_Rx();
}
}
However, I would prefer to use the interrupt based code. In addition, I don't like it when mysterious things are happening in my microcontroller...
2016-06-14 02:00 AM
> TOGGLE_TEST_PIN_AT_SAMPLING();
Move this from Timer's interrupt to the external interrupt handler, then show pulses of this test pin, together with the input waveform.
JW
2016-06-14 02:38 AM
One more thing
> I might have changed the clock frequency How exactly? Did it involve change of the APB prescalers? What mcu are we talking about? JW2016-06-14 03:08 AM
I have attached two photos, one with the received data and the external interrupts and one with the received data and the timer interrupts. In both cases the character A (hex 41) is transmitted at 115200 Baud.
I am running on the Discovery Development Board with an STM32F407 microcontroller. My UART is capable of sending characters just fine and when sending it's using the same timer periods as when receiving. Therefore, I'm not suspecting clock issues. ________________ Attachments : ExternalInt.jpg : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I0ZN&d=%2Fa%2F0X0000000baY%2F12UMtTZbUYtYGpHaniPIOshsAZhkgcU32067U4F83gc&asPdf=falseTimerInt.jpg : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I0ZI&d=%2Fa%2F0X0000000baX%2FYyz2SHUMp2xa6aHiEr8m1ZJfiZSdWYXGVhdk9lMKnIs&asPdf=false2016-06-14 04:04 AM
I found the problem, it was a copy-and-paste-error. The following:
#define CLEAR_EXT_INT_FLAG_ON_Rx() while (EXTI->PR & Rx_PIN_FLAG) \
{
/* clear pending in EXTI controller */
\
EXTI->PR = Rx_PIN_FLAG; \
} \
do
{ \
NVIC_ClearPendingIRQ(EXTI15_10_IRQn); \
}
while
(NVIC_GetPendingIRQ(EXTI15_10_IRQn))
should instead be:
#define CLEAR_EXT_INT_FLAG_ON_Rx() while (EXTI->PR & Rx_PIN_FLAG) \
{
/* clear pending in EXTI controller */
\
EXTI->PR = Rx_PIN_FLAG; \
} \
do
{ \
NVIC_ClearPendingIRQ(EXTERNAL_INT); \
}
while
(NVIC_GetPendingIRQ(EXTERNAL_INT))