首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >STM32H7xx尽可能快地切换IO

STM32H7xx尽可能快地切换IO
EN

Stack Overflow用户
提问于 2018-08-08 07:06:08
回答 1查看 2.6K关注 0票数 4

我正在尝试尽可能快地切换STM32H743上的IO。我使用的是外部10 the时钟,供电电压为3.3V,我确信我的主时钟运行在400 the,与GPIO (AHB4)对话的总线时钟运行在200 the。下面是我用来配置芯片和切换IO的一些示例代码:

代码语言:javascript
复制
RCC_ClkInitTypeDef clock;
RCC_OscInitTypeDef osc;

MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
while (!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
// External 10.000MHz oscillator
osc.OscillatorType = RCC_OSCILLATORTYPE_HSE;
osc.HSEState = RCC_HSE_ON;
osc.HSIState = RCC_HSI_OFF;
osc.CSIState = RCC_CSI_OFF;
osc.PLL.PLLState = RCC_PLL_ON;
osc.PLL.PLLSource = RCC_PLLSOURCE_HSE;
// ((10 / 5) * 400 ) / 2 = 400
osc.PLL.PLLM = 5;
osc.PLL.PLLN = 400;
osc.PLL.PLLP = 2;
osc.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
osc.PLL.PLLRGE    = RCC_PLL1VCIRANGE_3;
HAL_RCC_OscConfig(&osc);

// Sysclk source is PLL
clock.ClockType = (RCC_CLOCKTYPE_SYSCLK  | RCC_CLOCKTYPE_HCLK  |
                   RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 |
                   RCC_CLOCKTYPE_PCLK2   | RCC_CLOCKTYPE_D3PCLK1);

clock.SYSCLKSource   = RCC_SYSCLKSOURCE_PLLCLK;
clock.SYSCLKDivider  = RCC_SYSCLK_DIV1;
// HCLK = SYSCLK / 2 = 200
clock.AHBCLKDivider  = RCC_HCLK_DIV2;
clock.APB3CLKDivider = RCC_APB3_DIV2;  
clock.APB1CLKDivider = RCC_APB1_DIV2; 
clock.APB2CLKDivider = RCC_APB2_DIV2; 
clock.APB4CLKDivider = RCC_APB4_DIV2; 
HAL_RCC_ClockConfig(&clock, FLASH_LATENCY_4);
__HAL_RCC_CSI_ENABLE();
__HAL_RCC_SYSCFG_CLK_ENABLE();
HAL_EnableCompensationCell();

// GPIOC0 is pin I'm toggling
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_InitTypeDef gpio;
gpio.Mode  = GPIO_MODE_OUTPUT_PP;
gpio.Pull  = GPIO_NOPULL;
gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
gpio.Pin   = GPIO_PIN_0;
HAL_GPIO_Init(GPIOC, &gpio);

// Configure GPIOC9 as SYSCLK/2 (200MHz)
gpio.Mode      = GPIO_MODE_AF_PP;
gpio.Alternate = GPIO_AF0_MCO;
gpio.Pull      = GPIO_NOPULL;
gpio.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;
gpio.Pin       = GPIO_PIN_9;
HAL_GPIO_Init(GPIOC, &gpio);    
HAL_RCC_MCOConfig(RCC_MCO2, RCC_MCO2SOURCE_SYSCLK, RCC_MCODIV_2);

while (1)
{
    GPIOC->ODR = 0;
    GPIOC->ODR = 1;
    GPIOC->ODR = 0;
    GPIOC->ODR = 1;
    GPIOC->ODR = 0;
    GPIOC->ODR = 1;
    GPIOC->ODR = 0;
    GPIOC->ODR = 1;
    GPIOC->ODR = 0;
    GPIOC->ODR = 1;
    GPIOC->ODR = 0;
    GPIOC->ODR = 1;
    GPIOC->ODR = 0;
    GPIOC->ODR = 1;
    GPIOC->ODR = 0;
    GPIOC->ODR = 1;
    GPIOC->ODR = 0;
    GPIOC->ODR = 1;
    GPIOC->ODR = 0;
    GPIOC->ODR = 1;
}

当我在示波器上看到这一点时,我在200 my上正确地看到了我的SYSCLK/2,但切换引脚每60 is才切换一次。

当我查看这段代码的反汇编(编译后的"-O3")时,切换被编译为单条STR指令(r1 = 0,r2 = 1,r3 = &GPIOC->ODR):

代码语言:javascript
复制
...
str r1, [r3, #20] 
str r2, [r3, #20] 
str r1, [r3, #20] 
str r2, [r3, #20] 
str r1, [r3, #20] 
str r2, [r3, #20] 
str r1, [r3, #20] 
str r2, [r3, #20] 
str r1, [r3, #20] 
str r2, [r3, #20] 
str r1, [r3, #20]
...

我找不到Cortex-M7处理器的周期信息,但是当我查看Cortex-M4处理器(http://infocenter.arm.com/help/topic/com.arm.doc.ddi0439b/DDI0439B_cortex_m4_r0p0_trm.pdf)表3-1的周期时间时,我发现执行一个STR应该需要两个时钟周期。我希望看到我的IO大约每10纳秒(或者200 the AHB4总线上每两个时钟周期)切换一次。

我尝试过从闪存和SRAM运行代码,但IO速度没有差别。

为什么我的IO不是每两个时钟周期切换一次?

编辑:

在参考手册(st.com/resource/en/reference_manual/dm00314099.pdf)第10.2节中,它说:“快速切换能够每两个时钟周期改变一次。”

编辑:

因此,一些评论提到,处理器并不是真的打算直接与GPIO对话。因此,我重写了代码以使用DMA (我必须使用BDMA,并专门在RAM_D3中分配数据,以使其运行速度与软件循环一样快)。最终,我希望将计算出的数据从RAM输出到整个GPIO端口。如何按照参考手册的建议,每两个时钟周期输出一次数据?

在NUCLEO-H743ZI上运行的DMA代码:

代码语言:javascript
复制
#include <stm32h7xx_hal.h>
#include <stdint.h>

DMA_HandleTypeDef dma;

void SysTick_Handler(void)
{
    HAL_IncTick();
}

static void ClockConfig(void)
{
    RCC_ClkInitTypeDef clock;
    RCC_OscInitTypeDef osc;  
    MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    while (!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}

    osc.OscillatorType = RCC_OSCILLATORTYPE_CSI;
    osc.CSIState = RCC_CSI_ON;
    osc.CSICalibrationValue = 16;
    osc.PLL.PLLState = RCC_PLL_ON;
    osc.PLL.PLLSource = RCC_PLLSOURCE_CSI;
    osc.PLL.PLLM = 1;
    osc.PLL.PLLN = 200;
    osc.PLL.PLLP = 2;
    osc.PLL.PLLQ = 2;
    osc.PLL.PLLR = 2;
    osc.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
    osc.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    osc.PLL.PLLFRACN = 0;
    HAL_RCC_OscConfig(&osc);

    clock.ClockType      = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    clock.SYSCLKSource   = RCC_SYSCLKSOURCE_PLLCLK;
    clock.SYSCLKDivider  = RCC_SYSCLK_DIV1;
    clock.AHBCLKDivider  = RCC_HCLK_DIV2;
    clock.APB3CLKDivider = RCC_APB3_DIV2;  
    clock.APB1CLKDivider = RCC_APB1_DIV2; 
    clock.APB2CLKDivider = RCC_APB2_DIV2; 
    clock.APB4CLKDivider = RCC_APB4_DIV2; 
    HAL_RCC_ClockConfig(&clock, FLASH_LATENCY_4);
    __HAL_RCC_CSI_ENABLE();
    __HAL_RCC_SYSCFG_CLK_ENABLE();
    HAL_EnableCompensationCell();

    HAL_SYSTICK_Config(SystemCoreClock / 1000);
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
    HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

void GpioConfig(void)
{
    __GPIOC_CLK_ENABLE();
    GPIO_InitTypeDef gpio;
    gpio.Pin   = GPIO_PIN_0;
    gpio.Mode  = GPIO_MODE_OUTPUT_PP;
    gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    gpio.Pull  = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOC, &gpio);
    gpio.Pin   = GPIO_PIN_1;
    gpio.Mode  = GPIO_MODE_OUTPUT_PP;
    gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    gpio.Pull  = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOC, &gpio);
    gpio.Mode      = GPIO_MODE_AF_PP;
    gpio.Alternate = GPIO_AF0_MCO;
    gpio.Pull      = GPIO_NOPULL;
    gpio.Speed     = GPIO_SPEED_FAST;
    gpio.Pin       = GPIO_PIN_9;
    HAL_GPIO_Init(GPIOC, &gpio);    
    HAL_RCC_MCOConfig(RCC_MCO2, RCC_MCO2SOURCE_SYSCLK, RCC_MCODIV_2);
}

uint8_t complete = 0;
void DmaComplete(DMA_HandleTypeDef *handle)
{
    complete = 1;
}

void DmaError(DMA_HandleTypeDef *handle)
{
    complete = 1;
}

void DmaConfig(void)
{
    __HAL_RCC_BDMA_CLK_ENABLE();
    dma.Instance                 = BDMA_Channel0;
    dma.Init.Request             = DMA_REQUEST_MEM2MEM;
    dma.Init.Direction           = DMA_MEMORY_TO_MEMORY;
    dma.Init.PeriphInc           = DMA_PINC_ENABLE;
    dma.Init.MemInc              = DMA_MINC_DISABLE;
    dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    dma.Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD;
    dma.Init.Mode                = DMA_NORMAL;
    dma.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
    HAL_DMA_Init(&dma);
    HAL_DMA_RegisterCallback(&dma, HAL_DMA_XFER_CPLT_CB_ID,  DmaComplete);
    HAL_DMA_RegisterCallback(&dma, HAL_DMA_XFER_ERROR_CB_ID, DmaError);
    HAL_NVIC_SetPriority(BDMA_Channel0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(BDMA_Channel0_IRQn);
}

void BDMA_Channel0_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&dma);
}

void HardFault_Handler(void)
{
    __NOP();
}

__attribute__((section(".ram_d3")))
static uint16_t src[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

int main(void)
{
    HAL_Init();
    ClockConfig();
    GpioConfig();
    DmaConfig();
    while (1)
    {
        complete = 0;
        HAL_DMA_Start_IT(&dma, (uint32_t)&src, (uint32_t)&GPIOC->ODR, 10);
        while (complete == 0) ;
    }
}

EN

回答 1

Stack Overflow用户

发布于 2018-08-08 10:30:10

你的假设是错误的。GPIO速度不是由内核指令时序决定的,而是由外围设备的物理特性决定的。请参阅DS中的表,其中包含静态和动态GPIO端口特征。最大速度取决于许多参数。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51736591

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档