STM32基础--位带操作

2024-03-10 01:04
文章标签 基础 操作 stm32 位带

本文主要是介绍STM32基础--位带操作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

位带简介

位操作就是可以单独的对一个比特位读和写,这个在 51 单片机中非常常见。51 单片机中通过关键字 sbit 来实现位定义,STM32 没有这样的关键字,而是通过访问位带别名区来实现。
在 STM32 中,有两个地方实现了位带,一个是 SRAM 区的最低 1MB 空间,另一个是外设区最低 1MB 空间。这两个 1MB 的空间除了可以像正常的 RAM 一样操作外,他们还有自己的位带别名区,位带别名区把这 1MB 的空间的每一个位膨胀成一个 32 位的字,当访问位带别名区的这些字时,就可以达到访问位带区某个比特位的目的。
在这里插入图片描述

外设位带区

外 设 外 带 区 的 地 址 为:0X40000000~0X40100000, 大 小 为1MB, 这1MB的 大 小 在103系 列 大/中/小 容 量 型 号 的 单 片 机 中 包 含 了 片 上 外 设 的 全 部 寄 存 器, 这 些 寄 存 器的 地 址 为:0X40000000~0X40029FFF。 外 设 位 带 区 经 过 膨 胀 后 的 位 带 别 名 区 地 址 为:0X42000000~0X43FFFFFF,这个地址仍然在 CM3 片上外设的地址空间中。在 103 系列大/中小容量型号的单片机里面,0X40030000~0X4FFFFFFF 属于保留地址,膨胀后的 32MB 位带别名区刚好就落到这个地址范围内,不会跟片上外设的其他寄存器地址重合。

STM32 的全部寄存器都可以通过访问位带别名区的方式来达到访问原始寄存器比特位的效果,这比 51 单片机强大很多。因为 51 单片机里面并不是所有的寄存器都是可以比特位操作,有些寄存器还是得字节操作,比如 SBUF。

虽然说全部寄存器都可以实现比特操作,但我们在实际项目中并不会这么做,甚至不会这么做。有时候为了特定的项目需要,比如需要频繁的操作很多 IO 口,这个时候我们可以考虑把 IO 相关的寄存器实现比特操作。

SRAM 位带区

SRAM 的位带区的地址为:0X2000 0000~X2010 0000,大小为 1MB,经过膨胀后的位带别名区地址为:0X2200 0000~0X23FF FFFF,大小为 32MB。操作 SRAM 的比特位这个用得很少。

位带区和位带别名区地址转换

位带区的一个比特位经过膨胀之后,虽然变大到 4 个字节,但是还是 LSB 才有效。有人会问这不是浪费空间吗,要知道 STM32 的系统总线是 32 位的,按照 4 个字节访问的时候是最快的,所以膨胀成 4 个字节来访问是最高效的。

我们可以通过指针的形式访问位带别名区地址从而达到操作位带区比特位的效果。那这两个地址直接如何转换,我们简单介绍一下。

外设位带别名区地址

对于片上外设位带区的某个比特,记它所在字节的地址为 A, 位序号为 n(0<=n<=31)(n 的范围根据具体寄存器能控制的位决定),则该比特在别名区的地址为:

AliasAddr = 0x42000000+ (A-0x40000000)*8*4 +n*4

0X42000000 是外设位带别名区的起始地址,0x40000000 是外设位带区的起始地址, (A-0x40000000)表示该比特前面有多少个字节,一个字节有 8 位,所以 *8,一个位膨胀后是 4 个字节,所以 *4,n 表示该比特在 A 地址的序号,因为一个位经过膨胀后是四个字节,所以也 *4。

SRAM 位带别名区地址

对于 SRAM 位带区的某个比特,记它所在字节的地址为 A, 位序号为 n(0<=n<=31)(n 的范围根据具体寄存器能控制的位决定),则该比特在别名区的地址为:

AliasAddr = 0x22000000+ (A-0x20000000)*8*4 +n*4

公式原理则与上面一样。

统一公式

为了方便操作,我们可以把这两个公式合并成一个公式,把“位带地址 + 位序号”转换成别名区地址统一成一个宏。

// 把“位带地址 + 位序号”转换成别名地址的宏
#define 	BITBAND(addr, bitnum)			 ((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2))

addr & 0xF0000000 是为了区别 SRAM 还是外设,实际效果就是取出 4 或者 2,如果是外设,则取出的是 4,+0X02000000 之后就等于 0X42000000,0X42000000 是外设别名区的起始地址。如果是 SRAM,则取出的是 2,+0X02000000 之后就等于 0X22000000,0X22000000 是 SRAM 别名区的起始地址。

addr & 0x00FFFFFF 屏蔽了高三位,相当于减去 0X20000000 或者 0X40000000,但是为什么是屏蔽高三位?因为外设的最高地址是:0X20100000,跟起始地址 0X20000000 相减的时候,总是低5 位才有效,所以干脆就把高三位屏蔽掉来达到减去起始地址的效果,具体屏蔽掉多少位跟最高地址有关。SRAM 同理分析即可。«5 相当于 84,«2 相当于 *4,这两个我们在上面分析过。最后我们就可以通过指针的形式操作这些位带别名区地址,最终实现位带区的比特位操作。

// 把一个地址转换成一个指针
#define 		MEM_ADDR(addr)		*((volatile unsigned long*)(addr))
// 把位带别名区地址转换成指针
#define 		BIT_ADDR(addr, bitnum)		MEM_ADDR(BITBAND(addr, bitnum))

GPIO 位带操作

外设的位带区,覆盖了全部的片上外设的寄存器,我们可以通过宏为每个寄存器的位都定义一个位带别名地址,从而实现位操作。但这个在实际项目中不是很现实,也很少人会这么做,我们在这里仅仅演示下 GPIO 中 ODR 和 IDR 这两个寄存器的位操作。
从手册中我们可以知道 ODR 和 IDR 这两个寄存器对应 GPIO 基址的偏移是 12 和 8,我们先实现这两个寄存器的地址映射,其中 GPIOx_BASE 在库函数里面有定义。

别忘了的事

添加bsp_led_bitbang.c文件和bsp_led_bitbang.h文件
别忘了添加文件的时候,在魔术棒的地方加路径。(加上bsp_led_bitbang.h存放的路径)

GPIO 寄存器映射(bsp_led_bitbang.h中)

// 这里只定义了 GPIO ODR和IDR这两个寄存器的位带别名区地址,其他寄存器的没有定义//SRAM 位带区:    0X2000 0000~0X2010 0000
//SRAM 位带别名区:0X2200 0000~0X23FF FFFF//外设 位带区:    0X4000 0000~0X4010 0000
//外设 位带别名区:0X4200 0000~0X43FF FFFF// 把“位带地址+位序号”转换成别名地址的宏
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2)) 
/**addr & 0xF0000000,取地址的高4位,看看是2还是4,用于区分SRAM和外设地址,*如果是2,+0x02000000则=0X2200 0000,即是SRAM,如果是4,+0x02000000则=0X4200 0000,即是外设**addr & 0x000FFFFFF,屏蔽掉高两位,相当于-0X2000 0000或者-0X4000 0000,结果表示偏移位带区多少个字节*<<5  等于*8*4,因为位带区一个地址表示一个字节,一个字节有8个bit,一个bit可以膨胀成一个字,即4个字节*<<2 等于*4,因为一个位可以膨胀成一个字,即4个字节**分解成两条公式应该就是这样:*SRAM位带别名地址*AliasAddr= 0x22000000+((A-0x20000000)*8+n)*4 =0x22000000+ (A-0x20000000)*8*4 +n*4*外设位带别名地址*AliasAddr= 0x22000000+((A-0x20000000)*8+n)*4 =0x22000000+ (A-0x20000000)*8*4 +n*4*/// 把一个地址转换成一个指针
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) // 把位带别名区地址转换成指针
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))   // GPIO ODR 和 IDR 寄存器地址映射 
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C   
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C   
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C   
#define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C   
#define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C   
#define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C      
#define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C      #define GPIOA_IDR_Addr    (GPIOA_BASE+8)  //0x40010808   
#define GPIOB_IDR_Addr    (GPIOB_BASE+8)  //0x40010C08   
#define GPIOC_IDR_Addr    (GPIOC_BASE+8)  //0x40011008   
#define GPIOD_IDR_Addr    (GPIOD_BASE+8)  //0x40011408   
#define GPIOE_IDR_Addr    (GPIOE_BASE+8)  //0x40011808   
#define GPIOF_IDR_Addr    (GPIOF_BASE+8)  //0x40011A08   
#define GPIOG_IDR_Addr    (GPIOG_BASE+8)  //0x40011E08

现在我们就可以用位操作的方法来控制 GPIO 的输入和输出了,其中宏参数 n 表示具体是哪一个IO 口,n(0,1,2⋯15)。这里面包含了端口 A~G ,并不是每个单片机型号都有这么多端口,使用这部分代码时,要查看你的单片机型号,如果是 64pin 的则最多只能使用到 C 端口。

GPIO 位操作(bsp_led_bitbang.h中)

// 单独操作 GPIO的某一个IO口,n(0,1,2...15),n表示具体是哪一个IO口
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出   
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入   #define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出   
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入   #define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出   
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入   #define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出   
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入   #define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出   
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入  #define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出   
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入  #define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出   
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

就是分别使用别名操作每个I/O口的ODR寄存器和IDR寄存器实现输入和输出。

GPIO测试(bsp_led_bitbang.c中)

有关 LED GPIO 初始化和软件延时等函数我们直接用(以前写过的bsp_led.c和bsp_key.c文件里面的函数),修改的是控制 GPIO 输出的部分改成了位操作。该实验我们让 按键控制IO 口输出高低电平来控制LED 的亮灭,负逻辑点亮。具体使用哪一个 IO 和点亮方式由硬件平台决定。

void Led_Bitbang_GPIO_Test(void)
{//LED灯初始化LED_GPIO_Config();//按键初始化Key_GPIO_Config();//红灯亮PBout(5) = 0;/* 轮询按键状态,若按键按下则反转 LED(还没学到DMA或者中断呢) */while(1){if(Key_Scan_Bitbang('A',0) == KEY_ON){/*LED1 开启 */PBout(5) = 0;}if(Key_Scan_Bitbang('C',13) == KEY_ON){/*LED1 关闭 */PBout(5) = 1;}	}	}

代码

bsp_led_bitbang.c

/*** ****************************************************************************** @file        bsp_led_bitbang.c* @brief      	使用位带操作点亮或者熄灭LED灯* @author       (六千里)* @date        2024-03-09* @copyright   无* ******************************************************************************/#include "bsp_led_bitbang.h"/*** @brief检测是否有按键按下* @paramGPIOX: 具体的端口, GPIOX 可以是(A...G)* @paramGPIO_PIN: 具体的端口位, 可以是 x(x 可以是 0...15)* @retval	按键的状态*	@arg KEY_ON: 按键按下*	@arg KEY_OFF: 按键没按下
**/
static uint8_t Key_Scan_Bitbang(uint8_t GPIOX,uint8_t GPIO_Pin)
{switch (GPIOX) {case 'A':	{/* 检测是否有按键按下 */if((PAin(GPIO_Pin)) == KEY_ON){//因为有硬件消抖,故不写软件消抖/* 等待按键释放 */while(PAin(GPIO_Pin) == KEY_ON);												return KEY_ON;}else{return KEY_OFF;}												}case 'B':{/* 检测是否有按键按下 */if((PBin(GPIO_Pin)) == KEY_ON){//因为有硬件消抖,故不写软件消抖/* 等待按键释放 */while(PBin(GPIO_Pin) == KEY_ON);												return KEY_ON;}else{return KEY_OFF;}												}		case 'C':{/* 检测是否有按键按下 */if((PCin(GPIO_Pin)) == KEY_ON){//因为有硬件消抖,故不写软件消抖/* 等待按键释放 */while(PCin(GPIO_Pin) == KEY_ON);												return KEY_ON;}else{return KEY_OFF;}												}		case 'D':{/* 检测是否有按键按下 */if((PDin(GPIO_Pin)) == KEY_ON){//因为有硬件消抖,故不写软件消抖/* 等待按键释放 */while(PDin(GPIO_Pin) == KEY_ON);												return KEY_ON;}else{return KEY_OFF;}												}		case 'E':{/* 检测是否有按键按下 */if((PEin(GPIO_Pin)) == KEY_ON){//因为有硬件消抖,故不写软件消抖/* 等待按键释放 */while(PEin(GPIO_Pin) == KEY_ON);												return KEY_ON;}else{return KEY_OFF;}												}	case 'F':{/* 检测是否有按键按下 */if((PFin(GPIO_Pin)) == KEY_ON){//因为有硬件消抖,故不写软件消抖/* 等待按键释放 */while(PFin(GPIO_Pin) == KEY_ON);												return KEY_ON;}else{return KEY_OFF;}												}	case 'G':{/* 检测是否有按键按下 */if((PGin(GPIO_Pin)) == KEY_ON){//因为有硬件消抖,故不写软件消抖/* 等待按键释放 */while(PGin(GPIO_Pin) == KEY_ON);												return KEY_ON;}else{return KEY_OFF;}												}														default: return KEY_OFF;}}void Led_Bitbang_GPIO_Test(void)
{//LED灯初始化LED_GPIO_Config();//按键初始化Key_GPIO_Config();//红灯亮PBout(5) = 0;/* 轮询按键状态,若按键按下则反转 LED(还没学到DMA或者中断呢) */while(1){if(Key_Scan_Bitbang('A',0) == KEY_ON){/*LED1 开启 */PBout(5) = 0;}if(Key_Scan_Bitbang('C',13) == KEY_ON){/*LED1 关闭 */PBout(5) = 1;}	}	}

bsp_led_bitbang.h

#ifndef __BSP_LED_BITBANG_H
#define __BSP_LED_BITBANG_H/*** ****************************************************************************** 包含的头文件* ******************************************************************************/
#include "bsp_led.h"
#include "bsp_key.h"/*** ****************************************************************************** 宏定义* ******************************************************************************/// 这里只定义了 GPIO ODR和IDR这两个寄存器的位带别名区地址,其他寄存器的没有定义//SRAM 位带区:    0X2000 0000~0X2010 0000
//SRAM 位带别名区:0X2200 0000~0X23FF FFFF//外设 位带区:    0X4000 0000~0X4010 0000
//外设 位带别名区:0X4200 0000~0X43FF FFFF// 把“位带地址+位序号”转换成别名地址的宏
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2)) 
/**addr & 0xF0000000,取地址的高4位,看看是2还是4,用于区分SRAM和外设地址,*如果是2,+0x02000000则=0X2200 0000,即是SRAM,如果是4,+0x02000000则=0X4200 0000,即是外设**addr & 0x000FFFFFF,屏蔽掉高两位,相当于-0X2000 0000或者-0X4000 0000,结果表示偏移位带区多少个字节*<<5  等于*8*4,因为位带区一个地址表示一个字节,一个字节有8个bit,一个bit可以膨胀成一个字,即4个字节*<<2 等于*4,因为一个位可以膨胀成一个字,即4个字节**分解成两条公式应该就是这样:*SRAM位带别名地址*AliasAddr= 0x22000000+((A-0x20000000)*8+n)*4 =0x22000000+ (A-0x20000000)*8*4 +n*4*外设位带别名地址*AliasAddr= 0x22000000+((A-0x20000000)*8+n)*4 =0x22000000+ (A-0x20000000)*8*4 +n*4*/// 把一个地址转换成一个指针
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) // 把位带别名区地址转换成指针
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))   // GPIO ODR 和 IDR 寄存器地址映射 
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C   
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C   
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C   
#define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C   
#define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C   
#define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C      
#define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C      #define GPIOA_IDR_Addr    (GPIOA_BASE+8)  //0x40010808   
#define GPIOB_IDR_Addr    (GPIOB_BASE+8)  //0x40010C08   
#define GPIOC_IDR_Addr    (GPIOC_BASE+8)  //0x40011008   
#define GPIOD_IDR_Addr    (GPIOD_BASE+8)  //0x40011408   
#define GPIOE_IDR_Addr    (GPIOE_BASE+8)  //0x40011808   
#define GPIOF_IDR_Addr    (GPIOF_BASE+8)  //0x40011A08   
#define GPIOG_IDR_Addr    (GPIOG_BASE+8)  //0x40011E08 // 单独操作 GPIO的某一个IO口,n(0,1,2...15),n表示具体是哪一个IO口
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出   
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入   #define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出   
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入   #define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出   
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入   #define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出   
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入   #define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出   
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入  #define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出   
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入  #define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出   
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入  /*** ****************************************************************************** .c文件中包含的函数* ******************************************************************************/static uint8_t Key_Scan_Bitbang(uint8_t GPIOX,uint8_t GPIO_Pin);
void Led_Bitbang_GPIO_Test(void);
#endif /*__BSP_LED_BITBANG_H*/

stm32f10x_conf.h

/********************************************************************************* @file    Project/STM32F10x_StdPeriph_Template/stm32f10x_conf.h * @author  MCD Application Team* @version V3.5.0* @date    08-April-2011* @brief   Library configuration file.******************************************************************************* @attention** THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.** <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>*******************************************************************************//* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F10x_CONF_H
#define __STM32F10x_CONF_H/* Includes ------------------------------------------------------------------*/
/* Uncomment/Comment the line below to enable/disable peripheral header file inclusion */
#include "stm32f10x_adc.h"
#include "stm32f10x_bkp.h"
#include "stm32f10x_can.h"
#include "stm32f10x_cec.h"
#include "stm32f10x_crc.h"
#include "stm32f10x_dac.h"
#include "stm32f10x_dbgmcu.h"
#include "stm32f10x_dma.h"
#include "stm32f10x_exti.h"
#include "stm32f10x_flash.h"
#include "stm32f10x_fsmc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_i2c.h"
#include "stm32f10x_iwdg.h"
#include "stm32f10x_pwr.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_rtc.h"
#include "stm32f10x_sdio.h"
#include "stm32f10x_spi.h"
#include "stm32f10x_tim.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_wwdg.h"
#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */
/***自己书写文件的头文件**/
#include "bsp_led.h"
#include "bsp_key.h"
#include "bsp_led_bitbang.h"
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Uncomment the line below to expanse the "assert_param" macro in the Standard Peripheral Library drivers code */
/* #define USE_FULL_ASSERT    1 *//* Exported macro ------------------------------------------------------------*/
#ifdef  USE_FULL_ASSERT/*** @brief  The assert_param macro is used for function's parameters check.* @param  expr: If expr is false, it calls assert_failed function which reports *         the name of the source file and the source line number of the call *         that failed. If expr is true, it returns no value.* @retval None*/#define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */void assert_failed(uint8_t* file, uint32_t line);
#else#define assert_param(expr) ((void)0)
#endif /* USE_FULL_ASSERT */#endif /* __STM32F10x_CONF_H *//******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

main.c

/*** ****************************************************************************** @file        main.c* @brief       主函数* @author       (六千里)* @date        2024-03-09* @copyright   无* ******************************************************************************/
#include "stm32f10x.h"  int main(void)	
{// 来到这里的时候,系统的时钟已经被配置成72M。Led_Bitbang_GPIO_Test();}

这篇关于STM32基础--位带操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/792515

相关文章

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

C 语言基础之数组

文章目录 什么是数组数组变量的声明多维数组 什么是数组 数组,顾名思义,就是一组数。 假如班上有 30 个同学,让你编程统计每个人的分数,求最高分、最低分、平均分等。如果不知道数组,你只能这样写代码: int ZhangSan_score = 95;int LiSi_score = 90;......int LiuDong_score = 100;int Zhou

STM32(十一):ADC数模转换器实验

AD单通道: 1.RCC开启GPIO和ADC时钟。配置ADCCLK分频器。 2.配置GPIO,把GPIO配置成模拟输入的模式。 3.配置多路开关,把左面通道接入到右面规则组列表里。 4.配置ADC转换器, 包括AD转换器和AD数据寄存器。单次转换,连续转换;扫描、非扫描;有几个通道,触发源是什么,数据对齐是左对齐还是右对齐。 5.ADC_CMD 开启ADC。 void RCC_AD

STM32内部闪存FLASH(内部ROM)、IAP

1 FLASH简介  1 利用程序存储器的剩余空间来保存掉电不丢失的用户数据 2 通过在程序中编程(IAP)实现程序的自我更新 (OTA) 3在线编程(ICP把整个程序都更新掉) 1 系统的Bootloader写死了,只能用串口下载到指定的位置,启动方式也不方便需要配置BOOT引脚触发启动  4 IAP(自己写的Bootloader,实现程序升级) 1 比如蓝牙转串口,

FreeRTOS-基本介绍和移植STM32

FreeRTOS-基本介绍和STM32移植 一、裸机开发和操作系统开发介绍二、任务调度和任务状态介绍2.1 任务调度2.1.1 抢占式调度2.1.2 时间片调度 2.2 任务状态 三、FreeRTOS源码和移植STM323.1 FreeRTOS源码3.2 FreeRTOS移植STM323.2.1 代码移植3.2.2 时钟中断配置 一、裸机开发和操作系统开发介绍 裸机:前后台系

动手学深度学习【数据操作+数据预处理】

import osos.makedirs(os.path.join('.', 'data'), exist_ok=True)data_file = os.path.join('.', 'data', 'house_tiny.csv')with open(data_file, 'w') as f:f.write('NumRooms,Alley,Price\n') # 列名f.write('NA