使用STM32F103R在Keil环境中实现LED闪烁、OLED显示文字信息和继电器控制抽水泵通断的功能。
一、原理图:
板载LED引脚PA5,OLED使用IIC通信,使用的引脚是PB6,PB7,继电器控制使用的是PB12.

板载按键使用的是PC13.

二、连接示意图:

三、软件功能
MX配置:
按键,LED,IIC配置。

IIC配置:

定时器配置:

在STM32CubeMX中配置定时器(TIM),用于生成1秒的定时中断,配置GPIO引脚作为LED输出。
四、代码
GPIO初始化
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(RALEY_GPIO_Port, RALEY_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : KEY_Pin */
GPIO_InitStruct.Pin = KEY_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(KEY_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : LD2_Pin */
GPIO_InitStruct.Pin = LD2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : RALEY_Pin */
GPIO_InitStruct.Pin = RALEY_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(RALEY_GPIO_Port, &GPIO_InitStruct);
}中文文字库:
unsigned char F16x16[] =
{
0x10,0x60,0x02,0x8C,0x00,0xFE,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0xFE,0x00,0x00,
0x04,0x04,0x7E,0x01,0x00,0xFE,0x88,0x48,0x48,0x00,0x7E,0x90,0x88,0x88,0xE4,0x00,/*"?",0*/
0x80,0x40,0x20,0x90,0x88,0x86,0x80,0x80,0x80,0x83,0x8C,0x10,0x20,0x40,0x80,0x00,
0x00,0x80,0x40,0x20,0x18,0x07,0x00,0x40,0x80,0x40,0x3F,0x00,0x00,0x00,0x00,0x00,/*"?",1*/
0x00,0x00,0xFE,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0xE2,0x02,0x00,0x00,0x00,
0x00,0x00,0x7F,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x43,0x40,0x40,0x00,0x00,/*"?",2*/
0x00,0x00,0x7C,0x54,0x55,0x56,0x54,0x7C,0x54,0x56,0x55,0x54,0x7C,0x00,0x00,0x00,
0x01,0x01,0x01,0xFD,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0xFD,0x01,0x01,0x01,0x00,/*"?",3*/
0x10,0x10,0x10,0x10,0x10,0xFF,0x10,0x10,0xF0,0x10,0x11,0x16,0xD0,0x10,0x10,0x00,
0x80,0x40,0x20,0x18,0x06,0x41,0x20,0x10,0x3F,0x44,0x42,0x41,0x40,0x40,0x78,0x00,/*"?",4*/
0x04,0x04,0x04,0x04,0xFF,0x24,0x24,0x24,0x24,0x24,0xFF,0x04,0x04,0x04,0x04,0x00,
0x44,0x44,0x24,0x24,0x15,0x0D,0x05,0xFF,0x05,0x0D,0x15,0x24,0x24,0x44,0x44,0x00,/*"?",5*/
0x04,0x04,0x04,0x04,0xFF,0x24,0x24,0x24,0x24,0x24,0xFF,0x04,0x04,0x04,0x04,0x00,
0x44,0x44,0x24,0x24,0x15,0x0D,0x05,0xFF,0x05,0x0D,0x15,0x24,0x24,0x44,0x44,0x00,/*"?",6*/
};
unsigned char F16x161[] =
{
// 电(0) 子(1) 产(2) 品(3) 世(4) 界(5) 第(6) 4(7)
// 期(8)
//(9) L(10) e(11) t(12) '(13) s(14) (15)
// d(16) o(17) '(18) D(19) i(20) g(21) i(22) K(23)
// e(24) y(25) (26) 陪(27) 你(28) 渡(29) 过(30) 春(31)
// 夏(32) 秋(33) 冬(34) -(35) D(36) I(37) Y(38) 液(39)
// 体(40) 流(41) 量(42) 检(43) 测(44) 仪(45)
0xFF,0xFF,0x07,0x77,0x77,0x77,0x77,0x00,0x77,0x77,0x77,0x77,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xE0,0xF7,0xF7,0xF7,0xF7,0x80,0x77,0x77,0x77,0x77,0x60,0x7F,0x0F,0xFF,/*"电",0*/
0x7F,0x7D,0x7D,0x7D,0x7D,0x7D,0x7D,0x1D,0x5D,0x6D,0x75,0x79,0x7D,0x7F,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF,0x7F,0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,/*"子",1*/
0xFF,0xFB,0x7B,0x7B,0x6B,0x1B,0x7A,0x79,0x7B,0x3B中文函数:
// Parameters : x,y -- ?????(x:0~127, y:0~7);
// Description : ??ASCII_8x16.h????,16*16??
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)
{
unsigned char wm=0;
unsigned int adder=32*N;
OLED_SetPos(x , y);
for(wm = 0;wm < 16;wm++)
{
WriteDat(F16x161[adder]);
adder += 1;
}
OLED_SetPos(x,y + 1);
for(wm = 0;wm < 16;wm++)
{
WriteDat(F16x161[adder]);
adder += 1;
}
}
void OLED_ShowCN1(unsigned char x, unsigned char y, unsigned char N)
{
unsigned char wm=0;
unsigned int adder=32*N;
OLED_SetPos(x , y);
for(wm = 0;wm < 16;wm++)
{
WriteDat(F16x162[adder]);
adder += 1;
}
OLED_SetPos(x,y + 1);
for(wm = 0;wm < 16;wm++)
{
WriteDat(F16x162[adder]);
adder += 1;
}
}
void OLED_ShowCN_STR(u8 x , u8 y , u8 begin , u8 num)
{
u8 i;
for(i=0;i<num;i++){OLED_ShowCN(i*16+x,y,i+begin);}
}
void OLED_ShowCN_STR1(u8 x , u8 y , u8 begin , u8 num)
{
u8 i;
for(i=0;i<num;i++){OLED_ShowCN1(i*16+x,y,i+begin);}
}主函数功能:
HAL_TIM_Base_Start_IT(&htim2);
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
//HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
// HAL_GPIO_WritePin(RALEY_GPIO_Port, RALEY_Pin, GPIO_PIN_SET);
OLED_ShowStr(10,0,"WWW.EEPW.COM",2);
OLED_ShowCN_STR(10,3,0,6);
OLED_ShowCN_STR1(10,6,0,7);
// OLED_DrawBMP(30,0,89,8,BMP1);
//OLED_DrawGIF(30,2,78,8,12,294,BMP2);
// OLED_DrawGIF(30,2,78,8,12,294,bmp3);
if (read_key_state(KEY_GPIO_Port, KEY_Pin)) {
// 切换继电器状态
relayState = !relayState;
HAL_GPIO_WritePin(RALEY_GPIO_Port, RALEY_Pin, relayState);
// 等待一段时间以避免按键抖动影响
HAL_Delay(DEBOUNCE_DELAY);
}
}
/* USER CODE END 3 */
}在中断服务程序中,切换LED状态,实现LED闪烁,用于生成1秒的定时中断。
/* USER CODE BEGIN 1 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim== &htim2)
{
HAL_GPIO_TogglePin(LD2_GPIO_Port,LD2_Pin);
}
}五、STM32定时器的理解与配置
基本功能介绍:


计算定时器的时间:
APB2的时钟为1分频故出来的APB2时钟还是72MHZ,TIM1对系统时钟APB2(72MHZ)再进行7200分频,然后计数重载初值设置为100,则一个定时周期Tout=(100-1+1)*(7200-1+1)/72,000,000=1/10=0.1s,即100ms为一个计数周期
六、实际效果
OLED显示效果

继电器工作指示:
