gpt4 book ai didi

c - STM32 上的 USART 接收器

转载 作者:太空宇宙 更新时间:2023-11-04 05:32:40 25 4
gpt4 key购买 nike

您好,我目前正在研究 USART 通信,试图从任何 GPIO 引脚传输和接收数据。

我成功地以任何波特率传输数据,但在接收时卡住了。

我一次能收到一个角色。引脚设置为使用 RX 引脚的外部下降沿中断。

但是当我从终端向 Controller 传输像“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 ;}

感谢您的帮助。

最佳答案

您的解决方案的根本问题是您在过渡点而不是位中心对位进行采样。在检测到 START 转换时,您仅延迟一位周期,因此在位转换而不是位 centre 处采样 r1 - 这几乎肯定会导致错误,尤其是在边缘可能不是很快的高速下。第一个延迟应为 1.5 位周期长。 (delay_time * 2/3) 如下图所示:

enter image description here

第二个问题是您在 STOP 位之后不必要地延迟,这将导致您错过下一个 START 转换,因为它可能发生在您清除中断标志之前。一旦拥有 r8,您的工作就完成了。

采样 r0r9 在任何情况下都不会丢弃它们,并且状态 r0 隐含在 EXTI 的任何事件中如果发送方生成无效帧,则 r9 不会为 1。此外,如果您不对 r9 进行采样,则它之前的延迟也变得不必要了。这些行应该被删除:

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

这至少会给你两个位周期,让你的处理器可以做其他工作而不是卡在中断上下文中,但是延迟是一个中断处理程序不是一个好习惯 - 它会阻止正常代码和所有低优先级中断的执行使解决方案不适用于实时系统。在这种情况下,如果系统只需要软 UART Rx,那么通过简单地轮询 GPIO 而不是使用中断可能会获得更好的结果——至少其他中断可以正常运行,而且实现起来要简单得多.

你的“unrolled-loop”实现也没有真正的目的,延迟到位 - 即使在非常高的比特率下,循环开销在帧的持续时间内可能是微不足道的,如果是的话你可以调整延迟一点补偿:

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。

我已经在以 120MHz 和 4800 运行的 STM32 上成功地使用了这种技术,并将其推至 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 转换,通常可以忽略。如果它不是 1,一个真正的 UART 可能会产生一个帧错误,但如果您在任何情况下都不打算处理此类错误,那么检查就没有意义。

关于c - STM32 上的 USART 接收器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55256101/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com