【问题标题】:STM32F103C8/CoIDE/ST-LinkV2 Can't update registersSTM32F103C8/CoIDE/ST-LinkV2 无法更新寄存器
【发布时间】:2017-12-11 21:51:55
【问题描述】:

我是 STM32 的新手,不是嵌入式开发的新手(有 PIC、dsPIC、MSP430 的经验)。

环境

  • CooCox CoIDE
  • arm-none-eabi-gcc
  • ST-LINK V2(与 Segger J-Link 相同)通过 SWD

问题描述

我正在尝试使用直接寄存器操作编写一个基本库(STMCube HAL 很酷,但我发现无论如何我都必须阅读数据表,因此首选寄存器操作)。本质上,我只是想让Blue Pill 开发板上的一个LED 闪烁,它是引脚C13

我可以编译、刷写和调试,但是当我调试时,LED 不闪烁,寄存器也没有变化。寄存器值本身实际上并没有那么重要,因此您不需要出去查看数据表。我只需要能够操纵它们!

单步调试调试器工作正常,variables 监视窗口也适当更新。

我尝试过的事情

虽然这并不详尽(已经过了几个小时,所以我可能忘了包括一些),但这些是我尝试过的一些事情:

  • 更改BOOTx 引脚配置
  • 更改调试器配置(减速、加速、重置策略)。
  • 将调试器更改为 SEGGER J-Link
  • 锁定/解锁 GPIO 引脚

所有人都有相同的结果。我怀疑我可能缺少 CMSIS 中的某些内容或必要的包含文件,但我似乎找不到它。

我可能遗漏的另一点涉及振荡器设置。据我了解,启动时有一个默认配置,除非我想更改它,否则我不必担心。也许这是有缺陷的?

编辑:适用于 STM32CubeMX HAL

当我尝试使用 STM32CUBEMX HAL 进行基本切换时,它可以工作。这让我觉得我在包含、项目设置、振荡器设置...中缺少一些基本的东西?

代码

main.c

#include "gpio.h"

#define LED_PIN 13


int main(void)
{
    uint32_t i;

    // initialize the peripherals
    GPIO_init();
    GPIOC_setupOutput(LED_PIN, PUSH_PULL, PIN_SPEED_50MHz);

    while(1)
    {
        GPIOC_setPin(LED_PIN);
        for(i=0; i<4000000; i++);
        GPIOC_clearPin(LED_PIN);
        for(i=0; i<4000000; i++);
    }
}

gpio.h

#ifndef _GPIO_H
#define _GPIO_H

#include <stdint.h>
#include <stdbool.h>

typedef enum{
    ANALOG=0b00, FLOATING=0b01, PULL_UP_PULL_DOWN=0b10
}InputPinMode;

typedef enum{
    PUSH_PULL=0b00, OPEN_DRAIN=0b01, AF_PUSH_PULL=0b10, AF_OPEN_DRAIN=0b11
}OutputPinMode;

typedef enum{
    PIN_SPEED_2MHz=0b10, PIN_SPEED_10MHz=0b01, PIN_SPEED_50MHz=0b11
}PinSpeed;

void GPIO_init(void);
void GPIOA_setupInput(uint8_t pinNumber, InputPinMode mode);
void GPIOC_setupInput(uint8_t pinNumber, InputPinMode mode);
void GPIOA_setupOutput(uint8_t pinNumber, OutputPinMode mode, PinSpeed speed);
void GPIOC_setupOutput(uint8_t pinNumber, OutputPinMode mode, PinSpeed speed);

bool GPIOA_readPin(uint8_t pinNumber);


void GPIOA_setPin(uint8_t pinNumber);
void GPIOC_setPin(uint8_t pinNumber);

void GPIOA_clearPin(uint8_t pinNumber);
void GPIOC_clearPin(uint8_t pinNumber);

#endif

gpio.c

#include "stm32f1xx.h"
#include "gpio.h"

/**
 * @brief Initialize GPIO
 */
void GPIO_init(void){
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
    RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
    RCC->APB2ENR |= RCC_APB2ENR_IOPDEN;
    RCC->APB2ENR |= RCC_APB2ENR_IOPEEN;
}

/**
 * @brief Setup pin as an input
 * @param pinNumber the pin number
 * @param mode the input mode
 */
void GPIOA_setupInput(uint8_t pinNumber, InputPinMode mode){
    uint32_t pinNumberLocation, regValue;

    if(pinNumber < 8){
        pinNumberLocation = pinNumber << 2; // bit location
        regValue = (mode << 2) << pinNumberLocation;

        GPIOA->CRL &= ~(0b1111 << pinNumberLocation);   // clear the register
        GPIOA->CRL = regValue;
    }else{
        pinNumberLocation = (pinNumber - 8) << 2;   // bit location
        regValue = (mode << 2) << pinNumberLocation;

        GPIOA->CRH &= ~(0b1111 << pinNumberLocation);   // clear the register
        GPIOA->CRH = regValue;
    }
}

/**
 * @brief Setup port A pin as an output
 * @brief pinNumber the pin number
 * @brief mode the output mode
 * @brief speed the pin speed (lower results in less noise)
 */
void GPIOA_setupOutput(uint8_t pinNumber, OutputPinMode mode, PinSpeed speed){
    uint32_t pinNumberLocation, regValue;

    if(pinNumber < 8){
        pinNumberLocation = pinNumber << 2; // bit location
        regValue = ((mode << 2) << pinNumberLocation) + speed;

        GPIOA->CRL &= ~(0b1111 << pinNumberLocation);   // clear the register
        GPIOA->CRL |= regValue;
    }else{
        pinNumberLocation = (pinNumber - 8) << 2;   // bit location
        regValue = ((mode << 2) << pinNumberLocation) + speed;

        GPIOA->CRH &= ~(0b1111 << pinNumberLocation);   // clear the register
        GPIOA->CRH |= regValue;
    }
}

/**
 * @brief Setup port C pin as an output
 * @brief pinNumber the pin number
 * @brief mode the output mode
 * @brief speed the pin speed (lower results in less noise)
 */
void GPIOC_setupOutput(uint8_t pinNumber, OutputPinMode mode, PinSpeed speed){
    uint32_t pinNumberLocation, regValue;

    if(pinNumber < 8){
        pinNumberLocation = pinNumber << 2; // bit location
        regValue = ((mode << 2) << pinNumberLocation) + speed;

        GPIOC->CRL &= ~(0b1111 << pinNumberLocation);   // clear the register
        GPIOC->CRL |= regValue;
    }else{
        pinNumberLocation = (pinNumber - 8) << 2;   // bit location
        regValue = ((mode << 2) << pinNumberLocation) + speed;

        GPIOC->CRH &= ~(0b1111 << pinNumberLocation);   // clear the register
        GPIOC->CRH |= regValue;
    }
}

bool GPIOA_readPin(uint8_t pinNumber){
    uint16_t mask = 1 << pinNumber;

    if(GPIOA->IDR & mask)
        return true;
    else
        return false;
}

void GPIOA_setPin(uint8_t pinNumber){ GPIOA->BSRR = (1 << pinNumber);}
void GPIOC_setPin(uint8_t pinNumber){ GPIOC->BSRR = (1 << pinNumber);}

void GPIOA_clearPin(uint8_t pinNumber){ GPIOA->BSRR = ~(1 << (pinNumber + 16));}
void GPIOC_clearPin(uint8_t pinNumber){ GPIOC->BSRR = ~(1 << (pinNumber + 16));}

【问题讨论】:

  • GPIOA-&gt;CRL = GPIOB-&gt;CRL = GPIOC-&gt;CRL = GPIOD-&gt;CRL = GPIOE-&gt;CRL = crl; … - 这是一个非常糟糕的主意,甚至会损坏您的硬件。 .c 也不是头文件,它们不仅仅是声明,而是定义(也不应该在头文件中,inline 函数除外。
  • @olaf 我意识到*.c 不是头文件。为了简洁起见,我只是没有将头文件放在这里。我将编辑添加。另外,为什么设置所有 GPIOx-&gt;CRLGPIOx-&gt;CRH 是个坏主意?参考手册简单地说明它将所有引脚置于模拟输入模式。
  • 您应该获得更好的手册并将 GPIO 设置为外部硬件期望的状态。
  • @Olaf 我目前没有关联的外部硬件。我正在学习该设备。感谢您花时间教育我,但我仍然没有看到这个问题。在启动时将所有 GPIO 初始化为已知输入状态对我来说似乎没有争议,而且坦率地说,这甚至不是一个紧迫的问题,因为寄存器写入根本不起作用。你看到上面有什么不允许我写寄存器吗?
  • 我想知道你用那个设备做什么。没有 JTAG,没有 LED,没有按钮,没有晶体,什么都没有。如果你有一块死砖,你为什么还要写代码?

标签: c stm32


【解决方案1】:

PB2 上的二极管 - 工作代码

volatile uint32_t delay;
#include "stm32f10x.h"
int main(void)
{
  RCC->APB2ENR = RCC_APB2ENR_IOPBEN;
  GPIOB->CRL |= GPIO_CRL_MODE2_1;
  GPIOB->CRL &= ~GPIO_CRL_CNF2_0;

  while(1)
  {
    GPIOB->ODR |= GPIO_ODR_ODR2;   //or GPIO_ODR_2 depending of the .h header version 
    for(delay = 2000000; delay; delay--);
    GPIOB->ODR &= ~GPIO_ODR_ODR2;  //or GPIO_ODR_2 depending of the .h header version 
    for(delay = 2000000; delay; delay--);

    GPIOB->BSRR = 1 << 2;
    for(delay = 2000000; delay; delay--);
    GPIOB->BSRR = 1 << (2 + 16);
    for(delay = 2000000; delay; delay--);
  }
} // change 

【讨论】:

  • 我不得不将端口 C 引脚 13 全部更改,但除此之外,它工作得很好!
  • 当然,这两个部分都是一样的——只是想向您展示如何使用 ODR 和 BSRR 记录器。 ODR 你也可以切换 GPIOB->ODR ^= GPIO_ODR_ODR2;
【解决方案2】:

你的问题很简单。

您只是忘记启用外围时钟。默认情况下,Cortex micros 中的所有外设时钟都处于关闭状态。请阅读参考手册而不是数据表。查找“外设时钟启用寄存器”,并在适当的地方启用 GPIO 端口。

PS 在你的情况下是 RCC_APB2ENR

您在代码中犯了许多其他严重错误。我没有时间完整阅读,但 BSRR BRR 寄存器是只写的,您不能执行任何会导致读取的操作(即 |= 或 &=)。可能还有更多。仔细阅读 RM。另一个错误 - 您写入 BRR 的保留部分。 (保留高 16 位)如果您甚至写入正确的一半寄存器,您的 &= ~() 操作会做一些与您想象的不同的事情(从非法读取操作中抽象出来) - 它什么也不做。其实你根本没有阅读和理解 RM

所以对于未来: Cortex micros 可以访问外围寄存器 - 启动它们的时钟。甚至 EXTI(外部中断)也是外设,必须启用它的时钟。

一些外设甚至有多个时钟可以启用(例如 STM32F3xx 中的 ADC,其中模拟部分有多个时钟选项,并且必须单独启用 + 数字部分)

【讨论】:

  • 谢谢,非常有用!我完全明白你的意思!不幸的是,解决这些问题并没有解决根本原因。我仍然无法写入任何寄存器!我将更新以上内容以反映您的 cmets。我还将继续尝试其他设置,直到可以调试为止。再次感谢!
  • 如果您启用了时钟 - 您可以使用外围设备。没有奇迹。 = ~(1 = 1
猜你喜欢
  • 2021-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-27
  • 1970-01-01
  • 1970-01-01
  • 2022-09-25
相关资源
最近更新 更多