STM32F767 GPIO与UART的基本用法

 本文介绍STM32F767 GPIO与UART的基本用法。

开发环境

硬件环境

  • 电脑:Windows 10 Home x64
  • Apollo STM32F767开发板(ST-LINK V2仿真器)

软件环境

  • Keil Version 5.24.1 (Pack Installer:Keil.STM32F7xx_DFP.2.9.0.pack)
  • STM32CubeMX Version 4.25.0(Packages Manager:STM32CubeF7)

GPIO基本用法

 使用GPIO的基本流程为:使能GPIO对应端口时钟、配置IO参数、控制IO输出或者读取IO状态。

使能GPIO端口时钟

 任何需要使用IO端口的外设使用前都必须使能对应地IO端口时钟。对于不考虑功耗的系统,可以直接将所有的IO端口时钟全部打开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void RCC_CLK_Enable(void)
{
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOI_CLK_ENABLE();
}

设置IO参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1, GPIO_PIN_RESET);
/*Configure GPIO pins : PB0 PB1 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pin : PH3 */
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
}

 IO的初始化参数通过GPIO_InitStruct结构体设置,对于输入IO,需要的设置如下:

  • PIN脚号;
  • IO模式:可以选择push pull或者open drain;
  • IO上下拉设置:可以选择上拉、下拉或者不进行上拉下拉;
  • IO速度:可以设置IO的翻转速度,具体可以参考器件的手册;
  • 调用HAL_GPIO_Init()函数完成IO初始化。

 对于输入IO,与输出IO不同的设置如下:

  • IO模式:选择input模式;
  • IO速度:无需设置。

输出IO的控制

 IO的输出通过HAL_GPIO_WritePin()/HAL_GPIO_TogglePin()实现,后文举例说明。

输入IO的读取

 IO的输入数据通过HAL_GPIO_ReadPin()实现,后文举例说明。

GPIO使用示例

 示例代码如下所示。

1
2
3
4
5
6
7
8
9
10
if (HAL_GPIO_ReadPin(GPIOH, GPIO_PIN_3) == GPIO_PIN_RESET)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
while (HAL_GPIO_ReadPin(GPIOH, GPIO_PIN_3) == GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}

 该代码的功能是检测到port H PIN3为低电平后,翻转port B PIN1,同时点亮port B PIN1。

OLED的使用

 本文使用的OLED显示屏使用4个GPIO即可完成驱动。只需要移植底层IO代码,上层应用可以直接使用,底层代码实现如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void LCD_Hardware_Init(){
/*Configure GPIO pins : PC6 PC7 PC8 PC9 */
__HAL_RCC_GPIOE_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
}
#define oledSCL_1 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_2, GPIO_PIN_SET)
#define oledSCL_0 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_2, GPIO_PIN_RESET)
#define oledSDA_1 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_SET)
#define oledSDA_0 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET)
#define oledRST_1 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_SET)
#define oledRST_0 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_RESET)
#define oledDC_1 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_5, GPIO_PIN_SET)
#define oledDC_0 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_5, GPIO_PIN_RESET)

 OLED显示屏可以进行字符串、数字以及简单图标的展示,便于系统调试。

UART基本用法

 使用UART的基本流程为:初始化huart1结构体、调用HAL_UART_Init()函数完成初始化。

huart1结构体参数初始化

huart1结构体主要包含了串口参数,例如波特率、数据长度、停止位、奇偶校验位等。之后调用HAL_UART_Init()函数完成初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
UART_HandleTypeDef huart1;
/* USART1 init function */
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
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;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}

HAL_UART_Init()的功能

HAL_UART_Init()函数的内容如下所示。除参数合理性检测之外,将调用HAL_UART_MspInit()完成硬件IO的初始化、调用UART_SetConfig()完成UART参数的初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
* @brief Initializes the UART mode according to the specified
* parameters in the UART_InitTypeDef and creates the associated handle .
* @param huart uart handle
* @retval HAL status
*/
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)
{
/* Check the UART handle allocation */
if(huart == NULL)
{
return HAL_ERROR;
}
if(huart->Init.HwFlowCtl != UART_HWCONTROL_NONE)
{
/* Check the parameters */
assert_param(IS_UART_HWFLOW_INSTANCE(huart->Instance));
}
else
{
/* Check the parameters */
assert_param(IS_UART_INSTANCE(huart->Instance));
}
if(huart->gState == HAL_UART_STATE_RESET)
{
/* Allocate lock resource and initialize it */
huart->Lock = HAL_UNLOCKED;
/* Init the low level hardware : GPIO, CLOCK */
HAL_UART_MspInit(huart);
}
huart->gState = HAL_UART_STATE_BUSY;
/* Disable the Peripheral */
__HAL_UART_DISABLE(huart);
/* Set the UART Communication parameters */
if (UART_SetConfig(huart) == HAL_ERROR)
{
return HAL_ERROR;
}
if (huart->AdvancedInit.AdvFeatureInit != UART_ADVFEATURE_NO_INIT)
{
UART_AdvFeatureConfig(huart);
}
/* In asynchronous mode, the following bits must be kept cleared:
- LINEN and CLKEN bits in the USART_CR2 register,
- SCEN, HDSEL and IREN bits in the USART_CR3 register.*/
CLEAR_BIT(huart->Instance->CR2, (USART_CR2_LINEN | USART_CR2_CLKEN));
CLEAR_BIT(huart->Instance->CR3, (USART_CR3_SCEN | USART_CR3_HDSEL | USART_CR3_IREN));
/* Enable the Peripheral */
__HAL_UART_ENABLE(huart);
/* TEACK and/or REACK to check before moving huart->gState and huart->RxState to Ready */
return (UART_CheckIdleState(huart));
}
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(uartHandle->Instance==USART1)
{
/* USER CODE BEGIN USART1_MspInit 0 */
/* USER CODE END USART1_MspInit 0 */
/* USART1 clock enable */
__HAL_RCC_USART1_CLK_ENABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN USART1_MspInit 1 */
/* USER CODE END USART1_MspInit 1 */
}
}

UART的发送与接收

 UART的发送与接收通过HAL_UART_Transmit()/HAL_UART_Receive()实现,示例如下。

1
2
3
4
5
6
if (__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE)==SET)
{
HAL_UART_Receive(&huart1, usart1_rx, 1, 1000);
HAL_UART_Transmit(&huart1,(uint8 *)"\nYou send: ",11,1000);
HAL_UART_Transmit(&huart1,usart1_rx, 1,1000);
}

 当检测到串口RXNE标志位为1时,表示此时串口接收到信息,即调用HAL_UART_Receive()函数进行读取。读取后通过HAL_UART_Transmit()发送提示信息与接收到的信息。事实上可以直接调用HAL_UART_Receive()函数轮询串口,当返回值为HAL_TIMEOUT时即表示没有接收到信息。

UART发送函数的简化

 读取HAL_UART_Transmit()的实现流程,其相对复杂。对于通常用到的场景来说,可以进行简化,即采用寄存器方式操作。具体实现如下。

1
2
3
4
5
6
// Transmit a character from the SCI
void scia_xmit(int ch)
{
while ((USART1->ISR & USART_ISR_TC_Msk) == FALSE);
USART1->TDR = (uint8)(ch & 0xFF);
}

 该函数实现了单个字符的发送,注意对字符与0xff的&操作表示发送的数据位为8位

 基于该函数还可以封装诸如发送字符串、数字、不同进制数的相关函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include "uart.h"
// Test 1,SCIA DLB, 8-bit word, baud rate 0x000F, default, 1 STOP bit, no parity
// Transmit a character from the SCI
void scia_xmit(int ch)
{
while ((USART1->ISR & USART_ISR_TC_Msk) == FALSE);
USART1->TDR = (uint8)(ch & 0xFF);
}
void scia_msg(char * msg)
{
int i;
i = 0;
while(msg[i] != '\0')
{
scia_xmit(msg[i]);
i++;
}
}
void uart_printf(char * msg)
{
int i;
i = 0;
while(msg[i] != '\0')
{
scia_xmit(msg[i]);
i++;
}
}
void uart_print_data(int32 data)
{
int i = 0;
char temp[7];
if (data < 0)
{
data = - data;
temp[0] = '-';
}
else temp[0] = ' ';
temp[1] = data / 10000 + '0';
temp[2] = (data % 10000) /1000 + '0';
temp[3] = (data % 1000 ) /100 + '0';
temp[4] = (data % 100 ) /10 + '0';
temp[5] = (data % 10 ) /1 + '0';
temp[6] = '\0';
while(temp[i] != '\0')
{
scia_xmit(temp[i]);
i++;
}
}
void uart_print_info(char * msg, int32 data)
{
uart_printf(msg);
uart_printf(": ");
uart_print_data(data);
uart_printf("\n");
}
void printNumber(unsigned long n, uint8 base) {
char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte.
char *str = &buf[sizeof(buf) - 1];
*str = '\0';
// prevent crash if called with base == 1
if (base < 2) base = 10;
do {
unsigned long m = n;
n /= base;
char c = m - base * n;
*--str = c < 10 ? c + '0' : c + 'A' - 10;
} while(n);
uart_printf(str);
}
// Receive a character from the SCI
uint8 scia_recv(void)
{
uint8 ch;
while ((USART1->ISR & UART_FLAG_RXNE) == FALSE);
ch = USART1->RDR & 0xFF;
return ch;
}

UART接收函数的简化

 同理,HAL_UART_Receive()函数的实现也相对复杂,这里进行一个简单简化。

1
2
3
4
5
6
7
8
// Receive a character from the SCI
uint8 scia_recv(void)
{
uint8 ch;
while ((USART1->ISR & UART_FLAG_RXNE) == FALSE);
ch = USART1->RDR & 0xFF;
return ch;
}

 该函数实现了阻塞等待方式读取一个接收字符。

简化的UART发送接收

 示例代码如下,其实现的功能与基于HAL的发送接收函数一致,但是得到了明显简化。

1
2
3
4
5
6
if ((USART1->ISR & USART_ISR_RXNE_Msk) != FALSE)
{
usart1_rx[0] = USART1->RDR & 0xFF;
uart_printf("\nYou send: ");
scia_xmit(usart1_rx[0]);
}

文章目录
  1. 1. 开发环境
    1. 1.1. 硬件环境
    2. 1.2. 软件环境
  2. 2. GPIO基本用法
    1. 2.1. 使能GPIO端口时钟
    2. 2.2. 设置IO参数
    3. 2.3. 输出IO的控制
    4. 2.4. 输入IO的读取
    5. 2.5. GPIO使用示例
    6. 2.6. OLED的使用
  3. 3. UART基本用法
    1. 3.1. huart1结构体参数初始化
    2. 3.2. HAL_UART_Init()的功能
    3. 3.3. UART的发送与接收
    4. 3.4. UART发送函数的简化
    5. 3.5. UART接收函数的简化
    6. 3.6. 简化的UART发送接收
|