使用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显示效果
继电器工作指示: