ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于寄存器或固件库用keil5做led灯的开发

基于寄存器或固件库用keil5做led灯的开发 今天来从头记录一下用STM32F103C8T6最小系统板做流水灯的全过程。从Keil环境搭建开始一路做到寄存器点灯、标准库点灯、逻辑分析仪看波形最后用HAL库加按键中断控制暂停和恢复。代码给得很全注释也写得很细照着做基本能跑通。第一步Keil 5 环境搭建装Keil MDK下载Keil µVision 5安装包一路Next装好装完记得用注册机激活。 装器件包打开Keil点击Pack Installer图标搜索STM32F1找到Keil.STM32F1xx_DFP安装。没装这个包新建工程时找不到STM32F103C8这个芯片。 装ST-Link驱动去ST官网下个ST-Link驱动装上插上板子能在设备管理器里看到ST-Link就说明OK了。第二步寄存器方式点灯任务一2.1 新建工程新建工程选芯片STM32F103C8弹出的Manage Run-Time Environment窗口直接关掉不用管。2.2 查手册找寄存器地址咱们要直接操作内存地址得先知道GPIO的寄存器在哪儿。查《STM32F103参考手册》以PB9为例它在CRH的bit[7:4]。MODE设10输出模式2MHzCNF设00通用推挽输出合起来就是0010/* 寄存器地址宏定义 */#defineRCC_APB2ENR(*(volatileunsignedint*)0x40021018)// 时钟使能寄存器#defineGPIOA_CRL(*(volatileunsignedint*)0x40010800)// PA配置低寄存器#defineGPIOA_ODR(*(volatileunsignedint*)0x4001080C)// PA输出数据寄存器#defineGPIOB_CRH(*(volatileunsignedint*)0x40010C04)// PB配置高寄存器#defineGPIOB_ODR(*(volatileunsignedint*)0x40010C0C)// PB输出数据寄存器#defineGPIOC_CRH(*(volatileunsignedint*)0x40011004)// PC配置高寄存器#defineGPIOC_ODR(*(volatileunsignedint*)0x4001100C)// PC输出数据寄存器/* 软件延时 */voidDelay_ms(volatileunsignedintt){unsignedinti;while(t--)for(i0;i800;i);// 72MHz下约800次循环约为1ms}/* LED控制函数 *//* 红灯点亮PA4高电平 */voidA_LED_LIGHT(void){GPIOA_ODR|(14);// PA4 1红灯亮GPIOB_ODR~(19);// PB9 0绿灯灭GPIOC_ODR~(115);// PC15 0蓝灯灭}/* 绿灯点亮PB9高电平 */voidB_LED_LIGHT(void){GPIOA_ODR~(14);// PA4 0红灯灭GPIOB_ODR|(19);// PB9 1绿灯亮GPIOC_ODR~(115);// PC15 0蓝灯灭}/* 蓝灯点亮PC15高电平 */voidC_LED_LIGHT(void){GPIOA_ODR~(14);// PA4 0红灯灭GPIOB_ODR~(19);// PB9 0绿灯灭GPIOC_ODR|(115);// PC15 1蓝灯亮}/* 主函数 */intmain(void){/* 1. 开时钟GPIOA、GPIOB、GPIOC */RCC_APB2ENR|(12);// 使能GPIOARCC_APB2ENR|(13);// 使能GPIOBRCC_APB2ENR|(14);// 使能GPIOC/* 2. 配置为推挽输出 *//* PA4CRL bit[19:16] 0001推挽10MHz */GPIOA_CRL~(0xF16);GPIOA_CRL|(0x116);/* PB9CRH bit[7:4] 0010推挽2MHz */GPIOB_CRH~(0xF4);GPIOB_CRH|(0x24);/* PC15CRH bit[31:28] 0011推挽50MHz */GPIOC_CRH~(0xF28);GPIOC_CRH|(0x328);/* 3. 初始状态全部熄灭 */GPIOA_ODR~(14);GPIOB_ODR~(19);GPIOC_ODR~(115);/* 4. 循环流水灯 */while(1){A_LED_LIGHT();// 红灯亮Delay_ms(1000);// 延时1秒B_LED_LIGHT();// 绿灯亮Delay_ms(1000);C_LED_LIGHT();// 蓝灯亮Delay_ms(1000);}}voidSystemInit(void){}接着我们可以在源代码基础上增加pc13亮的一步在GPIOC_CRH里把PC13也配成推挽输出位置是bit[23:20]。增加一个D_LED_LIGHT()函数让PC13输出高电平。主循环里加上第四个灯。/* 寄存器地址宏定义 */#defineRCC_APB2ENR(*(volatileunsignedint*)0x40021018)#defineGPIOA_CRL(*(volatileunsignedint*)0x40010800)#defineGPIOA_ODR(*(volatileunsignedint*)0x4001080C)#defineGPIOB_CRH(*(volatileunsignedint*)0x40010C04)#defineGPIOB_ODR(*(volatileunsignedint*)0x40010C0C)#defineGPIOC_CRH(*(volatileunsignedint*)0x40011004)#defineGPIOC_ODR(*(volatileunsignedint*)0x4001100C)/* 软件延时 */voidDelay_ms(volatileunsignedintt){unsignedinti;while(t--)for(i0;i800;i);}/* LED控制函数 *//* 红灯亮PA4 */voidA_LED_LIGHT(void){GPIOA_ODR|(14);// PA4 1GPIOB_ODR~(19);// PB9 0GPIOC_ODR~(115);// PC15 0GPIOC_ODR~(113);// PC13 0}/* 绿灯亮PB9 */voidB_LED_LIGHT(void){GPIOA_ODR~(14);// PA4 0GPIOB_ODR|(19);// PB9 1GPIOC_ODR~(115);// PC15 0GPIOC_ODR~(113);// PC13 0}/* 蓝灯亮PC15 */voidC_LED_LIGHT(void){GPIOA_ODR~(14);// PA4 0GPIOB_ODR~(19);// PB9 0GPIOC_ODR|(115);// PC15 1GPIOC_ODR~(113);// PC13 0}/* 板载LED亮PC13 */voidD_LED_LIGHT(void){GPIOA_ODR~(14);// PA4 0GPIOB_ODR~(19);// PB9 0GPIOC_ODR~(115);// PC15 0GPIOC_ODR|(113);// PC13 1}/* 主函数 */intmain(void){/* 1. 开时钟GPIOA、GPIOB、GPIOC */RCC_APB2ENR|(12);RCC_APB2ENR|(13);RCC_APB2ENR|(14);/* 2. 配置为推挽输出 *//* PA4CRL bit[19:16] 0001 */GPIOA_CRL~(0xF16);GPIOA_CRL|(0x116);/* PB9CRH bit[7:4] 0010 */GPIOB_CRH~(0xF4);GPIOB_CRH|(0x24);/* PC15CRH bit[31:28] 0011 */GPIOC_CRH~(0xF28);GPIOC_CRH|(0x328);/* PC13CRH bit[23:20] 0011 */GPIOC_CRH~(0xF20);GPIOC_CRH|(0x320);/* 3. 初始状态全部熄灭 */GPIOA_ODR~(14);GPIOB_ODR~(19);GPIOC_ODR~(115);GPIOC_ODR~(113);/* 4. 四个灯循环流水 */while(1){A_LED_LIGHT();// 红灯亮Delay_ms(1000);B_LED_LIGHT();// 绿灯亮Delay_ms(1000);C_LED_LIGHT();// 蓝灯亮Delay_ms(1000);D_LED_LIGHT();// 板载LED亮Delay_ms(1000);}}voidSystemInit(void){}实验任务二下载 STM32F10x_StdPeriph_Lib_V3.5.0 标准外设库。这个库ST现在不直接提供了网上搜“STM32F10x标准外设库V3.5.0”能找到很多镜像下载。解压之后你会看到这么几个关键文件夹Libraries、Project、Utilities。咱们主要用前两个。先在电脑上找个地方比如桌面建一个工程根目录名字随意比如叫 LED_StdLib。打开后在里面建几个子文件夹像这样textLED_StdLib/├── Start/ // 存放启动文件和CMSIS内核文件├── Library/ // 存放标准库外设驱动源文件├── User/ // 存放用户自己的代码main.c等└── Output/ // Keil编译输出文件自动生成往文件夹里拷文件Start文件夹要放三个东西从标准库包的路径 Libraries\CMSIS\CM3\CoreSupport 里把 core_cm3.h 拷过来。从 Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x 里把 system_stm32f10x.c、system_stm32f10x.h、stm32f10x.h 这三个文件拷过来。从 Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm 里把启动文件拷过来。STM32F103C8T6属于中等容量Medium-density对应的启动文件是 startup_stm32f10x_md.s。Library文件夹的操作很简单把标准库包路径 Libraries\STM32F10x_StdPeriph_Driver 下的 inc 和 src 两个文件夹整个复制到你的 Library 文件夹里。inc 里是所有外设的头文件.hsrc 里是对应的源文件.c。建议全部拷过来后面用哪个加哪个省得反复折腾。Keil里新建工程和分组打开Keil µVision 5点 Project → New µVision Project保存路径选到 User 文件夹工程名随便取比如 LED_StdLib点击保存。弹出的芯片选择窗口里选 STMicroelectronics → STM32F1 Series → STM32F103 → STM32F103C8。选完弹出一个 Manage Run-Time Environment 窗口直接点 Cancel 关掉咱们手动加文件不用它在线库。然后在左侧 Project 窗口里右键 Target 1选 Manage Project Items建三个组Start、Library、User。建好之后分别往三个组里添加文件Start 组添加 startup_stm32f10x_md.s、core_cm3.h、system_stm32f10x.c、stm32f10x.h Library 组从 Library/src 里挑你需要的源文件。点灯实验最少要加 stm32f10x_gpio.c、stm32f10x_rcc.c、misc.c User 组添加你的 main.c先建个空的或者直接把之前的代码贴进去配置魔术棒这是最容易出错的一步很多人编译报错或者串口printf用不了都是这里没配对。点魔术棒图标进入 C/C 选项卡Define全局宏定义填 USE_STDPERIPH_DRIVER。这个宏告诉编译器“我用的标准库”stm32f10x.h 里会根据这个宏去包含 stm32f10x_conf.h。Include Paths头文件路径把 Start、Library/inc、User 三个文件夹都加进来。少了任何一个编译都会报“找不到头文件”。另外Target 选项卡里勾上 Use MicroLIB。这个选项跟printf重定向有关虽然咱们这个实验不一定要用printf但勾上没坏处以后加串口调试就省事了。#includestm32f10x.h// 这个头文件已经通过路径和宏定义自动找到voidDelay_ms(unsignedintt){unsignedinti;while(t--)for(i0;i800;i);}voidLED_Init(void){GPIO_InitTypeDef GPIO_InitStructure;/* 开GPIOA/B/C时钟 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);/* 配置PA4推挽输出 */GPIO_InitStructure.GPIO_PinGPIO_Pin_4;GPIO_InitStructure.GPIO_ModeGPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_InitStructure);/* 配置PB9推挽输出 */GPIO_InitStructure.GPIO_PinGPIO_Pin_9;GPIO_Init(GPIOB,GPIO_InitStructure);/* 配置PC13推挽输出 */GPIO_InitStructure.GPIO_PinGPIO_Pin_13;GPIO_Init(GPIOC,GPIO_InitStructure);/* 初始全部熄灭 */GPIO_ResetBits(GPIOA,GPIO_Pin_4);GPIO_ResetBits(GPIOB,GPIO_Pin_9);GPIO_ResetBits(GPIOC,GPIO_Pin_13);}intmain(void){LED_Init();while(1){GPIO_SetBits(GPIOA,GPIO_Pin_4);GPIO_ResetBits(GPIOB,GPIO_Pin_9);GPIO_ResetBits(GPIOC,GPIO_Pin_13);Delay_ms(1000);GPIO_ResetBits(GPIOA,GPIO_Pin_4);GPIO_SetBits(GPIOB,GPIO_Pin_9);GPIO_ResetBits(GPIOC,GPIO_Pin_13);Delay_ms(1000);GPIO_ResetBits(GPIOA,GPIO_Pin_4);GPIO_ResetBits(GPIOB,GPIO_Pin_9);GPIO_SetBits(GPIOC,GPIO_Pin_13);Delay_ms(1000);}}任务三环境搭建HAL库 STM32CubeMX任务三其实有两种搭法纯手工搭HAL库工程或者用STM32CubeMX生成工程。CubeMX省事得多下面主要讲CubeMX的方式最后顺带提一下纯手工的思路。先装STM32CubeMX去ST官网下载STM32CubeMX需要注册账号。安装的时候有个坑要保证电脑上装了Java运行环境JRECubeMX是基于Java的。装完之后打开第一次用需要等它下载固件包Firmware Package也就是STM32CubeF1里面包含了HAL库的所有源文件。下载过程可能比较慢耐心等或者用离线包。用CubeMX新建工程打开CubeMX点 File → New Project在芯片选择界面搜索框里输入 STM32F103C8选中 STM32F103C8Tx点 Start Project。配置引脚调试接口左边 System Core → SYSDebug选 Serial Wire也就是SWD否则烧录一次后芯片可能被锁死。时钟源System Core → RCCHigh Speed Clock (HSE) 选 Crystal/Ceramic Resonator表示用外部晶振。LED引脚在右边的芯片引脚图上找到PA4、PB9、PC13分别左键点击选择 GPIO_Output。按键引脚找到PA0左键点击选择 GPIO_EXTI0。注意选EXTI0之后PA0就变成外部中断模式了不再是一般的输入输出。配置时钟树点上方 Clock Configuration 选项卡把HSE输入设为8MHzPLL Source选HSEPLL倍频系数设为 x9系统时钟源选 PLLCLK。这样系统时钟就是 8MHz × 9 72MHz。AHB和APB2不分频APB1分频系数选 /236MHz。配置GPIO详细参数回到 Pinout Configuration 选项卡在左边 System Core → GPIO 里逐个引脚配置PA4、PB9、PC13LEDMode选 Output Push PullPull选 No pull-up and no pull-downSpeed选 Low 或 Medium。初始电平可以先设High或Low代码里后面会改。 PA0按键Mode已经是 External Interrupt Mode with Falling edge trigger detection下降沿触发Pull选 Pull-up这样按键另一端接地平时上拉为高按下变低触发下降沿中断。配置NVIC中断左边 System Core → NVIC在 EXTI line0 interrupt 那一行勾上 Enabled。抢占优先级Preemption Priority设 2子优先级Sub Priority设 0随便设一个就行只要不跟其他中断冲突。生成工程点上方 Project Manager 选项卡Project Name随便取比如 LED_HAL Project Location选一个没有中文和空格的路径 Toolchain/IDE选 MDK-ARM V5 Code Generator勾上 Generate peripheral initialization as a pair of .c/.h files per peripheral这样每个外设的初始化代码会单独放在 gpio.c 里主文件 main.c 会干净很多点右上角的 GENERATE CODECubeMX就会自动生成完整工程并弹出“Open Project”按钮点一下直接用Keil打开。在Keil里补充用户代码CubeMX生成的工程在 main.c 里已经写好了所有初始化函数你只需要在两个固定的 USER CODE BEGIN 和 USER CODE END 标记之间加自己的代码。这些标记是CubeMX保留的重新生成代码时标记之外的内容会被覆盖标记之内的会保留。全局变量在 /* USER CODE BEGIN PV/ 和 /USER CODE END PV/ 之间加上volatile uint8_t run_flag 1;中断回调函数在 /USER CODE BEGIN 4/ 和 /USER CODE END 4/ 之间加上void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin GPIO_PIN_0) { run_flag !run_flag; } }主循环在 while(1) 里面/USER CODE BEGIN WHILE/ 和 /USER CODE END WHILE */ 之间加上流水灯逻辑if(run_flag){HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);HAL_Delay(1000);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);HAL_Delay(1000);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);HAL_Delay(1000);}else{HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);}
返回列表