嵌入式开发之 STM32自行车码表(图文)

笔者将从以下几个方面逐步深入地讲解STM32F103C8开发板的使用,并在Windows下编写一个简单的自行车码表程序:

  • 元器件
  • 环境搭建
  • 电路连接
  • 一个简单的LED闪烁程序
  • 自行车码表
    • 准备工作之 元器件
    • 准备工作之 电路连接
    • 准备工作之 环境搭建
      • CubeMX配置
      • KeilST-LINK下载程序调试程序
        • 下载并注册安装Keil Keil安装程序及注册机
        • 用keil打开CubeMX生成的工程文件
        • build
        • keilST-LINK下载和调试以blink为例
          • 出错误了
          • 解决方法一
          • 解决方法二
          • 连线图
    • 第一个实验 Hello
      • 串口每05s发出一次 Hello
    • 第二个实验 按钮检测
      • 按下按钮时输出pressed
    • 第三个实验 PA12中断
      • 下降沿中断触发的按钮检测
    • 第四个实验 定时器中断
    • 第五个实验 自行车码表

准备工作之 元器件


本实验需要的元器件有:

STM32F103C8开发板 1块





ST-LINK 1个





LED小灯 若干



330ohm电阻 若干



按钮 若干



子母线 若干




准备工作之 电路连接

正确的电路连接是成功的一半

首先我们测试 ST-LINK vccgnd 的效果,熟悉 面包板 的使用,通过 按钮 点亮 LED小灯 。电路图如下:

此处注意一定要接330ohm电阻,否则LED灯会因为电流过大而烧坏。笔者由于疏忽导致一个LED冒出了焦糊的味道… Sigh



反馈:LED灯成功由按钮控制点亮

结论:LED灯工作正常、ST-LINK供电正常、按钮、面包板正常

准备工作之 环境搭建

万事开头难

CubeMX配置

1、 首先下载安装CubeMX

2、 New Project 新建项目,选择对应开发板型号为STM32F103C8

3、 选择引脚功能, PA12和PA11作为GPIO的输入。

4、 generate code 发现需要下载cube库,一直下载不好,于是通过本地导入的方式搞定。在Cube官网下载STM32CubeF1补丁

5、 help->Install New Libraries->From Local导入库和补丁

6、 导入完成后尝试generate code,设置project属性,选择MDK-ARM v5为工具链。

7、code生成成功,但此时无法打开,因为我们还未安装Keil 工具链

Keil+ST-LINK下载程序/调试程序

下载并注册安装Keil Keil安装程序及注册机

安装keil

自动下载找不到stm32包

于是手动下载stm32F1pack STM32F1xx_DFP.1.1.0.pack

下载好后导入Pack Installer

用keil打开CubeMX生成的工程文件

可以看到MX已经生成了代码的框架,我们只需要填充我们自己的代码即可。

main.c中有一个自动生成的函数MX_GPIO_Init,帮我们进行了引脚的初始化。

void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct;

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pins : PA11 PA12 */
  GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

}

build

点击图中按钮

在output选项卡中选择生成hex文件

keil+ST-LINK下载和调试,以blink为例

下载驱动程序并安装 st-link驱动安装

这时插入st-link,在设备管理器中应该可以看到:

表示驱动已经成功安装。

进入Keil IDE下,编写一段程序,让tst即PIN13的LED灯亮灭。代码如下:

while (1)
  {
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
        HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
        HAL_Delay(3000);
        HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);
        HAL_Delay(3000);
  }

点击配置按钮,切到debug tab,选择st linker debugger 并点击setting

出错误了!

发现sw Device下显示错误信息:No target connected。这让我很奇怪,明明接线没有错误,且驱动正常。

解决方法一

下载时将Boot0置为1,下载完毕后置回0

找了一些资料,发现是Boot0没有置为1,所以找不到target,无法下载,只有将Boot0置为1,才能下载。Boot0是0的时候,板子的闪存作为boot的空间,Boot0是1的时候,系统存储作为启动空间。

Boot0和Boot1的详细说明文档——百度文库

于是笔者将boot0置为1后reset发现可以检测到target设备,这时可以download了

download 完毕后,再将Boot0置回0,此时reset后就可以运行了。这个过程中,我们相当于扮演了一回 厂家 的角色,将芯片里烧录的程序做了修改后重新发行了。

解决方法二:

按住Reset时点击烧录,然后立刻松开Reset

后来笔者才发现,还有更简单的方法。而错误原因也跟我刚刚想的有所不同,是因为在程序中配置了GPIO13\GPIO14的缘故。参考博文:

KEIL提示“No target connected”的解决方法(原创)

修改后成功烧录,实现了板子上的TST引脚灯的亮灭。

连线图:


第一个实验 Hello

啥也不说先上电路图

这是CubeMX的引脚定义处,将PA10定义为RX,PA9定义为TX(好像只能这么干,表示PA10发出数据,PA9接受数据<–stm32端–>)。PA11和PA12暂且没用。不管。

需要注意的是,USB-TTL转接口的RX要接STM32板子的TX口,转接口的TX要接STM32板子的RX口,这也许就是握手的含义吧。

这里按钮还未使用,可以先忽略。

左侧配置USART1为Single Wire(Half-Duplex)生成的代码较为接近最终代码。

接下来开始生成代码。

生成完毕。

/*main.c*/
void MX_USART1_UART_Init(void)
{
    //默认生成的代码结构
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 9600;//波特率要从115200改为9600
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  //huart1.Init.OverSampling = UART_OVERSAMPLING_16;//这个可以注释掉
  HAL_HalfDuplex_Init(&huart1);

}
/* stm32f1xx_hal_msp.c */
void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{

  GPIO_InitTypeDef GPIO_InitStruct;

  if(huart->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */
    __HAL_RCC_GPIOA_CLK_ENABLE();//enable GPIO的时钟
    __HAL_RCC_USART1_CLK_ENABLE();//enable UART的时钟
  /* USER CODE END USART1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();

    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9;// 9 发
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_10;// 10 收
    //GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    //GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */
  }

}
/* stm32f1xx_hal_msp.c */
/* 善后处理 */
void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)
{

  if(huart->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspDeInit 0 */
        __HAL_RCC_USART1_FORCE_RESET();
        __HAL_RCC_USART1_RELEASE_RESET();
  /* USER CODE END USART1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART1_CLK_DISABLE();

    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);

  }
  /* USER CODE BEGIN USART1_MspDeInit 1 */

  /* USER CODE END USART1_MspDeInit 1 */

}
// main.c
while (1)
  {
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
        HAL_UART_Transmit(&huart1,(uint8_t*)"Hello\r\n",7,500);// 0.5s 输出一次hello
        HAL_Delay(500);
  }

串口每0.5s发出一次 Hello

第二个实验 按钮检测

按钮检测部分的代码

//main.c
int main(void)
{

  /* USER CODE BEGIN 1 */
    GPIO_PinState state1,state2;
    char printStr[20];
  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();

  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
        state1 = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11);
        //按下按钮后read出来的state为0,否则是1
        state2 = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12);
        if(state1==0)
            HAL_UART_Transmit(&huart1,(uint8_t*)"Button 11 pressed\r\n",19,500);
        else if(state2==0)
            HAL_UART_Transmit(&huart1,(uint8_t*)"Button 12 pressed\r\n",19,500);
        else
            HAL_UART_Transmit(&huart1,(uint8_t*)"No Signal\r\n",11,500);
        //HAL_Delay(20);
  }
  /* USER CODE END 3 */

}

GPIO的初始化部分

/*main.c*/ /*GPIO初始化*/
void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct;

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pins : PA11 PA12 */
  GPIO_InitStruct.Pin = GPIO_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

按下按钮时,输出pressed

第三个实验 PA12中断

PA12中断响应

配置PA12下降沿触发中断

/*main.c*/
void MX_GPIO_Init(void)
{
    ······
    GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
    HAL_NVIC_SetPriority(EXTI15_10_IRQn,0,0);
    HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
    ······
}

当中断触发时,会去stm32f1xx_it.c中找到EXTI15_10_IRQHandler这个函数,在其中调用HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);如果PIN12的值符合下降沿的要求,则调用中 断 回 调 函 数

HAL_GPIO_EXTI_Callback,将中断标志位置为1,count++,在main函数的循环中可以进行标志位判断,如果为1说明有中断产生,需要清楚标志位并作出处理。

//stm32f1xx_it.c

/**
* @brief This function handles GPIO 12 interrupt
*/
void EXTI15_10_IRQHandler(void){
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);
}
//main.c
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
    if (GPIO_Pin == GPIO_PIN_12){
        PA12Flag = 1; // set interrupt signal
        PA12Cnt ++; //cnt++ for each interrupt
    }else{
        UNUSED(GPIO_Pin);
    }
}

int main(void)
{
  // ·····
  // ·····
  /* USER CODE BEGIN 1 */
    GPIO_PinState state1,state2;
    int strLen=0;
    char printStr[20];
  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/
  // ·····
  // ·····

  while (1)
  {
    if(PA12Flag==1)
        {
            PA12Flag=0; //clear interupt signal
            strLen = sprintf(printStr,"Button 12 pressed %d times\r\n",PA12Cnt);
            HAL_UART_Transmit(&huart1,(uint8_t*)printStr,strLen,500);
        }
  }
  /* USER CODE END 3 */

}

下降沿中断触发的按钮检测

第四个实验 定时器中断

这个实验涉及到时钟,于是需要开启时钟的宏定义

需要修改 stm32f1xx_hal_conf.h

main.c重点代码如下:

/*main.c*/

//······
//······
int TIMFlag = 0;
TIM_HandleTypeDef TIM_Handle;
//······
//······
//······

/*中断检测函数*/
void TIM3_IRQHandler(void){
    HAL_TIM_IRQHandler(&TIM_Handle);
}

int main(void)
{
    //······
    TIM_Init();
    //······
    while (1)
    {
        if(TIMFlag==1)
        {
            TIMFlag=0; //clear interupt signal
            strLen = sprintf(printStr,"Clk pulse\r\n");
            HAL_UART_Transmit(&huart1,(uint8_t*)printStr,strLen,500);
        }
  }
    }
}
/*回调函数*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *TIM_Handle){
    TIMFlag = 1;
}

/*初始化函数*/
void TIM_Init()
{
  TIM_Handle.Instance = TIM3;
  TIM_Handle.Init.Prescaler = 8000;
  TIM_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
  TIM_Handle.Init.Period = 199;
  HAL_TIM_Base_Init(&TIM_Handle);
  HAL_TIM_Base_Start_IT(&TIM_Handle); //start timer
  HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(TIM3_IRQn); // set priority
}
/* stm32f1xx_hal_msp.c */
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* TIM_Handle){
    __TIM3_CLK_ENABLE();
}

void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* TIM_Handle){
    __TIM3_CLK_DISABLE();
}

第五个实验 自行车码表

编写完整的码表程序,PA12的按钮表示车轮转了一圈,通过计数器可以得到里程,通过定时器中断得到的时间可以计算出速度;PA11的按钮切换模式,模式一在串口输出里程,模式二在串口输出速度。

思路:每0.2秒计算一次速度,因此按钮12的判断在时钟中断内部。车轮长度按2.56m计算,每0.2s算一次速度,用当前的PA12Cnt减去上一个0.2s的PA12Cnt,乘以车轮长度除以0.2s即可算出即时速度。

代码部分只需修改main.c即可

/*main.c的主要代码*/

int PA11Flag=0;
int PA12Cnt1=0;
int PA12Cnt2=0;
int PA12Flag=0;
int TIMFlag = 0;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void TIM_Init(void);

void TIM3_IRQHandler(void){
    HAL_TIM_IRQHandler(&TIM_Handle);
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
    if (GPIO_Pin == GPIO_PIN_12){
        PA12Flag = 1; // set interrupt signal
        PA12Cnt1 ++; //cnt++ for each interrupt
    }else{
        UNUSED(GPIO_Pin);
    }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *TIM_Handle){
    TIMFlag = 1;
}

int main(void)
{

  /* USER CODE BEGIN 1 */
    GPIO_PinState state1,state2;
    int strLen=0;
    char printStr[20];
  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
    TIM_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
        state1 = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11);
        state2 = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12);
        if(state1==0)
            PA11Flag = ~PA11Flag;
        if(TIMFlag==1)
        {
            TIMFlag=0; //clear interupt signal

            if(~PA11Flag)
            {
                strLen = sprintf(printStr,"Distance: %lfm\r\n",PA12Cnt1*2.56);
                HAL_UART_Transmit(&huart1,(uint8_t*)printStr,strLen,500);
            }
            else if(PA11Flag)
            {
                strLen = sprintf(printStr,"Speed: %lfm/s\r\n",(PA12Cnt1-PA12Cnt2)*2.56/0.2);
                HAL_UART_Transmit(&huart1,(uint8_t*)printStr,strLen,500);
            }
            PA12Cnt2 = PA12Cnt1;
        }

  }
  /* USER CODE END 3 */

}

PS: 读者可以尝试自己将PA11改为边沿触发式,方式类似PA12,相信你可以搞定的~

时间: 2024-10-03 13:38:46

嵌入式开发之 STM32自行车码表(图文)的相关文章

嵌入式开发之GCC编译器使用

嵌入式开发之GCC编译器使用 Linux系统下GCC是GNU推出的功能强大的性能优越的多平台编译器,是GNU的代表作之一.GCC 可以在多种硬件平台上编译出可执行程序,其执行效率一般比其他编译器高%20到%30.GCC编译器能将C C++语言程序汇编程序编译,链接成可执行程序.在linux文件没有统一的后缀,系统从文件的属性来区分可执行程序和不可执行程序.  1.1GCC编译程序的四个阶段 预处理(Pre-Processing) 编译(Comping) 汇编(Assembing) 连接(Link

嵌入式 LAB 3:自行车码表

前期工作准备 Mac OS X + Windows 7虚拟机 安装STM32 ST-LINK Unity.用于烧录程序. 安装STM32 ST-LINK所需要的驱动 安装Keil UVision 5,这是IDE,可以生成Hex文件,也可以烧录到板子上. 安装USB-TTL所以需要的驱动. 安装PUTTY,Windows下查看串口. 器材准备 STM32F103板子一个 杜邦线.面包线若干 ST-LINK V2 USB-TTL 按钮两个.面包板两个 Hex文件生成 切换成MDK-ARM 一定要选择

嵌入式开发之davinci--- 8148/8168/8127 中的High-DefinitionVideo Processing Subsystem (HDVPSS)

High-DefinitionVideo Processing Subsystem (HDVPSS) 这一章介绍了高清视频处理子系统(HDVPSS). 2.1导论 2.1.1 简介 HDVPSS 使用TI开发的算法,灵活的复合和融合引擎,各种高质量外部视频接口,实现视频/图像显示和采集处理功能.   2.1.3 缩略语 名称 定义 COMP Compositor 复合器 DEI De-Interlacer 去隔行 DEIH High quality De-Interlacer 高质量去隔行 DV

嵌入式开发之davinci--- DVRRDK, EZSDK和DVSDK这三者有什么区别

下载的时候选择信息要避免security类型的产品,这个是要审查的. DVRRDK是专门针对DVR的开发包是非公开的,针对安防的客户定制的,效率要高. EZSDK是开放的版本架构上使用openmax可以为android提供支持 DVSDK是老的版本名称使用codec engine架构,现在新的芯片DM8xxx都是EZSDK了 三种不同的软件框架,使用不同的方法来利用dsp工作. http://blog.csdn.net/zhouzhuan2008/article/details/8512128

嵌入式开发之davinci--- 8148/8168/8127 中的xdc 简介

XDC是TI公司为嵌入式实时系统可重用软件组件(在XDC里被成为packages,以下成为包)制定的一套标准.它包括一些有用的工具,标准的API函数,静态配置文件和打包(packaging)操作.XDC最主要的亮点在于它标准化了传递过程,并简化了在应用程序中引用目标平台下其他包的过程. 本文从一个简单的“Hello,World“入手,引领大家进入XDC开发世界. 在XDC开发世界中,首先需要明确两个概念,package和repository. 所谓package,类似与java里的jar文件或.

嵌入式开发之cgic库---cgi库的使用

很幸运!用C语言写CGI程序还可以有比较简单的方式,那就是我们可以借助使用第三方库CGIC(CGIC是一个功能比较强大的支持CGI开发的标准C库,并支持Linux, Unix 和Windows等多操作系统)来编写,省去了必须自己去遵循CGI规范来码砖的痛苦,使CGI程序的编写变的简单,下面是我第一次使用CGIC库来写的第一个CGI程序:第一步:从cgic官网(http://www.boutell.com/cgic/ 此网站好像不稳定,偶尔能开)上下载cgic库文件第二步:在Dev-C++中创建一

嵌入式开发之hi3519---GPIO 驱动

在一个嵌入式系统中使用最多的莫过于 通用输入输出 GPIO口.看到论坛中经常有朋友问海思为什么没有提供GPIO驱动.其实不然. 在海思SDK  xxx/osdrv/tools/board_tools/reg-tools-1.0.0/source/tools/下 提供了himm的读写工具源码.你也可以根据himm的源码来写一个文件设备操作的驱动.毕竟轮子已经有了,我们就没必要再去造轮子了. 这个工具是用来 配置海思寄存器的.当然可以稍加改造或者在应用直接使用来控制通用寄存器. 根据海思提供资料 ,

嵌入式开发之hi3516---GV7601 SPI通信问题

http://blog.csdn.net/qq_29350001/article/details/52669964 http://blog.csdn.net/zqj6893/article/details/50386921 http://blog.csdn.net/yangzhongxuan/article/details/8021743 http://www.ebaina.com/bbs/thread-5035-1-1.html

嵌入式开发之davinci--- 8148/8168/8127 中的添加算饭scd 场景检测

Osd Scd (1)     Introduction over view a)         scene change detection block diagram a)         graph b)         resvolution d1:720x576(pal)-25fps 720x480 30-fps(ntsc)--------------704x576 ti cif:352x288 (支持的处理帧) quwu:1024x768/4 c)         说明: The