学习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.2drn",    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 rnrn",    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(115200);  //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!rn");        delay_ms(500);    }     printf("DHT11 Init Success!rn");    /*  若备份区域读取的值不对 */    if (RTC_ReadBackupRegister(RTC_BKP_DR0) != 0x5F5F)    {        RTC_Set_DateTime(); // 设置时间和日期    }    else    {        printf("rn  不需要重新配置 RTC …… rn");        /*  使能 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);}

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年10月22日
下一篇 2022年10月22日

相关推荐