4.1. Chương trình STM32F407
Viết chương trình đọc dữ liệu ADC chân PA4, PA5, PA6 hiển thị dữ liệu lên LCD
kiểu giao tiếp I2C dùng STM32F407
4.1.1. Khởi tạo và khai báo dùng STM32CubeMX
Trong mục System core chọn RCC, chế độ High speed clock (HSE) chọn
Crystal/Ceramic Resonator.
28 trang |
Chia sẻ: thanhle95 | Lượt xem: 924 | Lượt tải: 1
Bạn đang xem trước 20 trang tài liệu Giáo trình Thực hành Lập trình hệ thống nhúng STM32F407 - Bài 4: LCD I2C với STM32F407 Discovery, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 86
Bài 4. LCD I2C VỚI STM32F407 DISCOVERY
4.1. Chương trình STM32F407
Viết chương trình đọc dữ liệu ADC chân PA4, PA5, PA6 hiển thị dữ liệu lên LCD
kiểu giao tiếp I2C dùng STM32F407
4.1.1. Khởi tạo và khai báo dùng STM32CubeMX
Trong mục System core chọn RCC, chế độ High speed clock (HSE) chọn
Crystal/Ceramic Resonator.
Trong thẻ connectivity, chọn I2C1:
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 87
Trong thẻ DMA Add thêm I2C1_TX như hình bên dưới
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 88
Tronh thẻ PinOut view Kích chuột vào chân PD12, PD13, PD14, PD15 chọn
GPIO_Output
Trong thẻ System view chọn GPIO, chọn chân PD12, PD13, PD14, PD15:
GPIO output level: LOW
GPIO mode: Output Push Pull
GPIO Pull-up/Pull-down: No pull-up and no pull-down
Maximum output speed: High
Tronh thẻ PinOut view Kích chuột vào chân PA4, PA5, PA6 chọn là ADC1_IN4,
ADC1_IN5, ADC1_IN6.
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 89
Tronh thẻ PinOut view chọn ADC1.
Thẻ DMA settings chọn Add, sau đó chọn ADC1
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 90
DMA request settings: mode chọn circular
Trong ADC1 , thẻ parameter Setting:
ADCs_common_settings:
Mode: chọn Independent mode
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 91
ADC_settings:
Clock prescaler: PCLK2 divided by 2
Resolution : 12 bits (15ADC clock cycles) _ độ phân giải của ADC là 12 bit.
Data Alignment: Right Alignment
Scan conversion mode: Enalble
Continuous conversion mode: Enable
Discontinuous conversion mode: Disable
DMA continous requests: Enable
ADC_regular_Conversionmode:
Number of conversion: chọn 3
Rank 1: chọn channel 4
Rank 2: chọn channel 5
Rank 3: chọn channel 6
Trong thẻ Clock Configuration chọn như hình sau:
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 92
Thẻ Project manager:
Project name: nhập tên project
Project location: chọn nơi lưu project
Toolchain/IDE: chọn MDK-ARM
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 93
Chọn open project:
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 94
4.1.2. Chương trình mẫu:
Các lệnh sử dụng trong chương trình:
Khai báo biến toàn cục:
volatile uint16_t adc_value[3];
uint16_t giatri_cb1, giatri_cb2,giatri_cb3;
Lệnh cho phép bắt đầu chuyển đổi ADC:
HAL_ADC_Start_DMA(&hadc1,(uint32_t*) adc_value,2);cho phép bắt
đầu chuyển đổi adc1 có DMA.
Sau khi STM chuyển đổi xong sẽ thực hiện chương trình ngắt sau:
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 95
Chương trình mẫu toàn code:
#include "main.h"
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
I2C_HandleTypeDef hi2c1;
DMA_HandleTypeDef hdma_i2c1_tx;
/* USER CODE BEGIN PV */
volatile uint16_t adc_value[3];
uint16_t giatri_cb1, giatri_cb2,giatri_cb3;
/* USER CODE END PV */
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_I2C1_Init(void);
static void MX_ADC1_Init(void);
/* USER CODE BEGIN PFP */
void lcd_send_cmd4bit (int8_t cmd);
void lcd_send_cmd (char cmd);
void lcd_init (void);
void lcd_send_data(char chr);
/* USER CODE END PFP */
int main(void)
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 96
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* 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();
MX_DMA_Init();
MX_I2C1_Init();
MX_ADC1_Init();
/* USER CODE BEGIN 2 */
HAL_ADC_Start_DMA(&hadc1,(uint32_t*) adc_value,2);
lcd_init();
lcd_send_cmd(0x80);
HAL_Delay(100);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 97
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
lcd_send_cmd(0x80);
lcd_send_data('A');
HAL_Delay(300);
giatri_cb1= adc_value[0];
giatri_cb2= adc_value[1];
lcd_send_cmd(0xC0);
lcd_send_data(giatri_cb1/1000%10+0x30);
lcd_send_data(giatri_cb1/100%10+0x30);
lcd_send_data(giatri_cb1/10%10+0x30);
lcd_send_data(giatri_cb1/1%10+0x30);
lcd_send_cmd(0xC8);
lcd_send_data(giatri_cb2/1000%10+0x30);
lcd_send_data(giatri_cb2/100%10+0x30);
lcd_send_data(giatri_cb2/10%10+0x30);
lcd_send_data(giatri_cb2/1%10+0x30);
}
/* USER CODE END 3 */
}
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 98
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SC
ALE1);
/** Initializes the RCC Oscillators according to the
specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType =
RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 336;
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 99
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType =
RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource =
RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct,
FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
}
/**
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 100
* @brief ADC1 Initialization Function
* @param None
* @retval None
*/
static void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC1_Init 1 */
/* USER CODE END ADC1_Init 1 */
/** Configure the global features of the ADC (Clock,
Resolution, Data Alignment and number of conversion)
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 101
hadc1.Init.ExternalTrigConvEdge =
ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 2;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its
corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_84CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its
corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_5;
sConfig.Rank = 2;
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 102
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
/* USER CODE END ADC1_Init 2 */
}
/**
* @brief I2C1 Initialization Function
* @param None
* @retval None
*/
static void MX_I2C1_Init(void)
{
/* USER CODE BEGIN I2C1_Init 0 */
/* USER CODE END I2C1_Init 0 */
/* USER CODE BEGIN I2C1_Init 1 */
/* USER CODE END I2C1_Init 1 */
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 103
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 400000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C1_Init 2 */
/* USER CODE END I2C1_Init 2 */
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 104
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_DMA2_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Stream6_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn);
/* DMA2_Stream0_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOH_CLK_ENABLE();
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 105
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOD,
GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15,
GPIO_PIN_RESET);
/*Configure GPIO pins : PD12 PD13 PD14 PD15 */
GPIO_InitStruct.Pin =
GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
void lcd_init (void)
{
// 4 bit initialisation
HAL_Delay(100); // wait for >40ms
lcd_send_cmd4bit(0x00);
lcd_send_cmd4bit (0x30);
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 106
HAL_Delay(50); // wait for >4.1ms
lcd_send_cmd4bit (0x30);
HAL_Delay(5); // wait for >100us
lcd_send_cmd4bit (0x30);
HAL_Delay(1);
lcd_send_cmd4bit (0x20); // 4bit mode
HAL_Delay(1);
// dislay initialisation
lcd_send_cmd (0x28); // Function set --> DL=0 (4 bit
mode), N = 1 (2 line display) F = 0 (5x8 characters)
HAL_Delay(1);
lcd_send_cmd (0x08); //Display on/off control -->
D=0,C=0, B=0 ---> display off
HAL_Delay(1);
lcd_send_cmd (0x01); // clear display
HAL_Delay(1);
HAL_Delay(1);
lcd_send_cmd (0x06); //Entry mode set --> I/D = 1
(increment cursor) & S = 0 (no shift)
HAL_Delay(1);
lcd_send_cmd (0x0C); //Display on/off control --> D
= 1, C and B = 0. (Cursor and blink, last two bits)
HAL_Delay(1);
}
void lcd_send_cmd (char cmd)
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 107
{
char data_u, data_l;
uint8_t data_t[4];
data_u = cmd&0xf0; // select only upper nibble
data_l = (cmd<<4)&0xf0; // select only lower nibble
data_t[0] = data_u|0x04; //en=1, rs=0
data_t[1] = data_u; //en=0, rs=0
data_t[2] = data_l|0x04; //en=1, rs=0
data_t[3] = data_l; //en=0, rs=0
HAL_I2C_Master_Transmit (&hi2c1, 0x4E, (uint8_t *)
data_t, 4, 200);
}
void lcd_send_data(char chr){
char data_u, data_l;
uint8_t data_t[4];
data_u = chr&0xf0; // select only upper nibble
data_l = (chr<<4)&0xf0; // select only lower nibble
data_t[0] = data_u|0x04|0x01; //en=1, rs=1
data_t[1] = data_u|0x01; //en=0, rs=1
data_t[2] = data_l|0x04|0x01; //en=1, rs=1
data_t[3] = data_l|0x01; //en=0, rs=1
HAL_I2C_Master_Transmit (&hi2c1, 0x4E, (uint8_t *)
data_t, 4, 200);
}
#define LCD_EN 2
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 108
#define LCD_RW 1
#define LCD_RS 0
#define LCD_D4 4
void lcd_send_cmd4bit (int8_t cmd){ //cmd la 4bit 0xcmd_0
char data_u;
uint8_t data_t[4];
data_u = cmd&0xf0; // select only upper nibble
data_t[0] = data_u|0x04; //en=1, rs=0
data_t[1] = data_u; //en=0, rs=0
HAL_I2C_Master_Transmit (&hi2c1, 0x4E, (uint8_t *)
data_t, 2, 200);
}
/* USER CODE END 4 */
void Error_Handler(void)
{
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the
source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 109
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the
file name and line number,
tex: printf("Wrong parameters value: file %s on line
%d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT
STMicroelectronics *****END OF FILE****/
4.1.3. Biên dịch và nạp chương trình:
Biên dịch chương trình và download chương trình:
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 110
4.2. Giải thích chương trình
4.2.1. Chương trình lcd_send_cmd4bit
Chương trình này để gởi lệnh 4 bit cho LCD. Vì theo datasheet của HD44780 LCD
ta có như sau:
Có nghĩa là khi mở nguồn chờ 15ms
Sau đó gởi lệnh 0X03 sau đó chờ 4.1ms -> gởi lệnh 0x03 -> chờ 100us -> gởi lệnh
0x03 sau đó lệnh 0x02. Tất cả các lệnh này đều thẻ hiện 4 bit.
Vì vậy trong hàm khởi tạo LCD (lcd_init) ta phải thực hiện lệnh 4 bit trước.
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 111
Nếu không thực hiện điều này LCD bị sai trong lúc nhấn reset vi điều khiển
void lcd_send_cmd4bit (int8_t cmd){ //cmd la 4bit 0xcmd_0
char data_u;
uint8_t data_t[4];
data_u = cmd&0xf0; // select only upper nibble
data_t[0] = data_u|0x04; //en=1, rs=0
data_t[1] = data_u; //en=0, rs=0
HAL_I2C_Master_Transmit (&hi2c1, 0x4E, (uint8_t *)
data_t, 2, 100);
}
4.2.2. Chương trình lcd_send_cmd
Chương trình này gởi lệnh cho LCD, đầu tiên gởi lệnh 4 bit cao, sau đó là 4 bit thấp
void lcd_send_cmd (char cmd)
{
char data_u, data_l;
uint8_t data_t[4];
data_u = cmd&0xf0; // select only upper nibble
data_l = (cmd<<4)&0xf0; // select only lower nibble
data_t[0] = data_u|0x04; //en=1, rs=0
data_t[1] = data_u; //en=0, rs=0
data_t[2] = data_l|0x04; //en=1, rs=0
data_t[3] = data_l; //en=0, rs=0
HAL_I2C_Master_Transmit (&hi2c1, 0x4E, (uint8_t *)
data_t, 4, 100);
}
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 112
4.2.3. Chương trình lcd_send_data
Chương trình này để hiển thị dữ liệu lên LCD
void lcd_send_data(char chr){
char data_u, data_l;
uint8_t data_t[4];
data_u = chr&0xf0; // select only upper nibble
data_l = (chr<<4)&0xf0; // select only lower nibble
data_t[0] = data_u|0x04|0x01; //en=1, rs=1
data_t[1] = data_u|0x01; //en=0, rs=1
data_t[2] = data_l|0x04|0x01; //en=1, rs=1
data_t[3] = data_l|0x01; //en=0, rs=1
HAL_I2C_Master_Transmit (&hi2c1, 0x4E, (uint8_t *)
data_t, 4, 100);
}
4.2.4. Chương trình lcd_init
Chương trình này để khởi tạo LCD theo datasheet của HD44780 LCD trang 46. Đã
được giải thích trong mục 4.2.1
void lcd_init (void)
{
HAL_Delay(100); // wait for >40ms
lcd_send_cmd4bit(0x00);
lcd_send_cmd4bit (0x30);
HAL_Delay(50); // wait for >4.1ms
lcd_send_cmd4bit (0x30);
HAL_Delay(5); // wait for >100us
TH LẬP TRÌNH HỆ THỐNG NHÚNG STM32F407 - TDMU
Ngô Thanh Đông 113
lcd_send_cmd4bit (0x30);
HAL_Delay(1);
lcd_send_cmd4bit (0x20); // 4bit mode
HAL_Delay(1);
lcd_send_cmd (0x28); // Function set --> DL=0 (4 bit
mode), N = 1 (2 line display) F = 0 (5x8 characters)
HAL_Delay(1);
lcd_send_cmd (0x08);
//Display on/off control --> D=0,C=0, B=0 ---> display
off
HAL_Delay(1);
lcd_send_cmd (0x01); // clear display
HAL_Delay(1);
HAL_Delay(1);
lcd_send_cmd (0x06);
//Entry mode set --> I/D = 1 (increment cursor) & S = 0
(no shift)
HAL_Delay(1);
lcd_send_cmd (0x0C);
//Display on/off control --> D = 1, C and B = 0. (Cursor
and blink, last two bits)
HAL_Delay(1);
}
4.3. Bài tập thực hành
4.3.1. Câu 1:
Vẽ lưu đồ và viết chương trình hiển thị chuỗi Họ và tên lên LCD.