STM32F103实现自行车里程计

STM32F103实现自行车里程计

  • STM32F103实现自行车里程计
    • 器材准备
    • 连接
    • 环境搭建
      • 软件安装
      • 软件使用
        • STM32 CubeMX
        • Keil UVision 5
        • STM32 ST-LINK Unity
    • 元器件测试以及简单逻辑代码框架
      • 闪烁小灯
      • 串口通信
    • 自行车里程计的实现
      • 时间中断
      • 自行车里程计
    • 总结

器材准备

  • STM32F103板子一个
  • 杜邦线、面包线、面包板若干
  • ST-LINK V2 一个(烧录程序)
  • USB-TTL 一个(做串口输出测试)
  • 按钮两个
  • Win电脑一台(建议不要在mac下开发,实在太难受了)

连接

  • usb to ttl 和 stm32开发板连接图
    [id](#Table 1)
USB STM32
3.3V 3.3V
GND GND
TXD A10
RXD A9

- stm32和st-link直接按照对应的接口相连即可

环境搭建

软件安装

  • Keil UVision 5
  • STM32 ST-LINK Unity
  • STM32 CubeMX
  • STM32 ST-LINK所需要的驱动
  • USB-TTL所以需要的驱动 CH341SER for Win(这个视你的器件不同而定)
  • PUTTY(windows下查看串口)

软件使用

开始之前记得一定要先将驱动都安装好!

STM32 CubeMX

​ 进入STM32 CubeMX之后就可以使用了,我们选择STM32F103 - LQFP48 - 64Flash - 20RAM(老师提供的板子)进行配置。

​ STM32F103只是一块板子,我们先需要对板子进行简单的设计,比如哪个引脚做输入,哪个引脚做输出等等,直接使用STM32实现自行车里程计可能会出现一些完全想不到的错误,实现一些简单的功能对所用的器件进行简单测试(譬如是不是能正常工作的)。

由于我们使用的板子是STM32F103,直接使用CubeMX会去下载这个库,但是它自己下载速度超级慢,这里可以使用浏览器下载到1.3.0版本的库,然后使用CubeMX下一个补丁就可以了,速度会快很多。

​ 然后Project->Settings修改工程设置,将Toolchain修改成MDK-ARM V5(否则默认是EWARM),这样我们之后才可以使用Keil进行项目管理。最后,Project -> Generate Code即可生成代码。

Keil UVision 5

使用上面的工具 设置好引脚之后就可以开始写代码了,直接在CubeMX中选择OpenProject就可以打开工程文件。

  • 将ST-LINK与STM32对应的借口连起来,然后调好设置:Flash->Configure Flash Tools->Debug->Settings查看连接是否正确,连接成功将如下图所示。

  • 使用Keil生成HEX文件然后利用ST-LINK烧录进STM32中,需要在Flash->Configure Flash Tools->Output中勾选Create HEX File选项,如下图所示。其实不使用ST-LINK Unity,直接使用Keil也是能够烧录下板的,但是需要使用Patch Installer下载一个Algorithm,下载速度特别慢,因此这里还是使用生成HEX文件再利用ST-LINK Unity进行烧录的方法。

STM32F103实现自行车里程计_第1张图片

  • Flash->Configure->Utilities在Add Output File to Group选定Application/MDK-ARM,不然在目录下找不到对应的HEX文件。

STM32F103实现自行车里程计_第2张图片

  • Flash->Configure->Utilities->Settings选定Reset and Run,在STM32上使用的是ISP编程,烧录时要对芯片中Flash原有的数据全部抹除,烧进自己的数据,然后重新启动,bootloader起来后会跑对应的指令(在使用Keil下板时有用)。

  • 注意!每次重新打开Keil一定要记得将上面的流程全部都走一遍,不然就会出错,真是想吐槽这个IDE,一旦重启原来的设定就都不见了。
  • 将ST-LINK撤下,换上USB-TTL并接入PC(记得提前装好驱动),打开ST-LINK,导入一个HEX文件,如果像上面使用Keil建好的工程,则HEX文件在Project name/MDK-ARM/Project name下

STM32F103实现自行车里程计_第3张图片

  • 点击connect to the target,如果报错了说明是你的线路没接对或者驱动没装好
  • 点击Target->Erase Chip将芯片内Flash的数据全部擦除
  • 点击Target->Program & Verify,烧录数据,完成后烧录成功,芯片会执行对应的功能

元器件测试以及简单逻辑代码框架

闪烁小灯

  • 芯片引脚设置(PA9作为输出)
    STM32F103实现自行车里程计_第4张图片

  • 线路连接

    先使用STM32的3.3V输出检验二极管的方向,然后如下图所示小灯一个脚接GND,一个脚接A9,这样当A9输出高电平时小灯就会亮了。

STM32F103实现自行车里程计_第5张图片FullSizeRender

小灯在成功点亮之后说明杜邦线和小灯都是正常的(否则小灯可能就是坏的,或者线是坏的,只是极有可能的,我们连续测了三个小灯全是坏的,开始还以为是烧录少错了,查了好久,换成蜂鸣器做测试的时候居然响了。。。)。然后就可以使用面包板给小灯串联一个按钮,检测按钮和面包板的好坏,盒子里的器材中,我们又检测出一个按钮是坏的(按与不按小灯都会有信号)。

  • 代码

​ 我们之前对A9进行了设置,这个在工程中的main.c函数内有具体的体现。

void MX_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;  //定义初始化结构

    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_9;   //对端口9进行初始化
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA,&GPIO_InitStruct);
}

​ 自己写代码实现小灯闪烁

while(1) {
  HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_9); //转换A9的输出状态
  HAL_Delay(1000);
}
  • 总结

​ 至此,我们实现使用STM32开发板完成了闪烁小灯的任务,通过以上的步骤,可以将所有的坏的元器件过滤掉,避免之后的bug大串联到焦头烂额,并且整体跑通了STM32开发的流程,之后的开发按照这个模板来就可以了。

串口通信

  • 芯片引脚设置

STM32F103实现自行车里程计_第6张图片

​ 将PA9,PA10设置为串口通信用的TX和RX(对应接法已经在上面的表格中表达出来了),注意要在CubeMX中将USART1设置为半双工(Cortex-M3的单线程半双工模式),将PA12和PA11设为Input,我们将两个按钮分别接到这两个引脚和GND,这样芯片可以根据两个引脚的输入进行交互。

  • 线路连接

    原谅我这鬼畜的波浪线,如图所示,直接将两个USB口全部接到电脑上,就可以不用重复的插拔插拔了(共用了电源线)。A11,A12分别接上两个button用来监测按钮的状态,将A11,A12内部设置为上拉到输入模式(接一个上拉电阻),在main()函数内进行循环检测,并在其中一个按钮按下时,输出两个按钮的状态信息。
    这里写图片描述
    STM32F103实现自行车里程计_第7张图片

​ 按键已经去抖动,上方是按键状态后得到的输出。

  • 代码

main.c中进行修改

/*-------------------------------main.c------------------------------------*/
//....
void UART0_Init(UART_HandleTypeDef* UartHandle){
    UartHandle->Instance = USART1;
    UartHandle->Init.BaudRate = 9600;
    UartHandle->Init.WordLength = UART_WORDLENGTH_8B;
    UartHandle->Init.StopBits = UART_STOPBITS_1;
    UartHandle->Init.Parity = UART_PARITY_NONE;
    UartHandle->Init.HwFlowCtl = UART_HWCONTROL_NONE;
    UartHandle->Init.Mode = UART_MODE_TX_RX;

    HAL_UART_Init(UartHandle);    //初始化UART
}
//根据输入对给定的端口进行初始化,设置波特率等等

#define MASK 0xFF
//给按钮去抖动检测的长度(8bit)

void anti_jitter(int *bit, int state)
{
    *bit <<= 1;
    *bit&= MASK;
    *bit|=state;
}
//按钮不只是在开合的时候会有抖动,不按下的时候线路都会时断时续,所以去抖动一定要加上

int main(void)
{
//...
    char str[30];
    int Pin_11_Bitcount = 0,Pin_12_Bitcount=0;
    int Pin_11_State=0,Pin_12_State=0;
    int Change_Flag=1;
    UART_HandleTypeDef UartHandle;
    UART0_Init(&UartHandle);
//设置变量,并对UART0进行初始化

    HAL_UART_Transmit(&UartHandle, (uint8_t*)"Hello, World!\r\n", 16, 500);
//在Reset之后立刻向串口输出信息Hello, World! 这里记得要加入\r\n,否则输出的全是糊的
    while(1)
    {
        int count;
        GPIO_PinState state_11;
        GPIO_PinState state_12;

        state_11 = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11);
        state_12 = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12);
      //读取11、12的端口的状态,但是直接使用这些状态会有抖动的情况

        HAL_Delay(5);
      //延迟五秒用来延长读取状态的间隔,用来去抖动

        anti_jitter(&Pin_11_Bitcount,state_11);
        anti_jitter(&Pin_12_Bitcount,state_12);
      //对两个端口去抖动,state_11,state_12是当下的状态,Pin_11,Pin_12是去抖动自后的真实状态

        if(Pin_11_Bitcount != 0 && Pin_11_State == 0)
        {
                Pin_11_State = 1;
                Change_Flag = 1;
        }
        if(Pin_12_Bitcount !=0 && Pin_12_State == 0)
        {
                Pin_12_State = 1;
                Change_Flag = 1;
        }
        if(Pin_11_Bitcount == 0 && Pin_11_State == 1)
        {
                Pin_11_State = 0;
                Change_Flag = 1;
        }
        if(Pin_12_Bitcount == 0 && Pin_12_State == 1)
        {
                Pin_12_State = 0;
                Change_Flag = 1;
        }
      //当其中某一个端口的状态发生改变的时候设定Change_Flag标记,用来作为输出的标记

        if(Change_Flag == 1)
        {
            Change_Flag = 0;
            count = sprintf(str,"Pin 11:%d\r\n",Pin_11_State);
            HAL_UART_Transmit(&UartHandle, (uint8_t*)str, count, 500);
            count = sprintf(str,"Pin 12:%d\r\n",Pin_12_State);
            HAL_UART_Transmit(&UartHandle, (uint8_t*)str, count, 500);
          //向串口输出11和12端口的状态信息
        }
    }
}

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;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  //添加上拉电阻
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

​ 在老师提供的攻略中需要在stm32f1xx_hal_msp.c中进行端口配置,但是使用CubeMX后生成的文件中已经做好了基本的配置,这里就可以不做额外的配置了,如果使用GCC进行裸配也是可以的,之前已经搭好了ARM的交叉编译环境,但是引脚的配置等等实在太繁琐了,这里还是使用CubeMX配置了。

  • 总结
    • 与体系结构中类似,使用anti_jitter来去抖动,使得按钮的逻辑功能比较鲁邦
    • 使用Change_Flag,只有状态改变时才向串口输出信息,避免大量的信息向端口输出挤爆端口还看不清

自行车里程计的实现

时间中断

  • 设定定时器,内部时钟频率为8MH,因此我们设置Prescaler为8000,这样时钟回调每1ms会被调用 一次。

STM32F103实现自行车里程计_第8张图片

  • 代码:
/*---------------------------------main.c----------------------------------*/
TIM_HandleTypeDef T_Handler;

int T_Flag = 0;
int T_Count = 0;

void TIM3_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&T_Handler);
}
//攻略中说在msp中设定这个,但是由于在msp中没有办法传递参数,只能够用extern来做全局变量,用起来实在太奇怪,所以就干脆将相关的初始化函数全部搬到main.c中来写,感觉反而自然一些。

void TIM_Init()
{
    T_Handler.Instance = TIM3;
    T_Handler.Init.Prescaler = 8000;
    T_Handler.Init.CounterMode = TIM_COUNTERMODE_UP;
    T_Handler.Init.Period = 199;
        //设定时间中断的参数,每8000唤醒回调一次,总共唤醒199次

    HAL_TIM_Base_Init(&T_Handler);
    HAL_TIM_Base_Start_IT(&T_Handler);
        //初始化并启用时间中断的检测
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    T_Flag = 1;
}
//这个就是时间中断会调用的回调函数,每次调用就设置一次标记

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* 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 */
  UART_HandleTypeDef UartHandle;
  UART0_Init(&UartHandle);
  TIM_Init();
  //初始化时间、端口

  HAL_UART_Transmit(&UartHandle, (uint8_t*)"Hello, World!\r\n", 16, 500);

  while (1)
  {
        int count;
        if(T_Flag == 1)  //每次时间标志被中断置位后,就计数一次,然后输出
        {
            T_Flag = 0;
            T_Count++;
            count = sprintf(str,"Time clicks: %d\r\n",T_Count);
            HAL_UART_Transmit(&UartHandle,(uint8_t *)str,count,500);
        }
    }
}
//上面的函数完成了利用时间中断规律性计数
/*----------------------stm32f1xx_hal_msp.c---------------------------*/
//在HAL_MspInit函数中增加下面两条,用来设定时间中断的优先级以及时间中断的回调函数
    HAL_NVIC_SetPriority(TIM3_IRQn,0,0);
    HAL_NVIC_EnableIRQ(TIM3_IRQn);

//增加以下两条函数,用来在开始和关闭的时候启动和关闭时钟
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim)
{
    __TIM3_CLK_ENABLE();
}
void HAL_TIME_Base_MspDeInit(TIM_HandleTypeDef* htim)
{
    __TIM3_CLK_DISABLE();
}
  • 总结

​ 上面使用了时间中断来对计数器不断累加

自行车里程计

  • 上方使用了时间中断,这里将PA11、PA12也改造成时间中断,使用下降沿唤醒中断,实现前后台系统的自行车里程计。总共有两个按钮对应两个端口,按钮1每次按下时表示自行车轮已经转过一圈,按钮2每次按下表示在两个不同的模式间切换(显示总路程还是显示平均速度)。由于STM32自带RESET键,这里就不添加RESET按钮清零了。使用时间中断的相关说明在上面解释了。

  • 代码

/*---------------------------main.c-------------------------------------*/
/** ****************************************************************************** * File Name : main.c * Description : Main program body ****************************************************************************** * * COPYRIGHT(c) 2016 STMicroelectronics * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of STMicroelectronics nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************** */
/* Includes ------------------------------------------------------------------*/
#include "stm32f1xx_hal.h"

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);

/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/

/* USER CODE END PFP */

/* USER CODE BEGIN 0 */

#define MASK 0xFF
#define DELAYTIME 5
#define SPDMODE 1
#define DISMODE 0
#define CHANGEMODE(M) (M = 1-M) //
#define Tire_Perimeter 2.3 //轮胎周长

char str[30];
int Pin_12_Count = 0,Pin_12_Flag = 0;  //一个计数器,一个标记
TIM_HandleTypeDef T_Handler;
int mode=0;
float Distance = 0;                 //记录行驶总距离

int T_Flag = 0;
int T_Count = 0;

int Change_Mode_Flag = 0;

void UART0_Init(UART_HandleTypeDef* UartHandle){
    UartHandle->Instance = USART1;
    UartHandle->Init.BaudRate = 9600;
    UartHandle->Init.WordLength = UART_WORDLENGTH_8B;
    UartHandle->Init.StopBits = UART_STOPBITS_1;
    UartHandle->Init.Parity = UART_PARITY_NONE;
    UartHandle->Init.HwFlowCtl = UART_HWCONTROL_NONE;
    UartHandle->Init.Mode = UART_MODE_TX_RX;

    HAL_UART_Init(UartHandle);
}
//与前面的一样,对UART端口做初始化

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

void TIM_Init()
{
    T_Handler.Instance = TIM3;
    T_Handler.Init.Prescaler = 8000*5;
    T_Handler.Init.CounterMode = TIM_COUNTERMODE_UP;
    T_Handler.Init.Period = 199;
    HAL_TIM_Base_Init(&T_Handler);
    HAL_TIM_Base_Start_IT(&T_Handler);
}
//上面对于时间中断的处理仍然一样,不过把间隔时间设为原来的5倍

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    T_Flag = 1;
    T_Count++;
}
//后台时间中断处理,每次时间中断唤醒都会置标记, 前台while循环检测到之后对其进行处理

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if(GPIO_Pin == GPIO_PIN_12) {
        Distance+=Tire_Perimeter;
    } else if(GPIO_Pin == GPIO_PIN_11)
    {
        Change_Mode_Flag = 1;
    } else {
        UNUSED(GPIO_Pin);
    }
}
//后台处理引脚电平下降沿造成的中断,PA12用来记录里程的增加,PA11用来做模式改变的标记

/* USER CODE END 0 */

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* 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 */
    char str[30];
    int Pin_11_Bitcount = 0,Pin_12_Bitcount=0;
    int Pin_11_State=0,Pin_12_State=0;
    int Change_Flag=1;
    UART_HandleTypeDef UartHandle;
    UART0_Init(&UartHandle);
    TIM_Init();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
    HAL_UART_Transmit(&UartHandle, (uint8_t*)"Hello, World!\r\n", 16, 500);

  while (1)
  {
        int count;
  /* USER CODE END WHILE */
    //前台检测是否需要改变模式
        if(Change_Mode_Flag == 1)
        {
            Change_Mode_Flag = 0;
            CHANGEMODE(mode);
          //根据不同的模式来显示状态,串口传送信息
            if(mode == SPDMODE)
                count = sprintf(str,"Changed Into Speed Mode\r\n");
            else count = sprintf(str,"Changed into Distance Mode\r\n"); 
            HAL_UART_Transmit(&UartHandle,(uint8_t *)str,count,500);
        }
    //前台检测到时间中断产生了,信息更新
        if(T_Flag == 1)
        {
            T_Flag = 0;

          //根据模式的不同串口传输不同的数据
            if(mode == SPDMODE)
                count = sprintf(str,"Speed : %f\r\n",Distance/T_Count);
            else count = sprintf(str,"Distance : %f\r\n",Distance);
            HAL_UART_Transmit(&UartHandle,(uint8_t *)str,count,500);
        }
    }
    /* USER CODE END 3 */

}

/** System Clock Configuration */
void SystemClock_Config(void)
{

  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = 16;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  HAL_RCC_OscConfig(&RCC_OscInitStruct);

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);

  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

/* USART1 init function */
void MX_USART1_UART_Init(void)
{

  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  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);

}

/** Configure pins as * Analog * Input * Output * EVENT_OUT * EXTI */
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);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

#ifdef USE_FULL_ASSERT

/** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */

}

#endif

/** * @} */ 

/** * @} */ 

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
/*------------------------------stm32f1xx_hal_msp.c-----------------------------*/
/** ****************************************************************************** * File Name : stm32f1xx_hal_msp.c * Description : This file provides code for the MSP Initialization * and de-Initialization codes. ****************************************************************************** * * COPYRIGHT(c) 2016 STMicroelectronics * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of STMicroelectronics nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************** */
/* Includes ------------------------------------------------------------------*/
#include "stm32f1xx_hal.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/** * Initializes the Global MSP. */
void HAL_MspInit(void)
{
  /* USER CODE BEGIN MspInit 0 */

  /* USER CODE END MspInit 0 */

  __HAL_RCC_AFIO_CLK_ENABLE();

  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

  /* System interrupt init*/
  /* MemoryManagement_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(MemoryManagement_IRQn, 0, 0);
  /* BusFault_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(BusFault_IRQn, 0, 0);
  /* UsageFault_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(UsageFault_IRQn, 0, 0);
  /* DebugMonitor_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DebugMonitor_IRQn, 0, 0);
  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);

  /* USER CODE BEGIN MspInit 1 */
    HAL_NVIC_SetPriority(EXTI15_10_IRQn,0,0);
    HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);

    HAL_NVIC_SetPriority(TIM3_IRQn,0,0);
    HAL_NVIC_EnableIRQ(TIM3_IRQn);
  //设置时间中断和引脚电平下降沿引发的中断,优先级
  /* USER CODE END MspInit 1 */
}

void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(huart->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* 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;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

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

        GPIO_InitStruct.Pin = GPIO_PIN_11;
        GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;  //对引脚进行设置,使用下降沿触发
        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);                                                                
  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */
  }

}

void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)
{

  if(huart->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspDeInit 0 */

  /* 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 */

}

/* USER CODE BEGIN 1 */

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim)
{
    __TIM3_CLK_ENABLE();
}
void HAL_TIME_Base_MspDeInit(TIM_HandleTypeDef* htim)
{
    __TIM3_CLK_DISABLE();
}

/* USER CODE END 1 */

/** * @} */

/** * @} */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
/*----------------------------stm32f1xx_it.c----------------------------*/
/** ****************************************************************************** * @file stm32f1xx_it.c * @brief Interrupt Service Routines. ****************************************************************************** * * COPYRIGHT(c) 2016 STMicroelectronics * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of STMicroelectronics nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************** */
/* Includes ------------------------------------------------------------------*/
#include "stm32f1xx_hal.h"
#include "stm32f1xx.h"
#include "stm32f1xx_it.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/* External variables --------------------------------------------------------*/

/******************************************************************************/
/* Cortex-M3 Processor Interruption and Exception Handlers */ 
/******************************************************************************/

/** * @brief This function handles Non maskable interrupt. */
void NMI_Handler(void)
{
  /* USER CODE BEGIN NonMaskableInt_IRQn 0 */

  /* USER CODE END NonMaskableInt_IRQn 0 */

  /* USER CODE BEGIN NonMaskableInt_IRQn 1 */

  /* USER CODE END NonMaskableInt_IRQn 1 */
}

/** * @brief This function handles Hard fault interrupt. */
void HardFault_Handler(void)
{
  /* USER CODE BEGIN HardFault_IRQn 0 */

  /* USER CODE END HardFault_IRQn 0 */
  while (1)
  {
  }
  /* USER CODE BEGIN HardFault_IRQn 1 */

  /* USER CODE END HardFault_IRQn 1 */
}

/** * @brief This function handles Memory management fault. */
void MemManage_Handler(void)
{
  /* USER CODE BEGIN MemoryManagement_IRQn 0 */

  /* USER CODE END MemoryManagement_IRQn 0 */
  while (1)
  {
  }
  /* USER CODE BEGIN MemoryManagement_IRQn 1 */

  /* USER CODE END MemoryManagement_IRQn 1 */
}

/** * @brief This function handles Prefetch fault, memory access fault. */
void BusFault_Handler(void)
{
  /* USER CODE BEGIN BusFault_IRQn 0 */

  /* USER CODE END BusFault_IRQn 0 */
  while (1)
  {
  }
  /* USER CODE BEGIN BusFault_IRQn 1 */

  /* USER CODE END BusFault_IRQn 1 */
}

/** * @brief This function handles Undefined instruction or illegal state. */
void UsageFault_Handler(void)
{
  /* USER CODE BEGIN UsageFault_IRQn 0 */

  /* USER CODE END UsageFault_IRQn 0 */
  while (1)
  {
  }
  /* USER CODE BEGIN UsageFault_IRQn 1 */

  /* USER CODE END UsageFault_IRQn 1 */
}

/** * @brief This function handles Debug monitor. */
void DebugMon_Handler(void)
{
  /* USER CODE BEGIN DebugMonitor_IRQn 0 */

  /* USER CODE END DebugMonitor_IRQn 0 */
  while (1)
  {
  }
  /* USER CODE BEGIN DebugMonitor_IRQn 1 */

  /* USER CODE END DebugMonitor_IRQn 1 */
}

/** * @brief This function handles System tick timer. */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  HAL_SYSTICK_IRQHandler();
  /* USER CODE BEGIN SysTick_IRQn 1 */

  /* USER CODE END SysTick_IRQn 1 */
}

/******************************************************************************/
/* STM32F1xx Peripheral Interrupt Handlers */
/* Add here the Interrupt Handlers for the used peripherals. */
/* For the available peripheral interrupt handler names, */
/* please refer to the startup file (startup_stm32f1xx.s). */
/******************************************************************************/

/* USER CODE BEGIN 1 */
void EXTI15_10_IRQHandler(void) {
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_11);
}
//引脚引发的中断

/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
  • 总结

​ 上方使用了前后台的系统实现了自行车里程计,后台响应时间中断和按钮设置标记,前台根据标记进行处理并串口传送信号。

总结

  1. 嵌入式系统的相关开发一定要小心小心再小心,比如元器件的测试一定要从最简单的一样一样全部检测好之后再开始,不然如果出现按钮坏了+串口坏了+软件问题+环境问题等一系列问题叠加在一起之后,bug根本都找不出来,如果不做基层的检测,直接开始里程计的开发很有可能出现各种意向不到的意外。
  2. 使用前后台的方法实现自行车里程计十分简单有效,其一按钮的响应使用中断的方式可以避免使用轮询时没轮到按钮的时间块儿按钮没法响应。
  3. 上面是使用按钮的按下模拟轮胎旋转过一圈,最后实体化中,可以利用磁铁之间的引力峰值等等来表示轮胎转过了一圈,先对该传感器做单体测试,然后用它取代掉试验中所用的轮胎,串口通信处接一个接受串口的显示屏,总体就完成了

你可能感兴趣的:(STM32F103实现自行车里程计)