stm32week8

stm32学习

五.闪存

1.读取闪存和芯片ID的代码

MyFlash的封装:

复制代码
#include "stm32f10x.h"                  // Device header

/**
  * 函    数:FLASH读取一个32位的字
  * 参    数:Address 要读取数据的字地址
  * 返 回 值:指定地址下的数据
  */
uint32_t MyFLASH_ReadWord(uint32_t Address)
{
 return *((__IO uint32_t *)(Address)); //使用指针访问指定地址下的数据并返回
}

/**
  * 函    数:FLASH读取一个16位的半字
  * 参    数:Address 要读取数据的半字地址
  * 返 回 值:指定地址下的数据
  */
uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
{
 return *((__IO uint16_t *)(Address)); //使用指针访问指定地址下的数据并返回
}

/**
  * 函    数:FLASH读取一个8位的字节
  * 参    数:Address 要读取数据的字节地址
  * 返 回 值:指定地址下的数据
  */
uint8_t MyFLASH_ReadByte(uint32_t Address)
{
 return *((__IO uint8_t *)(Address)); //使用指针访问指定地址下的数据并返回
}

/**
  * 函    数:FLASH全擦除
  * 参    数:无
  * 返 回 值:无
  * 说    明:调用此函数后,FLASH的所有页都会被擦除,包括程序文件本身,擦除后,程序将不复存在
  */
void MyFLASH_EraseAllPages(void)
{
 FLASH_Unlock();     //解锁
 FLASH_EraseAllPages();   //全擦除
 FLASH_Lock();     //加锁
}

/**
  * 函    数:FLASH页擦除
  * 参    数:PageAddress 要擦除页的页地址
  * 返 回 值:无
  */
void MyFLASH_ErasePage(uint32_t PageAddress)
{
 FLASH_Unlock();     //解锁
 FLASH_ErasePage(PageAddress); //页擦除
 FLASH_Lock();     //加锁
}

/**
  * 函    数:FLASH编程字
  * 参    数:Address 要写入数据的字地址
  * 参    数:Data 要写入的32位数据
  * 返 回 值:无
  */
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data)
{
 FLASH_Unlock();       //解锁
 FLASH_ProgramWord(Address, Data);  //编程字
 FLASH_Lock();       //加锁
}

/**
  * 函    数:FLASH编程半字
  * 参    数:Address 要写入数据的半字地址
  * 参    数:Data 要写入的16位数据
  * 返 回 值:无
  */
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{
 FLASH_Unlock();       //解锁
 FLASH_ProgramHalfWord(Address, Data); //编程半字
 FLASH_Lock();       //加锁
}

基于MyFlash的封装:

复制代码
#include "stm32f10x.h"                  // Device header
#include "MyFLASH.h"

#define STORE_START_ADDRESS  0x0800FC00  //存储的起始地址
#define STORE_COUNT    512    //存储数据的个数

uint16_t Store_Data[STORE_COUNT];    //定义SRAM数组

/**
  * 函    数:参数存储模块初始化
  * 参    数:无
  * 返 回 值:无
  */
void Store_Init(void)
{
 /*判断是不是第一次使用*/
 if (MyFLASH_ReadHalfWord(STORE_START_ADDRESS) != 0xA5A5) //读取第一个半字的标志位,if成立,则执行第一次使用的初始化
 {
  MyFLASH_ErasePage(STORE_START_ADDRESS);     //擦除指定页
  MyFLASH_ProgramHalfWord(STORE_START_ADDRESS, 0xA5A5); //在第一个半字写入自己规定的标志位,用于判断是不是第一次使用
  for (uint16_t i = 1; i < STORE_COUNT; i ++)    //循环STORE_COUNT次,除了第一个标志位
  {
   MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, 0x0000);  //除了标志位的有效数据全部清0
  }
 }
 
 /*上电时,将闪存数据加载回SRAM数组,实现SRAM数组的掉电不丢失*/
 for (uint16_t i = 0; i < STORE_COUNT; i ++)     //循环STORE_COUNT次,包括第一个标志位
 {
  Store_Data[i] = MyFLASH_ReadHalfWord(STORE_START_ADDRESS + i * 2);  //将闪存的数据加载回SRAM数组
 }
}

/**
  * 函    数:参数存储模块保存数据到闪存
  * 参    数:无
  * 返 回 值:无
  */
void Store_Save(void)
{
 MyFLASH_ErasePage(STORE_START_ADDRESS);    //擦除指定页
 for (uint16_t i = 0; i < STORE_COUNT; i ++)   //循环STORE_COUNT次,包括第一个标志位
 {
  MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, Store_Data[i]); //将SRAM数组的数据备份保存到闪存
 }
}

/**
  * 函    数:参数存储模块将所有有效数据清0
  * 参    数:无
  * 返 回 值:无
  */
void Store_Clear(void)
{
 for (uint16_t i = 1; i < STORE_COUNT; i ++)   //循环STORE_COUNT次,除了第一个标志位
 {
  Store_Data[i] = 0x0000;       //SRAM数组有效数据清0
 }
 Store_Save();          //保存数据到闪存
}

main.c的测试程序:

复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Store.h"
#include "Key.h"

uint8_t KeyNum;     //定义用于接收按键键码的变量

int main(void)
{
 /*模块初始化*/
 OLED_Init();    //OLED初始化
 Key_Init();     //按键初始化
 Store_Init();    //参数存储模块初始化,在上电的时候将闪存的数据加载回Store_Data,实现掉电不丢失
 
 /*显示静态字符串*/
 OLED_ShowString(1, 1, "Flag:");
 OLED_ShowString(2, 1, "Data:");
 
 while (1)
 {
  KeyNum = Key_GetNum();  //获取按键键码
  
  if (KeyNum == 1)   //按键1按下
  {
   Store_Data[1] ++;  //变换测试数据
   Store_Data[2] += 2;
   Store_Data[3] += 3;
   Store_Data[4] += 4;
   Store_Save();   //将Store_Data的数据备份保存到闪存,实现掉电不丢失
  }
  
  if (KeyNum == 2)   //按键2按下
  {
   Store_Clear();   //将Store_Data的数据全部清0
  }
  
  OLED_ShowHexNum(1, 6, Store_Data[0], 4); //显示Store_Data的第一位标志位
  OLED_ShowHexNum(3, 1, Store_Data[1], 4); //显示Store_Data的有效存储数据
  OLED_ShowHexNum(3, 6, Store_Data[2], 4);
  OLED_ShowHexNum(4, 1, Store_Data[3], 4);
  OLED_ShowHexNum(4, 6, Store_Data[4], 4);
 }
}

读取ID的代码:

复制代码
OLED_ShowHexNum(2, 6, *((__IO uint16_t *)(0x1FFFF7E8)), 4);  //使用指针读取指定地址下的产品唯一身份标识寄存器
OLED_ShowHexNum(2, 11, *((__IO uint16_t *)(0x1FFFF7E8 + 0x02)), 4);
OLED_ShowHexNum(3, 1, *((__IO uint32_t *)(0x1FFFF7E8 + 0x04)), 8);
OLED_ShowHexNum(4, 1, *((__IO uint32_t *)(0x1FFFF7E8 + 0x08)), 8);

六.OLED

1.入门

工程里添加文件夹和其中的文件:

  1. 确定已经文件夹添加到工程文件夹中
  2. 点击三个箱子的按钮(Manage Project Items),新建一个group,在新的group中add files,文件类型选All files,添加要添加的文件
  3. 点击魔术棒按钮,点C/C++这一栏,在Include Paths中点击三个点的按钮,点击新建,点击三个点的按钮,选择要添加的文件夹

解决中文乱码(UTF-8):在魔术棒中,C/C++这一栏,在Misc Controls中添加--no-multibyte-chars

GB2312:点击右边的扳手,Encoding选择Chinese GB2312(Simplified),如果要添加新文件,记得将文件的编码格式转换成GB2312

2.取模软件的用法

基于江协科技写的函数库

取模软件:PCtoLCD

使用步骤:

  1. 模式->字符模式
  2. 点击齿轮,选阴码、列行式、逆向、十六进制、自定义格式、选择C51模式、将行前后缀的{}删掉
  3. 模式->图形模式,点击白纸新建一个空白图像,这样就可以手绘图像
  4. 可以直接导入.bmp文件

3.OLED硬件

SSD1306是一款OLED(有机发光二极管)/PLED(高分子发光二极管)点阵显示屏的控制器,可以嵌入在屏幕中,用于执行接收数据、显示数据、扫描刷新等任务

驱动接口:128个SEG引脚和64个COM引脚,对应12864像素点显示屏
内置显示存储器(GDDRAM):128
64bit(128*Byte)SRAM

供电:VDD=1.653.3V(IC逻辑),VCC=715V(面板驱动),最小系统板内置电压放大电路和降压电路

通信接口:8位6800/8080并行接口(一般用于数据量大的地方),3/4线SPI接口,I2C接口

最左边是MCU通信接口,传到右边的GDDRAM,然后传到右边的显示控制器,然后传到右边的驱动器,中间的是段驱动器,上下两部分是公共端驱动器

通信接口选择及通信线定义:

4针脚I2C接口模块原理图:

7针脚SPI接口模块原理图:

4.OLED软件

字节传输-6800并口的时序图和指令表(用的少):

字节传输-8080并口的时序图和指令表(用的少):

字节传输-4线SPI:

图中是SPI模式0或者模式3,高位先行

3线SPI:

没有D/C#引脚,固定在每字节数据前发送一个D/C#

I2C:

I2C也没有D/C#,固定在每个数据字节前发送一个控制字节

Co表示是否为连续模式,连续模式下每个数据字节前必有一个控制字节,非连续模式下控制模式后会跟数个数据字节,一般不用连续模式

每个字节定义了一列8个像素的亮灭,每页都是8行,写命令控制写在哪页哪列,是低位先行

命令表:

通过写命令时序传输的字节,作为发送给SSD1306的一个命令

SSD1306查询命令表的定义,执行相应的操作

命令可以由一个字节或者连续的多个字节组成

命令可分为基础命令、滚屏命令、寻址命令、硬件配置命令、时间及驱动命令5大类

上两个命令共同组成一个列地址,第三个是设置页地址

初始化过程(内部提供VCC):

5.I2C连接OLED代码

初始化函数:

复制代码
void OLED_Init(void)
{
 OLED_GPIO_Init();   //先调用底层的端口初始化
 
 /*写入一系列的命令,对OLED进行初始化配置*/
 OLED_WriteCommand(0xAE); //设置显示开启/关闭,0xAE关闭,0xAF开启
 
 OLED_WriteCommand(0xD5); //设置显示时钟分频比/振荡器频率
 OLED_WriteCommand(0x80); //0x00~0xFF
 
 OLED_WriteCommand(0xA8); //设置多路复用率
 OLED_WriteCommand(0x3F); //0x0E~0x3F
 
 OLED_WriteCommand(0xD3); //设置显示偏移
 OLED_WriteCommand(0x00); //0x00~0x7F
 
 OLED_WriteCommand(0x40); //设置显示开始行,0x40~0x7F
 
 OLED_WriteCommand(0xA1); //设置左右方向,0xA1正常,0xA0左右反置
 
 OLED_WriteCommand(0xC8); //设置上下方向,0xC8正常,0xC0上下反置

 OLED_WriteCommand(0xDA); //设置COM引脚硬件配置
 OLED_WriteCommand(0x12);
 
 OLED_WriteCommand(0x81); //设置对比度
 OLED_WriteCommand(0xCF); //0x00~0xFF

 OLED_WriteCommand(0xD9); //设置预充电周期
 OLED_WriteCommand(0xF1);

 OLED_WriteCommand(0xDB); //设置VCOMH取消选择级别
 OLED_WriteCommand(0x30);

 OLED_WriteCommand(0xA4); //设置整个显示打开/关闭

 OLED_WriteCommand(0xA6); //设置正常/反色显示,0xA6正常,0xA7反色

 OLED_WriteCommand(0x8D); //设置充电泵
 OLED_WriteCommand(0x14);

 OLED_WriteCommand(0xAF); //开启显示
 
 OLED_Clear();    //清空显存数组
 OLED_Update();    //更新显示,清屏,防止初始化后未显示内容时花屏
} 

设置写入地址:

复制代码
void OLED_SetCursor(uint8_t Page, uint8_t X)
{
 /*如果使用此程序驱动1.3寸的OLED显示屏,则需要解除此注释*/
 /*因为1.3寸的OLED驱动芯片(SH1106)有132列*/
 /*屏幕的起始列接在了第2列,而不是第0列*/
 /*所以需要将X加2,才能正常显示*/
// X += 2;
 
 /*通过指令设置页地址和列地址*/
 OLED_WriteCommand(0xB0 | Page);     //设置页位置
 OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4)); //设置X位置高4位
 OLED_WriteCommand(0x00 | (X & 0x0F));   //设置X位置低4位
}

/**
  * 函    数:将OLED显存数组更新到OLED屏幕
  * 参    数:无
  * 返 回 值:无
  * 说    明:所有的显示函数,都只是对OLED显存数组进行读写
  *           随后调用OLED_Update函数或OLED_UpdateArea函数
  *           才会将显存数组的数据发送到OLED硬件,进行显示
  *           故调用显示函数后,要想真正地呈现在屏幕上,还需调用更新函数
  */
void OLED_Update(void)
{
 uint8_t j;
 /*遍历每一页*/
 for (j = 0; j < 8; j ++)
 {
  /*设置光标位置为每一页的第一列*/
  OLED_SetCursor(j, 0);
  /*连续写入128个数据,将显存数组的数据写入到OLED硬件*/
  OLED_WriteData(OLED_DisplayBuf[j], 128);
 }
}

void OLED_Clear(void)
{
 uint8_t i, j;
 for (j = 0; j < 8; j ++)    //遍历8页
 {
  for (i = 0; i < 128; i ++)   //遍历128列
  {
   OLED_DisplayBuf[j][i] = 0x00; //将显存数组数据全部清零
  }
 }
}

void OLED_ShowNum(int16_t X, int16_t Y, uint32_t Number, uint8_t Length, uint8_t FontSize)
{
 uint8_t i;
 for (i = 0; i < Length; i++)  //遍历数字的每一位       
 {
  /*调用OLED_ShowChar函数,依次显示每个数字*/
  /*Number / OLED_Pow(10, Length - i - 1) % 10 可以十进制提取数字的每一位*/
  /*+ '0' 可将数字转换为字符格式*/
  OLED_ShowChar(X + i * FontSize, Y, Number / OLED_Pow(10, Length - i - 1) % 10 + '0', FontSize);
 }
}

void OLED_ShowImage(int16_t X, int16_t Y, uint8_t Width, uint8_t Height, const uint8_t *Image)
{
 uint8_t i = 0, j = 0;
 int16_t Page, Shift;
 
 /*将图像所在区域清空*/
 OLED_ClearArea(X, Y, Width, Height);
 
 /*遍历指定图像涉及的相关页*/
 /*(Height - 1) / 8 + 1的目的是Height / 8并向上取整*/
 for (j = 0; j < (Height - 1) / 8 + 1; j ++)
 {
  /*遍历指定图像涉及的相关列*/
  for (i = 0; i < Width; i ++)
  {
   if (X + i >= 0 && X + i <= 127)  //超出屏幕的内容不显示
   {
    /*负数坐标在计算页地址和移位时需要加一个偏移*/
    Page = Y / 8;
    Shift = Y % 8;
    if (Y < 0)
    {
     Page -= 1;
     Shift += 8;
    }
    
    if (Page + j >= 0 && Page + j <= 7)  //超出屏幕的内容不显示
    {
     /*显示图像在当前页的内容*/
     OLED_DisplayBuf[Page + j][X + i] |= Image[j * Width + i] << (Shift);
    }
    
    if (Page + j + 1 >= 0 && Page + j + 1 <= 7)  //超出屏幕的内容不显示
    {     
     /*显示图像在下一页的内容*/
     OLED_DisplayBuf[Page + j + 1][X + i] |= Image[j * Width + i] >> (8 - Shift);
    }
   }
  }
 }
}
相关推荐
云山工作室14 分钟前
基于单片机的并联均流电源设计(论文+源码)
单片机·嵌入式硬件·物联网·毕业设计·毕设
-代号95272 小时前
【JavaScript】十二、定时器
开发语言·javascript·ecmascript
nuc-1272 小时前
sqli-labs学习记录8
数据库·学习·sqli-labs
士别三日&&当刮目相看2 小时前
JAVA学习*简单的代理模式
java·学习·代理模式
灵感__idea3 小时前
JavaScript高级程序设计(第5版):扎实的基本功是唯一捷径
前端·javascript·程序员
摇滚侠3 小时前
Vue3 其它API toRow和markRow
前端·javascript
難釋懷3 小时前
JavaScript基础-history 对象
开发语言·前端·javascript
拉不动的猪3 小时前
刷刷题47(react常规面试题2)
前端·javascript·面试
今夜有雨.3 小时前
HTTP---基础知识
服务器·网络·后端·网络协议·学习·tcp/ip·http