STM32基础实验
上一节介绍了江苏学蠡信息科技有限公司的无线传感器网络实验平台关于NB-IOT实验所需要的各项硬件以及所需要的软件组成部分,这一章,主要是使用STM32F103单片机的基础实验进行介绍和演示。
1. 使用STM32CubeMX创建工程
STM32的开发目前一共有三种方式:
- 直接配置寄存器
直接配置寄存器的方式源于对51单片机的学习,但是使用直接配置寄存器的方式来学习STM32,对于新手小白而言无疑是灭顶之灾。STM32的寄存器远多于51系列单片机十多倍,这么多的寄存器能全记下来已经是实属不易,更别提去配置寄存器了。虽然也有追求大道根本的大佬会使用这种办法,可以直接接触到底层更好的掌握原理。 - 标准库
因为STM32的寄存器数量太多,ST公司就为每一款芯片都编写了一份库文件,就是常见的stm32F1xx什么之类的,在这些.c和.h文件中将各芯片所要用到的一些常用量的宏定义和一些外设都通过结构体变量封装起来。所以作为开发人员只需要配置结构体变量成员就可以修改外设的配置寄存器,从而选择不同的功能。目前使用标准库进行学习和开发STM32的人数最多,属于主流学习和开发方式。 - HAL库
使用HAL库是目前ST公司主推的开发方式,并且目前ST公司已经停止了对标准库的更新维护,以此来看未来ST公司想要的是将HAL库开发作为STM32的主流开发方式。
HAL,英文全称 Hardware Abstraction Layer,即硬件抽象层。HAL 库是 ST 公司提供的外设驱动代码的驱动库,用户只需要调用库的 API 函数,便可间接配置寄存器。我们要写程序控制 STM32 芯片,其实最终就是控制它的寄存器,使之工作在我们需要的模式下,HAL库将大部分寄存器的操作封装成了函数,我们只需要学习和掌握 HAL 库函数的结构和用法,就能方便地驱动 STM32 工作,以节省开发时间。并且HAL库也很好的解决了程序移植的问题,不同型号的stm32芯片它的标准库是不一样的,例如在F4上开发的程序移植到F3上是不能通用的,而使用HAL库,只要使用的是相通的外设,程序基本可以完全复制粘贴。同时采用ST公司的STM32CubeMX软件进行STM32开发,可以依靠图形化界面对单片机的进行功能配置,例如时钟频率、GPIO口、定时器、中断等,可以直接生成整个使用HAL库的工程文件,开发人员只需要调用对应的功能函数完成mian函数的编写就行
但目前使用HAL库进行开发还是存在些许弊端,因为STM32CubeMX是直接一步到位帮我们生成工程文件,所以可能会有很多“累赘”,导致使用HAL库开发的工程文件偏大,同时效率也较低。
在本实验中所使用的STM32开发方式都是采用HAL库和STM32CubeMX进行,对于STM32CubeMX的软件安装就不多做介绍,各位只要双击安装包,改个路径一路next即可。同时需要交代的是我所使用的固件包版本为STM32Cube_FW_F1_V1.8.0,各位在安装完成后在软件包管理页面中下载或者通过本地路径配置都可。
- 创建工程方式
- 选择你所开发的STM32单片机的型号
在本无线传感器网络实验平台中,NB-IOT核心模块使用的是STM32F103T8U6,因此在MCU/MPU Selector页面中直接搜索STM32F103T8U6即可。
- 双击上述图片的芯片型号,进入主设计界面
- 确认时钟源
进入工程后打开RCC选项,选择Crystal/Ceramic Resonator,即使用外部晶振作为 HSE 的时钟源。
- 配置系统时钟
硬件的外部晶振为 12MHz,我们填入 12;通道选择 HSE;PLLM 选择为/1;倍频系数 N选择为 x6; 系统时钟选择 PLLCLK;系统时钟设定为 72Mz;APB1 分频系数选择为/2 即PCLK1 位 36MHz; APB2 分频系数选择为/1 即 PCLK2 位 72MHz。
CubeMX 也提供了更简单的方法:在下图的“HCLK(MHz)”位置,实际上是可以编辑的。我们直接输入我们要的主频,这里是 72Mzh,按回车键,CubeMX 会帮我们提供一种设置主频和其它时钟的建议,选择是后会由软件自动配置好,当然只有启用外部的晶振后才能配置到 72Mhz 的时钟。
- 配置IO口
首先以一个简单的LED灯周期闪烁为例的工程,进行IO口的配置,我们需要配置两个 IO 即可,控制 LED 的引脚分别为 PB2 PB3,通过搜索框搜索可以定位 IO 口的引脚位置, 图中会闪烁显示,配置 PB2、PB3 的属性为 GPIO_Output。
- 进一步配置IO的具体属性
双击PB2,进入详细配置,选择 GPIO,配置 PB2 PB3 的默认电平,开漏输出,无上下拉,高速模式。引脚标签为 LED0 和 LED1。
- 配置工程属性
为了防止出现,烧录以后仿真器无法连接的情况,我们在 Pinout 里将 SYS 里面的 Debug设置成 Serial Wire。
- 选择 Project Manager 选项,配置工程的名称,路径,使用的 IDE 工具,堆栈大小。
注意路径中不要出现中文
- 生成代码
打开 Project Manager-> Code Generator 选项,Generated files 生成文件选项,勾选Generate peripheral initialization as a pair of ‘.c/.h’files per peripheral,勾选这个选项的话将会将每个外设单独分开成一组.c、.h 文件,使得代码结构更加的清晰。由于 CubeMX 默认勾选了复制所有的库,即工程中不使用到的代码也会复制进来,为了节省 CubeMX 生成工程的空间,我们勾选生成工程时只复制用到的库(这一步是可选操作,大家根据自己的实际选择)。点击 GENERATE CODE, 在设定的路径成功生成代码,选择打开工程。
- 编写用户代码
到这可以说新建工程的事情就已经告一段落了,接下来只需要在main函数中编写你需要的代码即可。因为使用的示例程序是LED灯周期闪烁,所以在main 函数中的编码如下: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(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); HAL_Delay(500); HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); HAL_Delay(500); } /* USER CODE END WHILE */
- 配置下载调试工具
在Keil中点击“魔术棒”按钮,设置下载工具为ST-link Debuger,程序下载完后复位并运行(一定要复位,不复位不出现象)。
- 下载验证
把编译好的程序下载到硬件并复位,可看到 NB-IOT 模块上的两颗红色 LED 灯会交替周期闪烁。(下载器与液晶扩展板的连接方式请参考上一篇文章——NB-IOT实验练习1中对于仿真器/编程器处)
- 选择你所开发的STM32单片机的型号
2. STM32基础实验
2.1 按键输入实验
- 实验目的:了解并掌握按键的使用方法和工作原理。
- 实验现象:当按下按键时,LED 会闪烁。
- 所需硬件:一个液晶扩展板、一个 NB-IOT 核心模块、一个 ST-Link 仿真器、1 根 mini USB
线、1 台 PC 机 - 硬件引脚说明:
MCU引脚号 | 按键引脚说明 | 按键编号 |
---|---|---|
PA5 | P_06 | K4(垂直向下按下) |
- 硬件电路原理图:
- 编码
-
按照STM32CubeMX创建工程小节里的流程,进行所用GPIO的配置,直接生成工程文件。
-
在编写按键驱动时,也要考虑更改硬件环境的情况。我们把按键检测引脚相关的宏定义在“bsp_key.h”文件中。以下代码根据按键的硬件连接,把检测按键输入的 GPIO 端口、GPIO 引脚号以及 GPIO 端口时钟封装起来了。
//引脚定义 /*******************************************************/ #define KEY_PIN GPIO_PIN_5 #define KEY_GPIO_PORT GPIOA #define KEY_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() /*******************************************************/
-
按键 GPIO 初始化函数
void Key_GPIO_Config(void) {GPIO_InitTypeDef GPIO_InitStructure;/*开启按键 GPIO 口的时钟*/KEY_GPIO_CLK_ENABLE();/*选择按键的引脚*/GPIO_InitStructure.Pin = KEY_PIN;/*设置引脚为输入模式*/GPIO_InitStructure.Mode = GPIO_MODE_INPUT;/*设置引脚不上拉也不下拉*/GPIO_InitStructure.Pull = GPIO_NOPULL;/*使用上面的结构体初始化按键*/HAL_GPIO_Init(KEY_GPIO_PORT, &GPIO_InitStructure); }
-
按键状态检测
uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin) {/*检测是否有按键按下 */if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == KEY_ON ){HAL_Delay(10); //延时消抖if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == KEY_ON ){/*等待按键释放 */while(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == KEY_ON);return KEY_ON;}elsereturn KEY_OFF;}elsereturn KEY_OFF; }
5.主函数
int main(void) {/* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* Configure the system clock */SystemClock_Config();/* LED 端口初始化 */LED_GPIO_Config();/*初始化按键*/Key_GPIO_Config();/* 轮询按键状态,若按键按下则反转 LED */while(1){if(Key_Scan(KEY_GPIO_PORT, KEY_PIN) == KEY_ON){/*LED1 反转*/LED1_TOGGLE;}} }
-
- 实验效果:
按下按键,松开后控制LED亮灭
2.2 外部中断实验
- 实验目的:了解并掌握 STM32 的外部中断的配置方法。
- 实验现象:通过外部中断,实现按键控制 LED 亮灭。
- 所需硬件:一个液晶扩展板、一个 NB-IOT 核心模块、一个 ST-Link 仿真器、1 根 mini USB
线、1 台 PC 机 - 硬件引脚说明:
MCU引脚号 | 按键引脚说明 | 按键编号 |
---|---|---|
PA5 | P_06 | K4(垂直向下按下) |
- 硬件电路原理图:
- 编码
-
按照STM32CubeMX创建工程小节里的流程,进行所用GPIO的配置,直接生成工程文件。
-
我们创建了两个文件:bsp_exti.c 和 bsp_exti.h 文件用来存放 EXTI 驱动程序及相关宏定义,中断服务函数放在 stm32f1xx_it.h 文件中。
-
按键和 EXTI 宏定义
//引脚定义 /*******************************************************/ #define KEY_INT_GPIO_PORT GPIOA #define KEY_INT_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE(); #define KEY_INT_GPIO_PIN GPIO_PIN_5 #define KEY_INT_EXTI_IRQ EXTI9_5_IRQn #define KEY_IRQHandler EXTI9_5_IRQHandler /*******************************************************/
-
EXTI 中断配置
void EXTI_Key_Config(void) {GPIO_InitTypeDef GPIO_InitStructure;/*开启按键 GPIO 口的时钟*/KEY_INT_GPIO_CLK_ENABLE();/* 选择按键的引脚 */GPIO_InitStructure.Pin = KEY_INT_GPIO_PIN;/* 设置引脚为输入模式 */GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING;/* 设置引脚不上拉也不下拉 */GPIO_InitStructure.Pull = GPIO_NOPULL;/* 使用上面的结构体初始化按键 */HAL_GPIO_Init(KEY_INT_GPIO_PORT, &GPIO_InitStructure);/* 配置 EXTI 中断源 到 key 引脚、配置中断优先级*/HAL_NVIC_SetPriority(KEY_INT_EXTI_IRQ, 0, 0);/* 使能中断 */HAL_NVIC_EnableIRQ(KEY_INT_EXTI_IRQ); }
-
EXTI 中断服务函数
void KEY_IRQHandler(void) {//确保是否产生了 EXTI Line 中断if(__HAL_GPIO_EXTI_GET_IT(KEY_INT_GPIO_PIN) != RESET){// LED1 取反LED1_TOGGLE;//清除中断标志位__HAL_GPIO_EXTI_CLEAR_IT(KEY_INT_GPIO_PIN);} }
-
主函数
int main(void) {/* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* Configure the system clock */SystemClock_Config();/* LED 端口初始化 */LED_GPIO_Config();/* 初始化 EXTI 中断,按下按键会触发中断,* 触发中断会进入 stm32f7xx_it.c 文件中的函数* KEY_IRQHandler 处理中断,反转 LED 灯。*/EXTI_Key_Config();/* 等待中断,由于使用中断方式,MCU 不用轮询按键 */while(1){} }
-
- 实验效果:
按下按键,松开后控制LED亮灭
2.3 SysTick 定时器实验
- 实验目的:了解并掌握 STM32 的 SysTick 定时器的配置方法。
- 实验现象:利用 SysTick 产生 1s 的时基,LED 以 1s 的频率闪烁。
- 所需硬件:一个液晶扩展板、一个 NB-IOT 核心模块、一个 ST-Link 仿真器、1 根 mini USB
线、1 台 PC 机 - 硬件引脚说明:使用单片机内部电路,无需外设介入
- 编码:
- 创建了两个文件:bsp_SysTick.c 和 bsp_ SysTick.h 文件用来存放 SysTick 驱动程序及相关宏定义,中断服务函数放在 stm32f1xx_it.c 文件中。
- SysTick 配置库函数
STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) { // 不可能的重装载值,超出范围 if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) { return (1UL); } // 设置重装载寄存器 SysTick->LOAD = (uint32_t)(ticks - 1UL); // 设置中断优先级 NVIC_SetPriority (SysTick_IRQn, (1UL << NVIC_PRIO_BITS) - 1UL); // 设置当前数值寄存器 SysTick->VAL = 0UL; // 设置系统定时器的时钟源为 AHBCLK=72M // 使能系统定时器中断 // 使能定时器 SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; return (0UL); }
- 配置 SysTick 中断优先级
// 设置系统定时器中断优先级 NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);
- SysTick 初始化函数
void SysTick_Init(void) {/* SystemFrequency / 1000 1ms 中断一次* SystemFrequency / 10000010us 中断一次* SystemFrequency / 1000000 1us 中断一次*/if (HAL_SYSTICK_Config(SystemCoreClock / 100000)){/* Capture error */while (1);} }
- SysTick 定时函数
/** * @brief us 延时程序,10us 为一个单位 * @param * @arg nTime: Delay_us( 1 ) 则实现的延时为 1 * 10us = 10us * @retval 无 */ void Delay_us(__IO u32 nTime) {TimingDelay = nTime;while(TimingDelay != 0); }
- SysTick 中断服务函数
/** * @brief This function handles System tick timer. */ void SysTick_Handler(void) {HAL_IncTick();TimingDelay_Decrement(); }
- 主函数
int main(void) {/* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* Configure the system clock */SystemClock_Config();/* LED 端口初始化 */LED_GPIO_Config();/* 配置 SysTick 为 10us 中断一次,时间到后触发定时中断,*进入 stm32f7xx_it.c 文件的 SysTick_Handler 处理,通过数中断次数计时*/SysTick_Init();while(1){LED1_TOGGLE;Delay_us(100000); // 10000 * 10us = 1000msLED2_TOGGLE;Delay_us(100000); // 10000 * 10us = 1000ms} }
- 实验效果:
复位 NB-IOT 节点后,LED 以 1s 的频率闪烁。
2.4 串口通讯实验
- 实验目的:了解串口通信的原理,并掌握如何使用 STM32 的串口发送和接收数据。
- 实验现象:在“串口调试助手”输入指令,让硬件根据这些指令执行一些任务,根据数据内容控制 LED 灯的亮灭。
- 所需硬件:一个液晶扩展板、一个 NB-IOT 核心模块、一个 ST-Link 仿真器、1 根 mini USB
线、1 台 PC 机 - 硬件说明:
为利用 USART 实现硬件与电脑通信,需要用到一个 USB 转 USART 的 IC,我们选择CP2102 芯片来实现这个功能,CP2102 是一个 USB 总线的转接芯片。具体电路设计见下图。
我们将 CP2102 的 TXD 引脚与 USART1 的 RX 引脚连接,CP2102 的 RXD 引脚与USART1 的 TX 引脚连接。CP2102 芯片集成在液晶扩展板上,其地线 (GND) 已与控制器的GND 连通。(需要说明的是,液晶扩展板上有一个串口选择开关,我们需要将它拨至右侧)
硬件原理图:
- 编码:
- 创建了两个文件:bsp_usart.c 和 bsp_usart.h 文件用来存放 USART 驱动程序及相关
宏定义。 - GPIO 和 USART 宏定义
//串口波特率 #define DEBUG_USART_BAUDRATE 115200 //引脚定义 /*******************************************************/ #define DEBUG_USART USART1 #define DEBUG_USART_CLK_ENABLE() __HAL_RCC_USART1_CLK_ENABLE(); #define DEBUG_USART_RX_GPIO_PORT GPIOA #define DEBUG_USART_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define DEBUG_USART_RX_PIN GPIO_PIN_10 #define DEBUG_USART_TX_GPIO_PORT GPIOA #define DEBUG_USART_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define DEBUG_USART_TX_PIN GPIO_PIN_9 #define DEBUG_USART_IRQHandler USART1_IRQHandler #define DEBUG_USART_IRQ USART1_IRQn /************************************************************/
- USART 初始化配置
/** * @brief DEBUG_USART GPIO 配置,工作模式配置。115200 8-N-1 * @param 无 * @retval 无 */ void DEBUG_USART_Config(void) {UartHandle.Instance = DEBUG_USART;UartHandle.Init.BaudRate = DEBUG_USART_BAUDRATE;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); }/** * @brief UART MSP 初始化 * @param huart: UART handle * @retval 无 */ void HAL_UART_MspInit(UART_HandleTypeDef *huart) { GPIO_InitTypeDef GPIO_InitStruct; DEBUG_USART_CLK_ENABLE(); DEBUG_USART_RX_GPIO_CLK_ENABLE(); DEBUG_USART_TX_GPIO_CLK_ENABLE(); /**USART1 GPIO Configuration PA9 ------> USART1_TX PA10 ------> USART1_RX */ /* 配置 Tx 引脚为复用功能 */ GPIO_InitStruct.Pin = DEBUG_USART_TX_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStruct); /* 配置 Rx 引脚为复用功能 */ GPIO_InitStruct.Pin = DEBUG_USART_RX_PIN; GPIO_InitStruct.Mode=GPIO_MODE_AF_INPUT; //模式要设置为复用输入模式! HAL_GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStruct); // HAL_NVIC_SetPriority(DEBUG_USART_IRQ ,0,1); //抢占优先级 0,子优先级 1 // HAL_NVIC_EnableIRQ(DEBUG_USART_IRQ ); //使能 USART1 中断通道 }
- 重定向 printf 和 scanf 函数
//重定向 c 库函数 printf 到串口 DEBUG_USART,重定向后可使用 printf 函数 int fputc(int ch, FILE *f) {/* 发送一个字节数据到串口 DEBUG_USART */HAL_UART_Transmit(&UartHandle, (uint8_t *)&ch, 1, 1000);return (ch); } //重定向 c 库函数 scanf 到串口 DEBUG_USART,重写向后可使用 scanf、getchar 等函数 int fgetc(FILE *f) {int ch;HAL_UART_Receive(&UartHandle, (uint8_t *)&ch, 1, 1000);return (ch); }
- 输出提示信息
/** * @brief 打印指令输入提示信息 * @param 无 * @retval 无 */ static void Show_Message(void) {printf("\r\n 这是一个通过串口通信指令控制 LED 灯实验 \r\n");printf("硬件接到指令后控制 LED 灯亮灭,指令对应如下:\r\n");printf(" 指令 ------ LED 灯序号 \r\n");printf(" 1 ------ LED1 \r\n");printf(" 2 ------ LED2 \r\n"); }
- 主函数
int main(void) { char ch; /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* Configure the system clock */ SystemClock_Config(); /* LED 端口初始化 */ LED_GPIO_Config(); /*初始化 USART 配置模式为 115200 8-N-1,中断接收*/ DEBUG_USART_Config(); /* 打印指令输入提示信息 */ Show_Message(); while(1) { /* 获取字符指令 */ ch=getchar(); printf("接收到字符:%c\r\n",ch); /* 根据字符指令控制 LED 灯亮灭 */ switch(ch) { case '1': LED1_TOGGLE; break; case '2': LED2_TOGGLE; break; default: /* 如果不是指定指令字符,打印提示信息 */ Show_Message(); break; } } }
- 创建了两个文件:bsp_usart.c 和 bsp_usart.h 文件用来存放 USART 驱动程序及相关
- 实验效果
复位 NB-IOT 节点后,保证硬件相关硬件连接正确,用 mini USB 线连接液晶扩展板的 USB接口,另外一端接至电脑。在电脑端打开串口调试助手并配置好相关参数并打开串口:- 波特率:115200
- 数据位: 8
- 停止位:1
在“字符串输入框”中输入命令 1 或 2,点击“发送”按钮,观察 NB-IOT 模块上的两颗LED 会相应的亮灭。
2.5 ADC 实验
-
实验目的:了解 ADC 的原理,并掌握如何使用 STM32 的 ADC 采集方向按键按下不同方向的采集电压值。
-
实验现象:打开“串口调试助手”,拨动方向按键不同方向传输打印输出不同的电压值信息。
-
所需硬件:一个液晶扩展板、一个 NB-IOT 核心模块、一个 ST-Link 仿真器、1 根 mini USB
线、1 台 PC 机 -
硬件说明:
方向按键的检测引脚通过连接至 STM32 芯片的 ADC 通道引脚。当我们拨动方向按键不同方向时,其检测引脚的电压也会随之改变,电压变化范围为 0~3.3V,亦是硬件默认的ADC 电压采集范围。(需要说明的是,液晶扩展板上有一个串口选择开关,我们需要将它拨至右侧)
硬件原理图:
-
编码:
- ADC 宏定义
// ADC GPIO 宏定义 #define RHEOSTAT_ADC_GPIO_PORT GPIOA //改为 PA5 #define RHEOSTAT_ADC_GPIO_PIN GPIO_PIN_5 #define RHEOSTAT_ADC_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() // ADC 序号宏定义 #define RHEOSTAT_ADC ADC1 #define RHEOSTAT_ADC_CLK_ENABLE() __HAL_RCC_ADC1_CLK_ENABLE(); #define RHEOSTAT_ADC_CHANNEL ADC_CHANNEL_5 //通道 5 // ADC 中断宏定义 #define Rheostat_ADC_IRQ ADC1_IRQn #define Rheostat_ADC_INT_FUNCTION ADC1_IRQHandler
- ADC GPIO 初始化函数
static void Rheostat_ADC_GPIO_Config(void) {GPIO_InitTypeDef GPIO_InitStructure;RHEOSTAT_ADC_CLK_ENABLE();// 使能 GPIO 时钟RHEOSTAT_ADC_GPIO_CLK_ENABLE();// 配置 IOGPIO_InitStructure.Pin = RHEOSTAT_ADC_GPIO_PIN;GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;GPIO_InitStructure.Pull = GPIO_NOPULL ; //不上拉不下拉HAL_GPIO_Init(RHEOSTAT_ADC_GPIO_PORT, &GPIO_InitStructure); }
- 配置 ADC 工作模式
static void Rheostat_ADC_Mode_Config(void) {RCC_PeriphCLKInitTypeDef ADC_CLKInit;// 开启 ADC 时钟//ADC 外设时钟ADC_CLKInit.PeriphClockSelection=RCC_PERIPHCLK_ADC;//分频因子 6 时钟为 72M/6=12MHzADC_CLKInit.AdcClockSelection=RCC_ADCPCLK2_DIV6;//设置 ADC 时钟HAL_RCCEx_PeriphCLKConfig(&ADC_CLKInit);ADC_Handle.Instance=RHEOSTAT_ADC;//右对齐ADC_Handle.Init.DataAlign=ADC_DATAALIGN_RIGHT;//非扫描模式ADC_Handle.Init.ScanConvMode=DISABLE;//连续转换ADC_Handle.Init.ContinuousConvMode=ENABLE;//1 个转换在规则序列中 也就是只转换规则序列 1ADC_Handle.Init.NbrOfConversion=1;//禁止不连续采样模式ADC_Handle.Init.DiscontinuousConvMode=DISABLE;//不连续采样通道数为 0ADC_Handle.Init.NbrOfDiscConversion=0;//软件触发ADC_Handle.Init.ExternalTrigConv=ADC_SOFTWARE_START;//初始化HAL_ADC_Init(&ADC_Handle);//------------------------------ADC_Config.Channel = RHEOSTAT_ADC_CHANNEL;ADC_Config.Rank = 1;// 采样时间间隔ADC_Config.SamplingTime = ADC_SAMPLETIME_55CYCLES_5 ;// 配置 ADC 通道转换顺序为 1,第一个转换,采样时间为 3 个时钟周期HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config);HAL_ADC_Start_IT(&ADC_Handle); }
- ADC 中断配置
// 配置中断优先级 static void Rheostat_ADC_NVIC_Config(void) { HAL_NVIC_SetPriority(Rheostat_ADC_IRQ, 0, 0); HAL_NVIC_EnableIRQ(Rheostat_ADC_IRQ); }
- ADC 中断服务函数
/** * @brief This function handles ADC interrupt request. * @param None * @retval None */ void ADC1_IRQHandler(void) {HAL_ADC_IRQHandler(&ADC_Handle); } /** * @brief 转换完成中断回调函数(非阻塞模式) * @param AdcHandle : ADC 句柄 * @retval 无 */ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle) {/* 获取结果 */ADC_ConvertedValue = HAL_ADC_GetValue(AdcHandle); }
- 主函数
int main(void) {/* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* Configure the system clock */SystemClock_Config();/* LED 端口初始化 */LED_GPIO_Config();/*初始化 USART 配置模式为 115200 8-N-1,中断接收*/DEBUG_USART_Config();Rheostat_Init();while (1){ADC_Vol =(float) ADC_ConvertedValue/4096*(float)3.3; // 读取转换的 AD 值printf("\r\n The current AD value = 0x%04X \r\n", ADC_ConvertedValue);printf("\r\n The current AD value = %f V \r\n",ADC_Vol);Delay(0x8fffff);} }
- ADC 宏定义
-
实验现象:
复位 NB-IOT 节点后,保证硬件相关硬件连接正确,用 mini USB 线连接液晶扩展板的 USB接口,另外一端接至电脑。在电脑端打开串口调试助手并配置好相关参数并打开串口:- 波特率:115200
- 数据位: 8
- 停止位:1
当我们拨动方向按键不同方向时,其检测引脚的电压也会随之改变,电压变化范围为0~3.3V。
2.6 OLED 屏显示实验
- 实验目的:了解 IIC 协议及 OLED 显示的原理,并掌握如何使用 STM32 的 IIC 总线驱动 OLED模块显示 ASCII 码字符。
- 实验现象:使用 IIC 模式驱动,驱动 OLED 模块,不停的显示 ASCII 码字符。
- 所需硬件:一个液晶扩展板、一个 NB-IOT 核心模块、一个 ST-Link 仿真器、1 根 mini USB
线、1 台 PC 机 - 硬件引脚说明:
MCU引脚号 | 按键引脚说明 | 按键编号 |
---|---|---|
PA11 | SDA | P_12 |
PA12 | SCL | P_13 |
PA8 | 使能OLED的电源EXTVDD_3V3 | P_13 |
- 硬件电路原理图:
- 编码:
这里使用的是IO口模拟IIC协议进行通信- IIC 硬件相关宏定义
#define OLED_SDA_PORT GPIOA #define OLED_SDA_PIN GPIO_PIN_11 #define OLED_SCL_PORT GPIOA #define OLED_SCL_PIN GPIO_PIN_12 #define VCC_3V3_PORT GPIOA #define VCC_3V3_PIN GPIO_PIN_8
- 初始化 IIC 的 GPIO
void OLED_GPIO_Config(void) {GPIO_InitTypeDef GPIO_InitStruct;__HAL_RCC_GPIOA_CLK_ENABLE();// Init the GPIO pinsGPIO_InitStruct.Pin = VCC_3V3_PIN;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(VCC_3V3_PORT, &GPIO_InitStruct);GpioWrite(VCC_3V3_PORT, VCC_3V3_PIN, GPIO_PIN_SET);DelayMs(100);// Init the GPIO pinsGPIO_InitStruct.Pin = OLED_SDA_PIN;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(OLED_SDA_PORT, &GPIO_InitStruct);GPIO_InitStruct.Pin = OLED_SCL_PIN;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(OLED_SCL_PORT, &GPIO_InitStruct);DelayMs(10);ugOled9616int(); }
- 初始化OLED
static void OLED_Init() {Set_Display_On_Off(0x00);// Display Off (0x00/0x01)Set_Display_Clock(0xA2);// Set Clock as 120 Frames/SecSet_Multiplex_Ratio(0x0F);// 1/16 Duty (0x0F~0x3F)Set_Display_Offset(0x00);// Shift Mapping RAM Counter (0x00~0x3F)Set_Start_Line(0x00);// Set Mapping RAM Display Start Line (0x00~0x3F)Set_Charge_Pump(0x04);// Enable Embedded DC/DC Converter (0x00/0x04)Set_Power_Save(0x05);// Set Low Power Save ModeSet_Addressing_Mode(0x02);// Set Page Addressing Mode (0x00/0x01/0x02)Set_Segment_Remap(0x01);// Set SEG/Column Mapping (0x00/0x01)Set_Common_Remap(0x08);// Set COM/Row Scan Direction (0x00/0x08)Set_Common_Config(0x00);// Set Sequential Configuration (0x00/0x10)Set_Contrast_Control(Brightness); // Set SEG Output CurrentSet_Precharge_Period(0xD2);// Set Pre-Charge as 13 Clocks & Discharge as2 ClockSet_VCOMH(0x20);// Set VCOM Deselect LevelSet_Entire_Display(0x00);// Disable Entire Display On (0x00/0x01)Set_Inverse_Display(0x00);// Disable Inverse Display On (0x00/0x01)FillRam(0x00);// Clear ScreenSet_Display_On_Off(0x01);// Display On (0x00/0x01) }
- 产生 IIC 起始信号
static void I2C_Start() {OLED_SDA_LOW();uDelay(1);OLED_SCL_HIGH();uDelay(1);OLED_SCL_LOW();uDelay(1); }
- 产生停止信号
static void I2C_Stop() {OLED_SCL_HIGH();uDelay(5);OLED_SDA_LOW();uDelay(5);OLED_SDA_HIGH();uDelay(5); }
- 发送一个字节数据
static void IIC_O( OledDataType mcmd ) {OledDataType length = 8;// Send Commandwhile(length--){if(mcmd & 0x80){OLED_SDA_HIGH();}else{OLED_SDA_LOW();}uDelay(1);OLED_SCL_HIGH();uDelay(1);OLED_SCL_LOW();uDelay(1);mcmd = mcmd << 1;} }
- 主函数
int main(void) {/* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* Configure the system clock */SystemClock_Config();/* LED 端口初始化 */LED_GPIO_Config();OLED_GPIO_Config(); //OLED 相关引脚定义放在 main.h 文件中HAL_Delay(10);LcdPutScDispRtoL((void*)"ABCDEFG1234567890", 1, 600); //液晶显示/* Infinite loop */while(1){} }
- IIC 硬件相关宏定义
- 实验效果:
复位 NB-IOT 节点后,OLED 模块从右至左滚动显示字符"ABCDEFG1234567890"