嵌入式开发之 STM32自行车码表(图文)
来源:互联网 发布:java xml映射class 编辑:程序博客网 时间:2024/05/09 16:19
笔者将从以下几个方面逐步深入地讲解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 vcc 和 gnd 的效果,熟悉 面包板 的使用,通过 按钮 点亮 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.cwhile (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.cint 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.cvoid 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,相信你可以搞定的~
- 嵌入式开发之 STM32自行车码表(图文)
- 嵌入式 LAB 3:自行车码表
- 嵌入式lab3——自行车码表
- 自行车码表
- Lab3: 自行车码表
- Lab3:自行车码表
- Lab 3:自行车码表
- LAb3-自行车码表
- 实验三:自行车码表
- Lab 3:自行车码表
- 自行车码表CPU选型
- 自行车码表使用说明—SIGMA(西格玛) BC906
- PRi——自行车码表
- 中断驱动的自行车码表
- STM32嵌入式入门必看之文章-----介绍非常详细!(学习STM32的理由!!!!)
- 视频教程:嵌入式stm32项目开发之心率检测仪的设计与实现
- 免费视频教程:嵌入式stm32项目开发之心率检测仪的设计与实现
- 视频教程免费分享:嵌入式stm32项目开发之心率检测仪的设计与实现
- codeforces_660C. Hard Process(尺取法)
- 递归算法 未名湖边的烦恼 java
- 1--ip首部
- 节点祖先
- Android Binder机制(1):Binder架构分析
- 嵌入式开发之 STM32自行车码表(图文)
- c函数指针详解
- Redis笔记_5
- 【LeetCode】299 Bulls and Cows (java实现)
- 低功耗蓝牙BLE之修改广播间隔等参数
- c++ 初学者,求助,课后习题。
- NavigationController 详解
- 离合器 油门 刹车使用要领
- 2--第四层