看到单片机中有很多struck 的应用,但是呢我当初学C语言的时候又没有很认真的去学习,今天复习下,写一篇小小的交流,希望能够给大家带来帮助。
/***********方式一**********/
struct Book
{
char title[128];
char aurhor[40];
float price;
unsigned int date;
char pubilsher[40];
};
/*定义了Book这个模板*/
struct Book book1,book2,book3;//使用struct Book这个模板建立了一个book1变量
/***********方式二**********/
struct Book
{
char title[128];
char aurhor[40];
float price;
unsigned int date;
char pubilsher[40];
}book1,book2,book3;
//和方式1的结果相同
scanf(“%s”,book.title)//使用的.(点)运算符,表示book变量里的title属性
scanf(“%s”,&book.price)//数组不需要加取地址符,整型需要
/********方式一*******/
struct Book book1
{
“小甲鱼带你学习带你飞”,
“小甲鱼”,
48.8,
2017111111,
“清华大学出版社”,
};
/********方法二*******/
struct Book book1{
.price=48.8 };//可以以这种方式定义多种数值
好的,进入正题。复习了struck结构体的基本用法。也就是大家在课堂上学习的知识之后。我们在单片机的学习以及编译过程中是如何使用的呢。
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOB_CLK_ENABLE(); //开启GPIOB时钟
__HAL_RCC_GPIOE_CLK_ENABLE(); //开启GPIOE时钟
GPIO_Initure.Pin=GPIO_PIN_5; //PB5
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH; //高速
HAL_GPIO_Init(GPIOB,&GPIO_Initure);
GPIO_Initure.Pin=GPIO_PIN_5; //PE5
HAL_GPIO_Init(GPIOE,&GPIO_Initure);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET); //PB5置1,默认初始化后灯灭
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET); //PE5置1,默认初始化后灯灭
}
这是一段led灯的初始化代码
(进入方式如下图,选中LED_Init,右键,Go to Defintion of xxxxxx)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mtUqVX8i-1622116136280)(Struck%20224c53c2d9e240c2af26d85e538af920/Untitled.png)]
我们来逐行分析这些代码
void LED_Init(void)
//空函数空返回值,定义了一个函数,函数的名字是LED_Init,空形参,空返回值
/***c语言函数部分**/
第一行很简单没什么问题,下一行GPIO_InitTypeDef GPIO_Initure;遇到问题了。看不懂
首先打开百度翻译翻译下这句话
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KVa8PrbF-1622116136282)(Struck%20224c53c2d9e240c2af26d85e538af920/Untitled%201.png)]
大概就是GPIO初始化的意思。我们再次右键看下,第一个可能是个函数,将后面的这部分定义了。
typedef struct
{
uint32_t Pin; /*!< Specifies the GPIO pins to be configured.
This parameter can be any value of @ref GPIO_pins_define */
uint32_t Mode; /*!< Specifies the operating mode for the selected pins.
This parameter can be a value of @ref GPIO_mode_define */
uint32_t Pull; /*!< Specifies the Pull-up or Pull-Down activation for the selected pins.
This parameter can be a value of @ref GPIO_pull_define */
uint32_t Speed; /*!< Specifies the speed for the selected pins.
This parameter can be a value of @ref GPIO_speed_define */
} GPIO_InitTypeDef;
还是和刚刚一样右键然后Go to Defintion of xxxxxx。我们来到了这里,但是全是英文。所以又要借助我们的老朋友了,百度翻译。
/************翻译之后的**************/
typedef struct
{
uint32_t Pin; /*指定要配置的GPIO引脚,此参数可以是@ref GPIO_pins_define中的值*/
uint32_t Mode; /*指定所选管脚的工作模式。此参数可以是@ref GPIO\u mode\u define的值*/
uint32_t Pull; /*指定选定接点的上拉或下拉激活。此参数可以是@ref GPIO\u pull\u define的值*/
uint32_t Speed; /*指定选定接点的速度。此参数可以是@ref GPIO\u speed\u define的值*/
} GPIO_InitTypeDef;
我们可以看到这里的用法与我们c语言中的struck有些类似,但是又有些许的不同。结果是一样的。
现说结论,他这里建立了一个 GPIO_InitTypeDef的模板。与下列的代码相似。或者说是结果相同
struct GPIO_InitTypeDef
{
uint32_t Pin; //uint32_t是定义了一个32位的变量,右键go to也能找到对应的位置
uint32_t Mode;
uint32_t Pull;
uint32_t Speed;
};
/******************************/
/*******go to之后得到的内容*****/
//typedef unsigned char uint8_t; -->将unsigned char改名为uint8_t
//typedef unsigned short int uint16_t; -->将unsigned short int改名为uint16_t
//typedef unsigned int uint32_t; -->将unsigned int改名为uint16_t
//typedef unsigned __INT64 uint64_t; -->将unsigned __INT64改名为uint64_t
/***进行变化之后更方便我们使用时候选取适合字节数的变量名来定义****/
但是他这里用了一个typedef,相当于把这部分重命名了方便了我们以后的使用。对比下
/******使用纯粹的C语言编程*******/
struct GPIO_InitTypeDef
{
uint32_t Pin;
uint32_t Mode;
uint32_t Pull;
uint32_t Speed;
}GPIO_Initure;
//或者如下
struct GPIO_InitTypeDef
{
uint32_t Pin;
uint32_t Mode;
uint32_t Pull;
uint32_t Speed;
};
struct GPIO_InitTypeDef GPIO_Initure;
/********使用改进后的代码进行编程******/
typedef struct
{
uint32_t Pin; /*指定要配置的GPIO引脚,此参数可以是@ref GPIO_pins_define中的值*/
uint32_t Mode; /*指定所选管脚的工作模式。此参数可以是@ref GPIO\u mode\u define的值*/
uint32_t Pull; /*指定选定引脚的上拉或下拉激活。此参数可以是@ref GPIO\u pull\u define的值*/
uint32_t Speed; /*指定选定引脚的速度。此参数可以是@ref GPIO\u speed\u define的值*/
} GPIO_InitTypeDef;
//这段代码放在底层文件中,官方配置的。我们直接调用即可。
GPIO_InitTypeDef GPIO_Initure;//我们自己书写的函数时使用的
//总的来说是提高了代码的可移植性以及可读性。
总的来说就是定义了一个结构体,方式与我们正常的C语言编程不同而已。结果是一样的。我们继续往下看
void LED_Init(void)
/***c语言函数部分**/
//空函数空返回值,定义了一个函数,函数的名字是LED_Init,空形参,空返回值
{
GPIO_InitTypeDef GPIO_Initure;
//定义了一个名字为GPIO_Initure的结构体,里面有pin(引脚),mode(工作方式),pull(输出方式)
//speed(输出速度)几个属性的一个结构体
__HAL_RCC_GPIOB_CLK_ENABLE(); //开启GPIOB时钟
__HAL_RCC_GPIOE_CLK_ENABLE(); //开启GPIOE时钟
//两个函数,功能如注释所示
GPIO_Initure.Pin=GPIO_PIN_5; //PB5
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH; //高速
//使用struck的初始化方式对struck结构体进行了初始化
HAL_GPIO_Init(GPIOB,&GPIO_Initure);
//这里又出现了一个新的函数HAL_GPIO_Init,我们继续go to去看下
GPIO_Initure.Pin=GPIO_PIN_5; //PE5
HAL_GPIO_Init(GPIOE,&GPIO_Initure);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET); //PB5置1,默认初始化后灯灭
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET); //PE5置1,默认初始化后灯灭
}
HAL_GPIO_Init的go to结果(快速翻过即可)
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
{
uint32_t position = 0x00u;
uint32_t ioposition;
uint32_t iocurrent;
uint32_t temp;
uint32_t config = 0x00u;
__IO uint32_t *configregister; /* Store the address of CRL or CRH register based on pin number */
uint32_t registeroffset; /* offset used during computation of CNF and MODE bits placement inside CRL or CRH register */
/* Check the parameters */
assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_Init->Pin));
assert_param(IS_GPIO_MODE(GPIO_Init->Mode));
/* Configure the port pins */
while (((GPIO_Init->Pin) >> position) != 0x00u)
{
/* Get the IO position */
ioposition = (0x01uL << position);
/* Get the current IO position */
iocurrent = (uint32_t)(GPIO_Init->Pin) & ioposition;
if (iocurrent == ioposition)
{
/* Check the Alternate function parameters */
assert_param(IS_GPIO_AF_INSTANCE(GPIOx));
/* Based on the required mode, filling config variable with MODEy[1:0] and CNFy[3:2] corresponding bits */
switch (GPIO_Init->Mode)
{
/* If we are configuring the pin in OUTPUT push-pull mode */
case GPIO_MODE_OUTPUT_PP:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP;
break;
/* If we are configuring the pin in OUTPUT open-drain mode */
case GPIO_MODE_OUTPUT_OD:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_OD;
break;
/* If we are configuring the pin in ALTERNATE FUNCTION push-pull mode */
case GPIO_MODE_AF_PP:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_AF_OUTPUT_PP;
break;
/* If we are configuring the pin in ALTERNATE FUNCTION open-drain mode */
case GPIO_MODE_AF_OD:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_AF_OUTPUT_OD;
break;
/* If we are configuring the pin in INPUT (also applicable to EVENT and IT mode) */
case GPIO_MODE_INPUT:
case GPIO_MODE_IT_RISING:
case GPIO_MODE_IT_FALLING:
case GPIO_MODE_IT_RISING_FALLING:
case GPIO_MODE_EVT_RISING:
case GPIO_MODE_EVT_FALLING:
case GPIO_MODE_EVT_RISING_FALLING:
/* Check the GPIO pull parameter */
assert_param(IS_GPIO_PULL(GPIO_Init->Pull));
if (GPIO_Init->Pull == GPIO_NOPULL)
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_FLOATING;
}
else if (GPIO_Init->Pull == GPIO_PULLUP)
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_PU_PD;
/* Set the corresponding ODR bit */
GPIOx->BSRR = ioposition;
}
else /* GPIO_PULLDOWN */
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_PU_PD;
/* Reset the corresponding ODR bit */
GPIOx->BRR = ioposition;
}
break;
/* If we are configuring the pin in INPUT analog mode */
case GPIO_MODE_ANALOG:
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_ANALOG;
break;
/* Parameters are checked with assert_param */
default:
break;
}
/* Check if the current bit belongs to first half or last half of the pin count number
in order to address CRH or CRL register*/
configregister = (iocurrent < GPIO_PIN_8) ? &GPIOx->CRL : &GPIOx->CRH;
registeroffset = (iocurrent < GPIO_PIN_8) ? (position << 2u) : ((position - 8u) << 2u);
/* Apply the new configuration of the pin to the register */
MODIFY_REG((*configregister), ((GPIO_CRL_MODE0 | GPIO_CRL_CNF0) << registeroffset), (config << registeroffset));
/*--------------------- EXTI Mode Configuration ------------------------*/
/* Configure the External Interrupt or event for the current IO */
if ((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
{
/* Enable AFIO Clock */
__HAL_RCC_AFIO_CLK_ENABLE();
temp = AFIO->EXTICR[position >> 2u];
CLEAR_BIT(temp, (0x0Fu) << (4u * (position & 0x03u)));
SET_BIT(temp, (GPIO_GET_INDEX(GPIOx)) << (4u * (position & 0x03u)));
AFIO->EXTICR[position >> 2u] = temp;
/* Configure the interrupt mask */
if ((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
{
SET_BIT(EXTI->IMR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->IMR, iocurrent);
}
/* Configure the event mask */
if ((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
{
SET_BIT(EXTI->EMR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->EMR, iocurrent);
}
/* Enable or disable the rising trigger */
if ((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
{
SET_BIT(EXTI->RTSR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->RTSR, iocurrent);
}
/* Enable or disable the falling trigger */
if ((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
{
SET_BIT(EXTI->FTSR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->FTSR, iocurrent);
}
}
}
position++;
}
}
……………………………………
??????????
什么玩意……………………
好吧,抱歉拉下了一行
/**
* @brief Initializes the GPIOx peripheral according to the specified parameters in the GPIO_Init.
* @param GPIOx: where x can be (A..G depending on device used) to select the GPIO peripheral
* @param GPIO_Init: pointer to a GPIO_InitTypeDef structure that contains
* the configuration information for the specified GPIO peripheral.
* @retval None
*/
很显然我们进入的是官方的配置文件了,也就是官方的库。所谓的标准库以及hal库是不同的。当然我们也可以自己写库(这就是学习51单片机的同学最应该掌握的部分以及学习的部分)。但是对于学习32的同学来说我们只需要去看懂相应的函数用法即可。也就是说我们上面的长串代码不需要读懂,只需要看懂这段代码前面的官方注释即可。也提醒我们同学,在自己写库以及写代码的时候一定要加上注释加上简介,方便后续的使用。
/**
*@简介 根据GPIO_Init中指定的参数初始化GPIOx外围设备。
*@参数 GPIOx:其中x可以是(A~G,取决于使用的设备)来选择GPIO外围设备
*@参数 GPIO_Init:指向包含指定GPIO外围设备的配置信息的GPIO_InitTypeDef结构的指针。
*@返回值 无
*/
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
大致意思就是将我们定义的那个结构体放到GPIO_x中去
HAL_GPIO_Init(GPIOB,&GPIO_Initure);
//所以这行代码是说,将我们定义的结构体放到GPIOB中得到的结果就是
// GPIOB_pin_5的配置如下
// GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
// GPIO_Initure.Pull=GPIO_PULLUP; //上拉
// GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH; //高速
这段分析之后我们再回到源代码中
void LED_Init(void)
/***c语言函数部分**/
//空函数空返回值,定义了一个函数,函数的名字是LED_Init,空形参,空返回值
{
GPIO_InitTypeDef GPIO_Initure;
//定义了一个名字为GPIO_Initure的结构体,里面有pin(引脚),mode(工作方式),pull(输出方式)
//speed(输出速度)几个属性的一个结构体
__HAL_RCC_GPIOB_CLK_ENABLE(); //开启GPIOB时钟
__HAL_RCC_GPIOE_CLK_ENABLE(); //开启GPIOE时钟
//两个函数,功能如注释所示
GPIO_Initure.Pin=GPIO_PIN_5; //PB5
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH; //高速
//使用struck的初始化方式对struck结构体进行了初始化
HAL_GPIO_Init(GPIOB,&GPIO_Initure);
// GPIOB_pin_5的配置如下
// GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
// GPIO_Initure.Pull=GPIO_PULLUP; //上拉
// GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH; //高速
GPIO_Initure.Pin=GPIO_PIN_5; //PE5
//这里应该对代码没有改变,应该只是为了加个注释
HAL_GPIO_Init(GPIOE,&GPIO_Initure);
// GPIOE_pin_5的配置如下
// GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
// GPIO_Initure.Pull=GPIO_PULLUP; //上拉
// GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH; //高速
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET); //PB5置1,默认初始化后灯灭
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET); //PE5置1,默认初始化后灯灭
}
//The end
我们可以看到,单片机的编程依旧是以C语言为基础的。同时今天讲解的这个struck结构体的方式不仅教授了大家struck在单片机中的应用。也教会了大家一些在keil中的常用检索方式,以及如何应对keil中的未知代码以及函数。当然还有一个最重要的最重要的!!找一个好的翻译软件。
(推荐谷歌翻译,可以实时翻译)