Приемник USART на STM32 - PullRequest
       89

Приемник USART на STM32

0 голосов
/ 20 марта 2019

Привет! В настоящее время я работаю над связью USART, пытаясь передать и получить данные с любого вывода GPIO.

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

Мне удалось получить персонажа одновременно.Вывод устанавливается как внешнее прерывание по падающему фронту, используя вывод RX.

Но когда я передаю строку, такую ​​как «test», от терминала к контроллеру, принимается только «t», остальное 3 символа является значением мусора.Я думал, что после получения первого символа и его сохранения прерывание не запускается так быстро для следующего символа.

Многие вещи жестко запрограммированы в этом примере кода для целей тестирования.

ЗдесьПример кода для получателя

void EXTI0_IRQHandler(void){
r0 = GPIOA->IDR;
delay_us(delay_time);
r1 = GPIOA->IDR;
delay_us(delay_time);
r2 = GPIOA->IDR;
delay_us(delay_time);
r3 = GPIOA->IDR;
delay_us(delay_time);
r4 = GPIOA->IDR;
delay_us(delay_time);
r5 = GPIOA->IDR;
delay_us(delay_time);
r6 = GPIOA->IDR;
delay_us(delay_time);
r7 = GPIOA->IDR;
delay_us(delay_time);
r8 = GPIOA->IDR;
delay_us(delay_time);
r9 = GPIOA->IDR;
delay_us(delay_time);
r1 = r1 & 0x00000001;
r2 = r2 & 0x00000001;
r3 = r3 & 0x00000001;
r4 = r4 & 0x00000001;
r5 = r5 & 0x00000001;
r6 = r6 & 0x00000001;
r7 = r7 & 0x00000001;
r8 = r8 & 0x00000001;
x |= r8;
x = x << 1;
x |= r7;
x = x << 1;
x |= r6;
x = x << 1;
x |= r5;
x = x << 1;
x |= r4;
x = x << 1;
x |= r3;
x = x << 1;
x |= r2;
x = x << 1;
x |= r1;
buff1[z++] = x;
EXTI->PR |= 0X00000001;
x=0;
return ;}

Спасибо за помощь.

Ответы [ 3 ]

5 голосов
/ 20 марта 2019

Основная проблема вашего решения заключается в том, что вы выбираете биты в точке перехода, а не в центре битов.При обнаружении перехода START вы задерживаете только один битовый период, поэтому сэмплируйте r1 на битовом переходе, а не на бите center - это почти наверняка приведет к ошибкам, особенно на высокой скорости, где краяможет быть не очень быстроПервая задержка должна быть длиной 1,5 бита.(delay_time * 2 / 3), как показано ниже:

enter image description here

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

Выборка r0 и r9 не имеет смысла, вы их ни в коем случае не отбрасываете, а состояние r0 подразумевается в любом событии формыEXTI-переход и r9 не будут равны 1, если отправитель генерирует недопустимые кадры.Более того, если вы не делаете выборку r9, задержка перед ней также становится ненужной.Эти строки должны быть удалены:

delay_us(delay_time);
r9 = GPIOA->IDR;
delay_us(delay_time);

Это, по крайней мере, даст вам два битных периода, когда ваш процессор может выполнять другую работу, кроме того, что застревает в контексте прерывания, но задержка является обработчиком прерывания, что не является хорошей практикой.- он блокирует выполнение нормального кода и все прерывания с более низким приоритетом, что делает решение непригодным для систем реального времени.В этом случае, если soft-UART Rx - это все, что должна делать система, вы, вероятно, получите лучшие результаты, просто опросив GPIO, а не используя прерывания - по крайней мере, тогда другие прерывания могли бы работать нормально, и это намного проще реализовать.

Ваша реализация с "развернутым циклом" также не имеет смысла с задержками на месте - даже при очень высоких битовых скоростях издержки цикла, вероятно, будут незначительными в течение длительности кадра, и если бы это былоВы можете немного подстроить задержки, чтобы компенсировать:

void EXTI0_IRQHandler(void)
{
    delay_us(delay_time * 2 / 3);
    for( int i = 7; i >= 0; i-- )
    {
        x |= GPIOA->IDR << i ;
        delay_us(delay_time);
    }

    EXTI->PR |= 0X00000001;
    buff1[z++] = x;
    x = 0 ;
    return ;
}

Более надежное решение для мягкого приемника, которое будет хорошо играть с другими процессами в вашей системе, должно использовать прерывание EXTI только для обнаружения начального бита;обработчик должен отключить EXTI и запустить таймер со скоростью передачи данных в бодах плюс полбитового периода.Обработчик прерываний для таймера выбирает вывод GPIO в центре битового периода, а при первом прерывании после EXTI меняет период на один битовый период.Для каждого прерывания таймера он производит выборку и подсчитывает биты до тех пор, пока не будет сдвинуто целое слово данных, когда он отключает таймер и повторно включает EXTI для следующего начального бита.

Я успешно использовал эту технику наSTM32 работает на частоте 120 МГц при 4800 и увеличивает его до 38400, но при 26 микросекундах на бит он становится довольно занятым в контексте прерывания, и у вашего приложения, вероятно, есть другие дела?

Ниже приведена слегка обобщенная версиямоей реализации.Он использует вызовы стандартной периферийной библиотеки STM32, а не прямой доступ к регистру или более позднюю версию STM32Cube HAL, но вы можете легко перенести его так или иначе, как вам нужно.Кадрирование: N, 8,1.

#define SOFT_RX__BAUD = 4800u ;
#define SOFT_RX_TIMER_RELOAD = 100u ;

void softRxInit( void )
{
    // Enable SYSCFG clock
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

    // Connect the EXTI Line to GPIO Pin
    SYSCFG_EXTILineConfig( EXTI_PortSourceGPIOB, EXTI_PinSource0 );

    TIM_Cmd( TIM10, DISABLE);

    // NVIC initialisation
    NVIC_InitTypeDef NVIC_InitStructure = {0,0,0,DISABLE};
    NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_TIM10_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 12; 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    // Enable peripheral clock to timers
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM10, ENABLE);

    TIM_ARRPreloadConfig( TIM10, DISABLE );

    // Generate soft Rx rate clock (4800 Baud)
    TIM_TimeBaseInitTypeDef init = {0};
    TIM_TimeBaseStructInit( &init ) ;
    init.TIM_Period = static_cast<uint32_t>( SOFT_RX_TIMER_RELOAD );
    init.TIM_Prescaler = static_cast<uint16_t>( (TIM10_ClockRate() / (SOFT_RX__BAUD * SOFT_RX_TIMER_RELOAD)) - 1 );
    init.TIM_ClockDivision = 0;
    init.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit( TIM10, &init ) ;

    // Enable the EXTI Interrupt in the NVIC
    NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 12;  
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init( &NVIC_InitStructure );

    // Dummy call to handler to force initialisation 
    // of UART frame state machine
    softRxHandler() ;
}

// Soft UART Rx START-bit interrupt handler
void EXTI0_IRQHandler()
{
    // Shared interrupt, so verify that it is the correct one
    if( EXTI_GetFlagStatus( EXTI_Line0 ) == SET )
    {
        // Clear the EXTI line pending bit.
        // Same as EXTI_ClearITPendingBit( EXTI_Line11 )
        EXTI_ClearFlag( EXTI_Line0 ) ;

        // Call Soft UART Rx handler
        softRxHandler() ;
    }
}

void TIM1_UP_TIM10_IRQHandler( void )
{
    // Call Soft UART Rx handler
    softRxHandler() ;

    TIM_ClearITPendingBit( TIM10, TIM_IT_Update );
}

// Handler for software UART Rx
inline void softRxHandler()
{
    static const int START_BIT = -1 ;
    static const int STOP_BIT = 8 ;
    static const int HALF_BIT = SOFT_RX_TIMER_RELOAD / 2;
    static const int FULL_BIT = SOFT_RX_TIMER_RELOAD ;
    static int rx_bit_n = STOP_BIT ;
    static const uint8_t RXDATA_MSB = 0x80 ;
    static uint8_t rx_data = 0 ;
    static EXTI_InitTypeDef extiInit = { EXTI_Line0,
                                         EXTI_Mode_Interrupt,
                                         EXTI_Trigger_Falling,
                                         DISABLE } ;

    // Switch START-bit/DATA-bit
    switch( rx_bit_n )
    {
        case START_BIT :
        {
            // Stop waiting for START_BIT
            extiInit.EXTI_LineCmd = DISABLE;
            EXTI_Init( &extiInit );

            // Enable the Interrupt
            TIM_ClearITPendingBit( TIM10, TIM_IT_Update );
            TIM_ITConfig( TIM10, TIM_IT_Update, ENABLE );

            // Enable the timer (TIM10)
            // Set time to hit centre of data LSB
            TIM_SetAutoreload( TIM10, FULL_BIT + HALF_BIT ) ;
            TIM_Cmd( TIM10, ENABLE );

            // Next = LSB data
            rx_data = 0 ;
            rx_bit_n++ ;
        }
        break ;

        // STOP_BIT is only set on first-time initialisation as a state, othewise it is
        // transient within this scase.
        // Use fall through and conditional test to allow
        // case to handle both initialisation and UART-frame (N,8,1) restart.
        case STOP_BIT :
        default :   // Data bits
        {
            TIM_ClearITPendingBit( TIM10, TIM_IT_Update );

            if( rx_bit_n < STOP_BIT )
            {
                if( rx_bit_n == 0 )
                {
                    // On LSB reset time to hit centre of successive bits
                    TIM_SetAutoreload( TIM10, FULL_BIT ) ;
                }

                // Shift last bit toward LSB (emulate UART shift register)
                rx_data >>= 1 ;

                // Read Rx bit from GPIO
                if( GPIO_ReadInputDataBit( GPIOB, GPIO_Pin_0 ) != 0 )
                {
                    rx_data |= RXDATA_MSB ;
                }

                // Next bit
                rx_bit_n++ ;
            }

            // If initial state or last DATA bit sampled...
            if( rx_bit_n == STOP_BIT )
            {
                // Stop DATA-bit sample timer
                TIM_Cmd( TIM10, DISABLE );

                // Wait for new START-bit
                rx_bit_n = START_BIT ;
                extiInit.EXTI_LineCmd = ENABLE;
                EXTI_Init( &extiInit );

                // Place character in Rx buffer
                serialReceive( rx_data ) ;
            }
        }
        break ;
    }
}

Код работает так же, как реальный UART, как показано на диаграмме синхронизации выше, за исключением того, что в моей реализации бит STOP фактически не дискретизируется- это не нужно;он служит только для гарантии того, что последующий бит START является переходом 1 -> 0 и, как правило, может игнорироваться.Реальный UART, вероятно, генерировал бы ошибку кадрирования, если бы она не была равна 1, но если вы не собираетесь обрабатывать такие ошибки в любом случае, проверка не имеет смысла.

0 голосов
/ 27 апреля 2019

Вы упомянули, что символ 't' получен при отправке строки "test". Введите достаточную задержку между символами в строке. Надеюсь, это работает. Вы можете использовать docklite для отправки строки с межсимвольной задержкой.

0 голосов
/ 20 марта 2019

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

При соглашении «стартовый бит является инверсией стоп-бита», ваш код обнаруживает дополнительное ребро между символами, таким образом, очевидно, смещая поток битов, который вы обнаруживает, на один бит.

...