学习STM32编程——具备交互功能的人机界面应用开发

(3) 2024-09-22 11:12

Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说
学习STM32编程——具备交互功能的人机界面应用开发,希望能够帮助你!!!。

关键代码:

bsp_spi.c

/** * @brief SPI2 外设初始化 * @param None * @retval None */ void SPI2_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_AHB1PeriphClockCmd(SPI2_MOSI_CLK|SPI2_SCK_CLK,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE); GPIO_PinAFConfig(SPI2_MOSI_PORT,SPI2_MOSI_PINSOURCE,SPI2_MOSI_AF); GPIO_PinAFConfig(SPI2_SCK_PORT,SPI2_SCK_PINSOURCE,SPI2_SCK_AF); /* MOSI-PB15 SCK-PB10 */ GPIO_InitStructure.GPIO_Pin = SPI2_MOSI_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 速度 50MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; // 推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(SPI2_MOSI_PORT,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = SPI2_SCK_PIN; GPIO_Init(SPI2_SCK_PORT,&GPIO_InitStructure); RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2,ENABLE); // 复位 SPI2 RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2,DISABLE); // 停止复位 SPI2 /* SPI 工作参数配置 */ SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;// 双线全双工 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // 主机模式 SPI_InitStructure.SPI_DataSize =SPI_DataSize_8b; // 数据帧长度 8bit SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; // 空闲时 SCK 高电平 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;// 在 SCK 的偶数边沿采集数据 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 片选线由软件管理 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;//2 分频 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // 高位在前 SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 多项式 SPI_Init(SPI2,&SPI_InitStructure); SPI_Cmd(SPI2,ENABLE); } /** * @brief SPI2 发送与接收数据 * @param TxData: 要发送的数据 * @retval 通过 SPI2 接收到的数据 */ uint8_t SPI2_ReadWriteByte(uint8_t TxData) { uint8_t retry = 0; /* 检查发送缓存空标志位 TXE */ while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) { retry++; if(retry>200) return 0; } SPI_I2S_SendData(SPI2, TxData); // 通过外设 SPI2 发送数据 retry=0; /* 检查接收缓存非空标志位 RXNE */ while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) { retry++; if(retry>200) return 0; } return SPI_I2S_ReceiveData(SPI2); // 返回 SPI2 接收的数据 }

oled.c

uint8_t OLED_GRAM[128][8]; /** * @brief 向显示驱动芯片写入数据 * @param data :要写入的数据 * @retval None */ void OLED_Write_Data(u8 data) { OLED_CS_LOW; OLED_DC_HIGH; //DC 引脚高电平,写入数据 SPI2_ReadWriteByte(data); // 调用硬件 SPI 写入 1 个字节 } /** * @brief 向显示驱动芯片写入命令 * @param Cmd :要写入的命令 * @retval None */ void OLED_Write_Cmd(u8 cmd) { OLED_CS_LOW; OLED_DC_LOW; //DC 引脚低电平,写入命令 SPI2_ReadWriteByte(cmd); // 调用硬件 SPI 写入 1 个字节 } /** * @brief 将要显示的数据写入 SSD1306 的 GDDRAM * @param None * @retval None */ void OLED_Refresh_Gram(void) { uint8_t page,column; for(page=0; page<8; page++) { OLED_Write_Cmd(0xB0+page); // 设置页地址 (0~7) OLED_Write_Cmd(0x00); // 设置列低 4 位地址 OLED_Write_Cmd(0x10); // 设置列高 4 位地址 for(column=0; column<128; column++) OLED_Write_Data(OLED_GRAM[column][page]); } } /** * @brief 初始化 OLED 控制相关 GPIO 引脚 * @param None * @retval None */ void OLED_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(OLED_CS_CLK|OLED_DC_CLK|OLED_RST_CLK,ENABLE); /* CS(NSS)-PA3 | DC(Data or Command)-PA12 | RST-PA11 */ GPIO_InitStructure.GPIO_Pin = OLED_CS_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(OLED_CS_PORT,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = OLED_DC_PIN; GPIO_Init(OLED_DC_PORT,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = OLED_RST_PIN; GPIO_Init(OLED_RST_PORT,&GPIO_InitStructure); } /** * @brief OLED 显示模块初始化 * @param None * @retval None */ void OLED_Init(void) { OLED_GPIO_Config(); // 初始化 OLED 控制相关引脚 OLED_RST_HIGH; // 硬件复位 delay_ms(100); OLED_RST_LOW; delay_ms(100); OLED_RST_HIGH; OLED_Write_Cmd(0xAE); // 关闭显示 OLED_Write_Cmd(0x20); // 设置存储器寻址模式 OLED_Write_Cmd(0x02); // 页寻址模式 OLED_Write_Cmd(0xA8); // 设置行扫多路系数 OLED_Write_Cmd(0x3F); OLED_Write_Cmd(0xD3); // 设置行扫偏移 (0x00~0x3F) OLED_Write_Cmd(0x00); //not offset OLED_Write_Cmd(0x40|0x00); // 设置显示起始行 (0x00~0x3F) OLED_Write_Cmd(0xA1); // 设置段重映射 0xa0 左右反置 0xa1 正常 OLED_Write_Cmd(0xC8); // 设置行扫方向 0xc0 上下反置 0xc8 正常 OLED_Write_Cmd(0xD9); // 设置充放电周期 OLED_Write_Cmd(0xF1); // 充电周期 15Clocks & 放电周期 1Clock OLED_Write_Cmd(0xDA); // 设置行扫方式 OLED_Write_Cmd(0x12); OLED_Write_Cmd(0x81); // 设置背光对比度 OLED_Write_Cmd(0x7F); // 默认是 0x7F(0x00~0xFF) OLED_Write_Cmd(0xA4); // 设置非全屏显示 (0xa4/0xa5) OLED_Write_Cmd(0xA6); // 设置正常显示 (0xa6/a7) OLED_Write_Cmd(0xD5); // 设置时钟分频 OLED_Write_Cmd(0x80); OLED_Write_Cmd(0x8D); // 打开内置升压泵 OLED_Write_Cmd(0x14); //0x14 打开, 0x10 关闭 OLED_Write_Cmd(0xDB); // 反向截止电压设置 OLED_Write_Cmd(0x40); OLED_Write_Cmd(0xAF); // 打开 OLED 面板显示 OLED_Display_Clear(); // 清屏 } /** * @brief OLED 清屏函数 * @param None * @retval None */ void OLED_Display_Clear(void) { u8 i,n; for(i=0; i<8; i++) for(n=0; n<128; n++) OLED_GRAM[n][i]=0x00; OLED_Refresh_Gram(); //OLED 显存写入全 0 } /** * @brief OLED 定位函数 * @param x :列地址 (0~127) , y :行地址 (0~63) * @retval None */ void OLED_Set_Pos(u8 x,u8 y) { OLED_Write_Cmd(0xB0 + y/8); // 设置页指针 OLED_Write_Cmd(x&0x0F); // 列低 4 位地址 OLED_Write_Cmd(((x&0xF0)>>4)|0x10); // 列高 4 位地址 } /** * @brief 在指定位置画点函数 * @param x: 列地址 (0~127) , y: 行地址 (0~63) * @param mode:1 填充, 0 清空 * @retval None */ void OLED_DrawPoint(u8 x,u8 y,u8 mode) { u8 page,bx,temp=0; OLED_Set_Pos(x,y); if(x>127||y>63) return; // 超出范围 page=y/8; bx=y%8; temp=1<<bx; if(mode) OLED_GRAM[x][page]|=temp; else OLED_GRAM[x][page]&=~temp; /* 写入显示缓存 */ OLED_Write_Data(OLED_GRAM[x][page]); } /** * @brief 在指定位置显示字符 * @param x :列地址 (0~127) , y :行地址 (0~63) * @param chr :要显示的字符 | size :字体大小 12/16/24 * @param mode : 1 填充, 0 清空 * @retval None */ void OLED_Display_Onechar(u8 x,u8 y,u8 chr,u8 size,u8 mode) { u8 temp,t,t1; u8 y0=y; u8 csize=(size/8+((size%8)?1:0))*(size/2); chr=chr-' '; // 得到偏移后的值 for(t=0; t<csize; t++) { if(size==12) temp = oled_asc2_1206[chr][t]; // 调用 1206 字体 else if(size==16) temp = oled_asc2_1608[chr][t]; // 调用 1608 字体 else if(size==24) temp = oled_asc2_2412[chr][t]; // 调用 2412 字体 else return; // 字库中不存在相关字体 for(t1=0; t1<8; t1++) { if(temp & 0x80) OLED_DrawPoint(x, y, mode); else OLED_DrawPoint(x, y, !mode); temp <<= 1; y++; if((y-y0) == size) { y = y0; x++; break; } } } } /** * @brief 在指定位置显示字符串 * @param x :列地址 (0~127) , y :行地址 (0~63) * @param *p :要显示的字符串起始地址 * @param size :字体大小 12/16/24 * @retval None */ void OLED_Display_String(u8 x,u8 y,char *p, u8 size) { while((*p <= '~') && (*p >= ' '))// 判断是否为非法字符 { if(x > (128-(size/2))) { x = 0; y += size; } if(y > (64-size)) { y = x = 0; OLED_Display_Clear(); } OLED_Display_Onechar(x,y,*p,size,1); x += size/2; p++; } }

bsp_rtc.c

/** * @brief 显示日期和时间 * @param None * @retval None */ void RTC_Show_DateTime(void) { char WeekDay[4]; // 用于存放星期的缩写,如 Mon RTC_TimeTypeDef RTC_TimeStructure; RTC_DateTypeDef RTC_DateStructure; /* 获取日历 */ RTC_GetTime(RTC_Format_BIN, &RTC_TimeStructure); RTC_GetDate(RTC_Format_BIN, &RTC_DateStructure); /* 将 RTC 获取到的“星期”参数转化为相应的英文缩写 */ switch(RTC_DateStructure.RTC_WeekDay) { case 1: // 星期一 WeekDay[0]='M';WeekDay[1]='O';WeekDay[2]='N'; break; case 2: // 星期二 WeekDay[0]='T';WeekDay[1]='U';WeekDay[2]='E'; break; case 3: // 星期三 WeekDay[0]='W';WeekDay[1]='E';WeekDay[2]='D'; break; case 4: // 星期四 WeekDay[0]='T';WeekDay[1]='H';WeekDay[2]='U'; break; case 5: // 星期五 WeekDay[0]='F';WeekDay[1]='R';WeekDay[2]='I'; break; case 6: // 星期六 WeekDay[0]='S';WeekDay[1]='A';WeekDay[2]='T'; break; case 7: // 星期日 WeekDay[0]='S';WeekDay[1]='U';WeekDay[2]='N'; break; } /* 串口显示日期 */ printf("The Date : Y:20%0.2d - M:%0.2d - D:%0.2d - W:%0.2d\r\n", RTC_DateStructure.RTC_Year, RTC_DateStructure.RTC_Month, RTC_DateStructure.RTC_Date, RTC_DateStructure.RTC_WeekDay); /* 使用 OLED 显示日期 */ sprintf(DateShow,"20%0.2d-%0.2d-%0.2d %s", RTC_DateStructure.RTC_Year, RTC_DateStructure.RTC_Month, RTC_DateStructure.RTC_Date, WeekDay); /* 串口显示时间 */ printf("The Time : %0.2d:%0.2d:%0.2d \r\n\r\n", RTC_TimeStructure.RTC_Hours, RTC_TimeStructure.RTC_Minutes, RTC_TimeStructure.RTC_Seconds); /* 使用 OLED 显示时间 */ sprintf(TimeShow,"%0.2d:%0.2d:%0.2d", RTC_TimeStructure.RTC_Hours, RTC_TimeStructure.RTC_Minutes, RTC_TimeStructure.RTC_Seconds); OLED_Show_DateTime(); // 使用 OLED 显示日期时间 } /** * @brief OLED 显示日期和时间 * @param None * @retval None */ void OLED_Show_DateTime(void) { OLED_Display_String(32,0,"Calendar",16); OLED_Display_String(4,32,DateShow,16); OLED_Display_String(32,48,TimeShow,16); }

main.c

int main(void) { delay_init(168); // 延时函数初始化 LED_Init(); //LED 端口初始化 Key_Init(); // 按键端口初始化 EXTIx_Init(); // 外部中断初始化 USART1_Init(); //USART1 初始化 IIC_Init(); //IIC 总线初始化 BH1750_Init(); //BH1750 初始化 DHT11_Init(); //DHT11 初始化 SPI2_Init(); //SPI2 外设初始化 OLED_Init(); //OLED 显示模块初始化 RTC_CLK_Config(1); //RTC 配置,时钟源选择 LSE while(DHT11_Init()) //DHT11 初始化 { printf("DHT11 Init Error!\r\n"); delay_ms(500); } printf("DHT11 Init Success!\r\n"); /* 若备份区域读取的值不对 */ if (RTC_ReadBackupRegister(RTC_BKP_DR0) != 0x5F5F) { RTC_Set_DateTime(); // 设置时间和日期 } else { printf("\r\n 不需要重新配置 RTC …… \r\n"); /* 使能 PWR 时钟 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); /* PWR_CR:DBF 置 1 ,使能 RTC 、 RTC 备份寄存器和备份 SRAM 的访问 */ PWR_BackupAccessCmd(ENABLE); /* 等待 RTC APB 寄存器同步 */ RTC_WaitForSynchro(); } while(1) { if(keyValue == KEY_D_PRESS) // 如果“下键”按下 { if(refresh_flag == 1) { refresh_flag = 2; OLED_Display_Clear(); // 刷一次屏 } RTC_Show_DateTime(); // 显示时间和日期 } else // 如果“下键”没有按下 { bh1750Light = BH1750_Measure(); // 读取 BH1750 的光照强度值 DHT11_Read_Data(&temperature,&humidity);// 读取 DHT11 的温湿度值 /* 组合需要显示的信息 */ sprintf(lightString,"Light:%0.5d",bh1750Light); printf("%s",lightString); sprintf(tempString,"Temp:%d",temperature); printf("%s",tempString); sprintf(humiString,"Humi:%d",humidity); printf("%s",humiString); if(refresh_flag == 2) { refresh_flag = 1; OLED_Display_Clear(); // 刷一次屏 } Show_TempHumiLight(); // 显示环境参数 } LED1 = ~LED1; delay_ms(500); } } /** * @brief OLED 显示环境参数 ( 温度 / 湿度 / 光照强度 ) * @param None * @retval None */ void Show_TempHumiLight(void) { OLED_Display_String(20,0,"Environment",16); OLED_Display_String(20,16,tempString,16); OLED_Display_String(20,32,humiString,16); OLED_Display_String(20,48,lightString,16); }

今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

上一篇

已是最后文章

下一篇

已是最新文章

发表回复