发新帖本帖赏金 51.00元(功能说明)我要提问
123下一页
返回列表
打印
[STM32F1]

我做了个STM32F103的ARDUINO接口的板子

[复制链接]
22826|48
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 qjp1988113 于 2021-7-12 21:19 编辑

#申请原创# 最近要调STM32F1XX,想做个最小系统。手头的ARDUINO接口模块比较大。于是,我做了个STM32F103的ARDUINO接口的板子。
主控用的STM32F103RX(当然也可换成PIN2PIN的国产MCU),W25QXX,24C02,CH340串口,还引出个IIC OLED12864的接口。
用的是LCEDA画的,现在感觉是越来越好用了,我现在几乎放弃XD了
废话不说,上图:原理图:
PCB图:

3D视图:

实物:

其中由于最近国外芯片价格暴涨,板上主要芯片均是国产(最近国产也不便宜了,GD32F103RC要90一片)。
在一家淘宝卖家店,求哥哥拜爷爷,人给了我一片STM32F103RCT6,要了35。这个价格在现在讲很不错了。
其中主要芯片:
STM32F103RCT6------->GD32F103RCT6
W25Q16            ------->BY26Q16
AT24C02           ------->BL24C02
设计时考虑外接的ARDUINO接口。板上已有外设尽量不占用ARDUINO接口。
其中24C02,由于想试验硬件IIC,使用了电阻来选通。具体如图:

如果焊接红框勾选的电阻,则占用PB6/PB7,此时与ARDUINO的IIC接口相连。
如果选用蓝框,则无影响,但这2脚无硬件IIC功能,只能软IIC。
板子设计时,考虑到运行RTT实时操作系统,特地选用大容量的STM32F103RCT6(FLASH:256K,RAM:64K),
而不使用小容量的STM32F103RBT6(FLASH:128K,RAM:20K)),并预留SD卡接口,挂载FATFS功能。
其中W25Q16 2MBYTE的FLASH用来存储字库及对应图片(由于空间2M不是很大,仅准备加载ASCII及宋体16)。
预留IIC接口的OLED,驱动OLED12864,可在上面运行简单的菜单系统或者轻量级的GUI。
板上有USB转串口CH340电路及USB DEVICE电路,可通过跳线帽,强制上拉D+,当然也可以通过引进控制。
设置初期,除了当普通开发板应用外。还兼任考虑ARDUINO,可刷入STM32F103RC ARDUINO底层固件,
并引出ISP下载控制按键。有兴趣的可以刷入ARDUINO固件,在STM32上面体验一把ARDUINO。
支持包请到https://github.com/rogerclarkmelbourne上下载,该Git仓库中有两个我们需要下载的项目集,
分别是“Arduino_STM32-master”和“STM32duino-bootloader-master”。具体参考本篇**:
https://blog.csdn.net/argon_ghost/article/details/88297262
由于时间有限,这里本人并未具体尝试。
这里当做普通开发板使用,并调试了部分外设程序.
这里,如果之前写过W25Q16驱动的,对于BY25Q16而言,寄存器指令几乎一致,唯一不同的时ID:

具体指令表格:




我们把本例程中用到的对应ID,换成BY25Q16对应的。
//定义B25Q16 ID 由规格书所得
#<font color="#ff0000"><b>define B25Q16         0X6814</b></font>

extern u16 W25QXX_TYPE;                                        //

#define        W25QXX_CS                 PAout(8)                  //
                                 
////////////////////////////////////////////////////////////////////////////
//BY25Q16对于寄存器一致
#define W25X_WriteEnable                0x06
#define W25X_WriteDisable                0x04
#define W25X_ReadStatusReg                0x05
#define W25X_WriteStatusReg                0x01
#define W25X_ReadData                        0x03
#define W25X_FastReadData                0x0B
#define W25X_FastReadDual                0x3B
#define W25X_PageProgram                0x02
#define W25X_BlockErase                        0xD8
#define W25X_SectorErase                0x20
#define W25X_ChipErase                        0xC7
#define W25X_PowerDown                        0xB9
#define W25X_ReleasePowerDown        0xAB
#define W25X_DeviceID                        0xAB
#define W25X_ManufactDeviceID        0x90
#define W25X_JedecDeviceID                0x9F

void W25QXX_Init(void);
u16  W25QXX_ReadID(void);                              //
u8         W25QXX_ReadSR(void);                        //
void W25QXX_Write_SR(u8 sr);                          //
void W25QXX_Write_Enable(void);                  //
void W25QXX_Write_Disable(void);                //
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//
void W25QXX_Erase_Chip(void);                      //
void W25QXX_Erase_Sector(u32 Dst_Addr);        //
void W25QXX_Wait_Busy(void);                   //
void W25QXX_PowerDown(void);                //
void W25QXX_WAKEUP(void);                                //
至于BL24C02则几乎完全一致,AT24C02的代码都无需修改:
具体地址定义及读写时序:

<blockquote>#include "24cxx.h" 
着重说明,这里的SD卡用的是SPI模式,而不是SDIO模式:

本板中BY25Q16与SD共用SPI1,通过CS端切换使用:
#include "sys.h"
#include "mmc_sd.h"                          
#include "spi.h"
#include "usart.h"       
                                  
u8  SD_Type=0;//SD卡的类型
////////////////////////////////////移植修改区///////////////////////////////////
//移植时候的接口
//data:要写入的数据
//返回值:读到的数据
u8 SD_SPI_ReadWriteByte(u8 data)
{
        return SPI2_ReadWriteByte(data);
}          
//SD卡初始化的时候,需要低速
void SD_SPI_SpeedLow(void)
{
        SPI2_SetSpeed(SPI_BaudRatePrescaler_256);//设置到低速模式       
}
//SD卡正常工作的时候,可以高速了
void SD_SPI_SpeedHigh(void)
{
        SPI2_SetSpeed(SPI_BaudRatePrescaler_2);//设置到高速模式       
}
//SPI硬件层初始化
//FLASH_CS-->PA8
//SD_CS-->PD2
void SD_SPI_Init(void)
{
    //设置硬件上与SD卡相关联的控制引脚输出
    //禁止其他外设(NRF/W25Q64)对SD卡产生影响
    //目前仅关在2个 B25Q16 及SD卡

    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(        RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD, ENABLE );//PORTB时钟使能

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;  // PA8 推挽
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_SetBits(GPIOA,GPIO_Pin_8);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;  // PD2 推挽
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOD, &GPIO_InitStructure);
    GPIO_SetBits(GPIOD,GPIO_Pin_2);

    SPI2_Init();
    SD_CS=1;
}
///////////////////////////////////////////////////////////////////////////////////
//取消选择,释放SPI总线
void SD_DisSelect(void)
{
        SD_CS=1;
        SD_SPI_ReadWriteByte(0xff);//提供额外的8个时钟
}
//选择sd卡,并且等待卡准备OK
//返回值:0,成功;1,失败;
u8 SD_Select(void)
{
        SD_CS=0;
        if(SD_WaitReady()==0)return 0;//等待成功
        SD_DisSelect();
        return 1;//等待失败
}
//等待卡准备好
//返回值:0,准备好了;其他,错误代码
u8 SD_WaitReady(void)
{
        u32 t=0;
        do
        {
                if(SD_SPI_ReadWriteByte(0XFF)==0XFF)return 0;//OK
                t++;                         
        }while(t<0XFFFFFF);//等待
        return 1;
}
//等待SD卡回应
//Response:要得到的回应值
//返回值:0,成功得到了该回应值
//    其他,得到回应值失败
u8 SD_GetResponse(u8 Response)
{
        u16 Count=0xFFFF;//等待次数                                                             
        while ((SD_SPI_ReadWriteByte(0XFF)!=Response)&&Count)Count--;//等待得到准确的回应            
        if (Count==0)return MSD_RESPONSE_FAILURE;//得到回应失败   
        else return MSD_RESPONSE_NO_ERROR;//正确回应
}
//从sd卡读取一个数据包的内容
//buf:数据缓存区
//len:要读取的数据长度.
//返回值:0,成功;其他,失败;       
u8 SD_RecvData(u8*buf,u16 len)
{                                    
        if(SD_GetResponse(0xFE))return 1;//等待SD卡发回数据起始令牌0xFE
    while(len--)//开始接收数据
    {
        *buf=SPI2_ReadWriteByte(0xFF);
        buf++;
    }
    //下面是2个伪CRC(dummy CRC)
    SD_SPI_ReadWriteByte(0xFF);
    SD_SPI_ReadWriteByte(0xFF);                                                                                                                      
    return 0;//读取成功
}
//向sd卡写入一个数据包的内容 512字节
//buf:数据缓存区
//cmd:指令
//返回值:0,成功;其他,失败;       
u8 SD_SendBlock(u8*buf,u8 cmd)
{       
        u16 t;                            
        if(SD_WaitReady())return 1;//等待准备失效
        SD_SPI_ReadWriteByte(cmd);
        if(cmd!=0XFD)//不是结束指令
        {
                for(t=0;t<512;t++)SPI2_ReadWriteByte(buf[t]);//提高速度,减少函数传参时间
            SD_SPI_ReadWriteByte(0xFF);//忽略crc
            SD_SPI_ReadWriteByte(0xFF);
                t=SD_SPI_ReadWriteByte(0xFF);//接收响应
                if((t&0x1F)!=0x05)return 2;//响应错误                                                                                                                      
        }                                                                                                                                                                       
    return 0;//写入成功
}

//向SD卡发送一个命令
//输入: u8 cmd   命令
//      u32 arg  命令参数
//      u8 crc   crc校验值          
//返回值:SD卡返回的响应                                                                                                                          
u8 SD_SendCmd(u8 cmd, u32 arg, u8 crc)
{
    u8 r1;       
        u8 Retry=0;
        SD_DisSelect();//取消上次片选
        if(SD_Select())return 0XFF;//片选失效
        //发送
    SD_SPI_ReadWriteByte(cmd | 0x40);//分别写入命令
    SD_SPI_ReadWriteByte(arg >> 24);
    SD_SPI_ReadWriteByte(arg >> 16);
    SD_SPI_ReadWriteByte(arg >> 8);
    SD_SPI_ReadWriteByte(arg);          
    SD_SPI_ReadWriteByte(crc);
        if(cmd==CMD12)SD_SPI_ReadWriteByte(0xff);//Skip a stuff byte when stop reading
    //等待响应,或超时退出
        Retry=0X1F;
        do
        {
                r1=SD_SPI_ReadWriteByte(0xFF);
        }while((r1&0X80) && Retry--);         
        //返回状态值
    return r1;
}                                                                                                                                                                              
//获取SD卡的CID信息,包括制造商信息
//输入: u8 *cid_data(存放CID的内存,至少16Byte)          
//返回值:0:NO_ERR
//                 1:错误                                                                                                                  
u8 SD_GetCID(u8 *cid_data)
{
    u8 r1;          
    //发CMD10命令,读CID
    r1=SD_SendCmd(CMD10,0,0x01);
    if(r1==0x00)
        {
                r1=SD_RecvData(cid_data,16);//接收16个字节的数据         
    }
        SD_DisSelect();//取消片选
        if(r1)return 1;
        else return 0;
}                                                                                                                                                                  
//获取SD卡的CSD信息,包括容量和速度信息
//输入:u8 *cid_data(存放CID的内存,至少16Byte)            
//返回值:0:NO_ERR
//                 1:错误                                                                                                                  
u8 SD_GetCSD(u8 *csd_data)
{
    u8 r1;         
    r1=SD_SendCmd(CMD9,0,0x01);//发CMD9命令,读CSD
    if(r1==0)
        {
            r1=SD_RecvData(csd_data, 16);//接收16个字节的数据
    }
        SD_DisSelect();//取消片选
        if(r1)return 1;
        else return 0;
}  
//获取SD卡的总扇区数(扇区数)   
//返回值:0: 取容量出错
//       其他:SD卡的容量(扇区数/512字节)
//每扇区的字节数必为512,因为如果不是512,则初始化不能通过.                                                                                                                  
u32 SD_GetSectorCount(void)
{
    u8 csd[16];
    u32 Capacity;  
    u8 n;
        u16 csize;                                              
        //取CSD信息,如果期间出错,返回0
    if(SD_GetCSD(csd)!=0) return 0;            
    //如果为SDHC卡,按照下面方式计算
    if((csd[0]&0xC0)==0x40)         //V2.00的卡
    {       
                csize = csd[9] + ((u16)csd[8] << 8) + 1;
                Capacity = (u32)csize << 10;//得到扇区数                           
    }else//V1.XX的卡
    {       
                n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
                csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;
                Capacity= (u32)csize << (n - 9);//得到扇区数   
    }
    return Capacity;
}
//初始化SD卡
u8 SD_Initialize(void)
{
    u8 r1;      // 存放SD卡的返回值
    u16 retry;  // 用来进行超时计数
    u8 buf[4];  
        u16 i;

        SD_SPI_Init();                //初始化IO
        SD_SPI_SpeedLow();        //设置到低速模式
        for(i=0;i<10;i++)SD_SPI_ReadWriteByte(0XFF);//发送最少74个脉冲
        retry=20;
        do
        {
                r1=SD_SendCmd(CMD0,0,0x95);//进入IDLE状态
        }while((r1!=0X01) && retry--);
        SD_Type=0;//默认无卡
        if(r1==0X01)
        {
                if(SD_SendCmd(CMD8,0x1AA,0x87)==1)//SD V2.0
                {
                        for(i=0;i<4;i++)buf[i]=SD_SPI_ReadWriteByte(0XFF);        //Get trailing return value of R7 resp
                        if(buf[2]==0X01&&buf[3]==0XAA)//卡是否支持2.7~3.6V
                        {
                                retry=0XFFFE;
                                do
                                {
                                        SD_SendCmd(CMD55,0,0X01);        //发送CMD55
                                        r1=SD_SendCmd(CMD41,0x40000000,0X01);//发送CMD41
                                }while(r1&&retry--);
                                if(retry&&SD_SendCmd(CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始
                                {
                                        for(i=0;i<4;i++)buf[i]=SD_SPI_ReadWriteByte(0XFF);//得到OCR值
                                        if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC;    //检查CCS
                                        else SD_Type=SD_TYPE_V2;   
                                }
                        }
                }else//SD V1.x/ MMC        V3
                {
                        SD_SendCmd(CMD55,0,0X01);                //发送CMD55
                        r1=SD_SendCmd(CMD41,0,0X01);        //发送CMD41
                        if(r1<=1)
                        {               
                                SD_Type=SD_TYPE_V1;
                                retry=0XFFFE;
                                do //等待退出IDLE模式
                                {
                                        SD_SendCmd(CMD55,0,0X01);        //发送CMD55
                                        r1=SD_SendCmd(CMD41,0,0X01);//发送CMD41
                                }while(r1&&retry--);
                        }else//MMC卡不支持CMD55+CMD41识别
                        {
                                SD_Type=SD_TYPE_MMC;//MMC V3
                                retry=0XFFFE;
                                do //等待退出IDLE模式
                                {                                                                                            
                                        r1=SD_SendCmd(CMD1,0,0X01);//发送CMD1
                                }while(r1&&retry--);  
                        }
                        if(retry==0||SD_SendCmd(CMD16,512,0X01)!=0)SD_Type=SD_TYPE_ERR;//错误的卡
                }
        }
        SD_DisSelect();//取消片选
        SD_SPI_SpeedHigh();//高速
        if(SD_Type)return 0;
        else if(r1)return r1;           
        return 0xaa;//其他错误
}
//读SD卡
//buf:数据缓存区
//sector:扇区
//cnt:扇区数
//返回值:0,ok;其他,失败.
u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt)
{
        u8 r1;
        if(SD_Type!=SD_TYPE_V2HC)sector <<= 9;//转换为字节地址
        if(cnt==1)
        {
                r1=SD_SendCmd(CMD17,sector,0X01);//读命令
                if(r1==0)//指令发送成功
                {
                        r1=SD_RecvData(buf,512);//接收512个字节          
                }
        }else
        {
                r1=SD_SendCmd(CMD18,sector,0X01);//连续读命令
                do
                {
                        r1=SD_RecvData(buf,512);//接收512个字节         
                        buf+=512;  
                }while(--cnt && r1==0);        
                SD_SendCmd(CMD12,0,0X01);        //发送停止命令
        }   
        SD_DisSelect();//取消片选
        return r1;//
}
//写SD卡
//buf:数据缓存区
//sector:起始扇区
//cnt:扇区数
//返回值:0,ok;其他,失败.
u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt)
{
        u8 r1;
        if(SD_Type!=SD_TYPE_V2HC)sector *= 512;//转换为字节地址
        if(cnt==1)
        {
                r1=SD_SendCmd(CMD24,sector,0X01);//读命令
                if(r1==0)//指令发送成功
                {
                        r1=SD_SendBlock(buf,0xFE);//写512个字节          
                }
        }else
        {
                if(SD_Type!=SD_TYPE_MMC)
                {
                        SD_SendCmd(CMD55,0,0X01);       
                        SD_SendCmd(CMD23,cnt,0X01);//发送指令       
                }
                r1=SD_SendCmd(CMD25,sector,0X01);//连续读命令
                if(r1==0)
                {
                        do
                        {
                                r1=SD_SendBlock(buf,0xFC);//接收512个字节         
                                buf+=512;  
                        }while(--cnt && r1==0);
                        r1=SD_SendBlock(0,0xFD);//接收512个字节
                }
        }   
        SD_DisSelect();//取消片选
        return r1;//
}       

//////////////////SD test////////////
//#include "malloc.h"

//读取SD卡的指定扇区的内容,并通过串口1输出
////sec:扇区物理地址编号
//void SD_Read_Sectorx(u32 sec)
//{
//        u8 *buf;
//        u16 i;
//        buf=mymalloc(512);                                //申请内存
//        if(SD_ReadDisk(buf,sec,1)==0)        //读取0扇区的内容
//        {       
//                LCD_ShowString(60,190,200,16,16,"USART1 Sending Data...");
//                printf("SECTOR 0 DATA:\r\n");
//                for(i=0;i<512;i++)printf("%x ",buf[i]);//打印sec扇区数据              
//                printf("\r\nDATA ENDED\r\n");
//                LCD_ShowString(60,190,200,16,16,"USART1 Send Data Over!");
//        }
//        myfree(buf);//释放内存       
//}

另MCU中的硬件IIC引脚均接了上拉,以防外接模块没有加。
对于OLED12864,就没什么好说的了,直接模拟IIC驱动:
#include "oled.h"
#include "stdlib.h"
#include "oledfont.h"           
#include "delay.h"

u8 OLED_GRAM[144][8];



////////////////具体IIC控制函数
//起始信号
static void I2C_Start(void)
{
        OLED_SDIN_Set();
        OLED_SCLK_Set();
        OLED_SDIN_Clr();
        OLED_SCLK_Clr();
}

//结束信号
static void I2C_Stop(void)
{
        OLED_SCLK_Set();
        OLED_SDIN_Clr();
        OLED_SDIN_Set();
}

//等待信号响应
static void I2C_WaitAck(void) //测数据信号的电平
{
        OLED_SCLK_Set();
        OLED_SCLK_Clr();
}

//写入一个字节
static void Send_Byte(u8 dat)
{
        u8 i;
        for(i=0;i<8;i++)
        {
                OLED_SCLK_Clr();//将时钟信号设置为低电平
                if(dat&0x80)//将dat的8位从最高位依次写入
                {
                        OLED_SDIN_Set();
    }
                else
                {
                        OLED_SDIN_Clr();
    }
                OLED_SCLK_Set();//将时钟信号设置为高电平
                OLED_SCLK_Clr();//将时钟信号设置为低电平
                dat<<=1;
  }
}

//发送一个字节
//向SSD1306写入一个字节。
//mode:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(u8 dat,u8 mode)
{
        I2C_Start();
        Send_Byte(0x78);
        I2C_WaitAck();
        if(mode){Send_Byte(0x40);}
  else{Send_Byte(0x00);}
        I2C_WaitAck();
        Send_Byte(dat);
        I2C_WaitAck();
        I2C_Stop();
}


//反显函数
void OLED_ColorTurn(u8 i)
{
        if(i==0)
                {
                        OLED_WR_Byte(0xA6,OLED_CMD);//正常显示
                }
        if(i==1)
                {
                        OLED_WR_Byte(0xA7,OLED_CMD);//反色显示
                }
}

//屏幕旋转180度
void OLED_DisplayTurn(u8 i)
{
        if(i==0)
                {
                        OLED_WR_Byte(0xC8,OLED_CMD);//正常显示
                        OLED_WR_Byte(0xA1,OLED_CMD);
                }
        if(i==1)
                {
                        OLED_WR_Byte(0xC0,OLED_CMD);//反转显示
                        OLED_WR_Byte(0xA0,OLED_CMD);
                }
}


//开启OLED显示
void OLED_DisPlay_On(void)
{
        OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
        OLED_WR_Byte(0x14,OLED_CMD);//开启电荷泵
        OLED_WR_Byte(0xAF,OLED_CMD);//点亮屏幕
}

//关闭OLED显示
void OLED_DisPlay_Off(void)
{
        OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
        OLED_WR_Byte(0x10,OLED_CMD);//关闭电荷泵
        OLED_WR_Byte(0xAF,OLED_CMD);//关闭屏幕
}

//更新显存到OLED       
void OLED_Refresh(void)
{
        u8 i,n;
        for(i=0;i<8;i++)
        {
           OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
           OLED_WR_Byte(0x00,OLED_CMD);   //设置低列起始地址
           OLED_WR_Byte(0x10,OLED_CMD);   //设置高列起始地址
           for(n=0;n<128;n++)
                 OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA);
  }
}
//自定义清屏函数
void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2)
{
        u8 i,n;
        for(i=y1;i<y2;i++)
        {
           for(n=x1;n<x2;n++)
                        {
                         OLED_GRAM[n][i]=0;//清除所有数据
                        }
  }
        OLED_Refresh();//更新显示
}
//清屏函数
void OLED_Clear(void)
{
        u8 i,n;
        for(i=0;i<8;i++)
        {
           for(n=0;n<128;n++)
                        {
                         OLED_GRAM[n][i]=0;//清除所有数据
                        }
  }
        OLED_Refresh();//更新显示
}

//画点
//x:0~127
//y:0~63
void OLED_DrawPoint(u8 x,u8 y)
{
        u8 i,m,n;
        i=y/8;
        m=y%8;
        n=1<<m;
        OLED_GRAM[x][i]|=n;
}

//清除一个点
//x:0~127
//y:0~63
void OLED_ClearPoint(u8 x,u8 y)
{
        u8 i,m,n;
        i=y/8;
        m=y%8;
        n=1<<m;
        OLED_GRAM[x][i]=~OLED_GRAM[x][i];
        OLED_GRAM[x][i]|=n;
        OLED_GRAM[x][i]=~OLED_GRAM[x][i];
}


//画线
//x:0~128
//y:0~64
void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2)
{
        u8 i,k,k1,k2;
        if((1)||(x2>128)||(1)||(y2>64)||(x1>x2)||(y1>y2))return;
        if(x1==x2)    //画竖线
        {
                        for(i=0;i<(y2-y1);i++)
                        {
                                OLED_DrawPoint(x1,y1+i);
                        }
  }
        else if(y1==y2)   //画横线
        {
                        for(i=0;i<(x2-x1);i++)
                        {
                                OLED_DrawPoint(x1+i,y1);
                        }
  }
        else      //画斜线
        {
                k1=y2-y1;
                k2=x2-x1;
                k=k1*10/k2;
                for(i=0;i<(x2-x1);i++)
                        {
                          OLED_DrawPoint(x1+i,y1+i*k/10);
                        }
        }
}
//x,y:圆心坐标
//r:圆的半径
void OLED_DrawCircle(u8 x,u8 y,u8 r)
{
        int a, b,num;
    a = 0;
    b = r;
    while(2 * b * b >= r * r)      
    {
        OLED_DrawPoint(x + a, y - b);
        OLED_DrawPoint(x - a, y - b);
        OLED_DrawPoint(x - a, y + b);
        OLED_DrawPoint(x + a, y + b);

        OLED_DrawPoint(x + b, y + a);
        OLED_DrawPoint(x + b, y - a);
        OLED_DrawPoint(x - b, y - a);
        OLED_DrawPoint(x - b, y + a);
        
        a++;
        num = (a * a + b * b) - r*r;//计算画的点离圆心的距离
        if(num > 0)
        {
            b--;
            a--;
        }
    }
}



//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//size:选择字体 12/16/24
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1)
{
        u8 i,m,temp,size2,chr1;
        u8 y0=y;
        size2=(size1/8+((size1%8)?1:0))*(size1/2);  //得到字体一个字符对应点阵集所占的字节数
        chr1=chr-' ';  //计算偏移后的值
        for(i=0;i<size2;i++)
        {
                if(size1==12)
        {temp=asc2_1206[chr1][i];} //调用1206字体
                else if(size1==16)
        {temp=asc2_1608[chr1][i];} //调用1608字体
                else if(size1==24)
        {temp=asc2_2412[chr1][i];} //调用2412字体
                else return;
                                for(m=0;m<8;m++)           //写入数据
                                {
                                        if(temp&0x80)OLED_DrawPoint(x,y);
                                        else OLED_ClearPoint(x,y);
                                        temp<<=1;
                                        y++;
                                        if((y-y0)==size1)
                                        {
                                                y=y0;
                                                x++;
                                                break;
          }
                                }
  }
}


//显示字符串
//x,y:起点坐标  
//size1:字体大小
//*chr:字符串起始地址
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1)
{
        while((*chr>=' ')&&(*chr<='~'))//判断是不是非法字符!
        {
                OLED_ShowChar(x,y,*chr,size1);
                x+=size1/2;
                if(x>128-size1)  //换行
                {
                        x=0;
                        y+=2;
    }
                chr++;
  }
}

//m^n
u32 OLED_Pow(u8 m,u8 n)
{
        u32 result=1;
        while(n--)
        {
          result*=m;
        }
        return result;
}

////显示2个数字
////x,y :起点坐标         
////len :数字的位数
////size:字体大小
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1)
{
        u8 t,temp;
        for(t=0;t<len;t++)
        {
                temp=(num/OLED_Pow(10,len-t-1))%10;
                        if(temp==0)
                        {
                                OLED_ShowChar(x+(size1/2)*t,y,'0',size1);
      }
                        else
                        {
                          OLED_ShowChar(x+(size1/2)*t,y,temp+'0',size1);
                        }
  }
}

//显示汉字
//x,y:起点坐标
//num:汉字对应的序号
void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1)
{
        u8 i,m,n=0,temp,chr1;
        u8 x0=x,y0=y;
        u8 size3=size1/8;
        while(size3--)
        {
                chr1=num*size1/8+n;
                n++;
                        for(i=0;i<size1;i++)
                        {
                                if(size1==16)
                                                {temp=Hzk1[chr1][i];}//调用16*16字体
                                else if(size1==24)
                                                {temp=Hzk2[chr1][i];}//调用24*24字体
                                else if(size1==32)      
                                                {temp=Hzk3[chr1][i];}//调用32*32字体
                                else if(size1==64)
                                                {temp=Hzk4[chr1][i];}//调用64*64字体
                                else return;
                                                       
                                                for(m=0;m<8;m++)
                                                        {
                                                                if(temp&0x01)OLED_DrawPoint(x,y);
                                                                else OLED_ClearPoint(x,y);
                                                                temp>>=1;
                                                                y++;
                                                        }
                                                        x++;
                                                        if((x-x0)==size1)
                                                        {x=x0;y0=y0+8;}
                                                        y=y0;
                         }
        }
}

//num 显示汉字的个数
//space 每一遍显示的间隔
void OLED_ScrollDisplay(u8 num,u8 space)
{
        u8 i,n,t=0,m=0,r;
        while(1)
        {
                if(m==0)
                {
            OLED_ShowChinese(128,24,t,16); //写入一个汉字保存在OLED_GRAM[][]数组中
                        t++;
                }
                if(t==num)
                        {
                                for(r=0;r<16*space;r++)      //显示间隔
                                 {
                                        for(i=0;i<144;i++)
                                                {
                                                        for(n=0;n<8;n++)
                                                        {
                                                                OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
                                                        }
                                                }
           OLED_Refresh();
                                 }
        t=0;
      }
                m++;
                if(m==16){m=0;}
                for(i=0;i<144;i++)   //实现左移
                {
                        for(n=0;n<8;n++)
                        {
                                OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
                        }
                }
                OLED_Refresh();
        }
}

//配置写入数据的起始位置
void OLED_WR_BP(u8 x,u8 y)
{
        OLED_WR_Byte(0xb0+y,OLED_CMD);//设置行起始地址
        OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
        OLED_WR_Byte((x&0x0f)|0x01,OLED_CMD);
}

//x0,y0:起点坐标
//x1,y1:终点坐标
//BMP[]:要写入的图片数组
void OLED_ShowPicture(u8 x0,u8 y0,u8 x1,u8 y1,u8 BMP[])
{
        u32 j=0;
        u8 x=0,y=0;
        if(y%8==0)y=0;
        else y+=1;
        for(y=y0;y<y1;y++)
         {
                 OLED_WR_BP(x0,y);
                 for(x=x0;x<x1;x++)
                 {
                         OLED_WR_Byte(BMP[j],OLED_DATA);
                         j++;
     }
         }
}
//OLED的初始化
void OLED_Init(void)
{
        GPIO_InitTypeDef  GPIO_InitStructure;
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);         //使能C端口时钟
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5;         
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                  //推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
        GPIO_Init(GPIOC, &GPIO_InitStructure);          //初始化GPIOC4,5
        GPIO_SetBits(GPIOC,GPIO_Pin_4|GPIO_Pin_5);       
       
        delay_ms(200);
       
        OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
        OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
        OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
        OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
        OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
        OLED_WR_Byte(0xCF,OLED_CMD);// Set SEG Output Current Brightness
        OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
        OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
        OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
        OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
        OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
        OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset        Shift Mapping RAM Counter (0x00~0x3F)
        OLED_WR_Byte(0x00,OLED_CMD);//-not offset
        OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
        OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
        OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
        OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
        OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
        OLED_WR_Byte(0x12,OLED_CMD);
        OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
        OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
        OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
        OLED_WR_Byte(0x02,OLED_CMD);//
        OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
        OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
        OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
        OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7)
        OLED_WR_Byte(0xAF,OLED_CMD);
        OLED_Clear();
}
目前仅对几个外设底层驱动进行了编写调试,下面准备上RTT并FATFS。
在main函数中调用:
  //OLED
    OLED_Init();
    OLED_ShowString(40,0,(u8*)"MPU6050",16);
    OLED_Refresh();
    //24CXX
    AT24CXX_Init();                        //IIC初始化
    while(AT24CXX_Check())//检测不到24c02
    {
        OLED_ShowString(0,16,(u8*)"24C04 Check Failed!",16);
        delay_ms(500);
        OLED_ShowString(0,32,(u8*)"Please Check!      ",16);
        delay_ms(500);
        LED0=!LED0;//DS0闪烁
        OLED_Refresh();
    }
    OLED_Clear();
    OLED_ShowString(0,16,(u8*)"24C04 Ready!",16);   
    OLED_Refresh();
    //OLED TEST
    OLED_ShowString(0,0,(u8*)"Start Write...",16);
    AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE);
    OLED_ShowString(0,16,(u8*)"Write Finished!",16);//提示传送完成
    OLED_Refresh();
    delay_ms(5000);
    OLED_Clear();
    OLED_ShowString(0,0,(u8*)"Start Read.... ",16);
    AT24CXX_Read(0,datatemp,SIZE);
    OLED_ShowString(0,16,(u8*)"ReadeData Is:  ",16);//提示传送完成
    OLED_ShowString(16,32,(u8*)datatemp,16);//显示读到的字符串
    OLED_Refresh();
    delay_ms(500);

    //FLASH
    W25QXX_Init();                        //W25QXX初始化
    OLED_Clear();
    while(W25QXX_ReadID()!=B25Q16)                                                                //检测不到B25Q16
    {
        OLED_ShowString(0,0,(u8*)"Check Failed!",16);
        delay_ms(500);
        OLED_ShowString(0,16,(u8*)"Please Check!",16);
        delay_ms(500);
        printf("read id : 0x%x\r\n",W25QXX_ReadID());
        OLED_Refresh();
        LED0=!LED0;//DS0闪烁
    }
    //printf("read id : 0x%x\r\n",W25QXX_ReadID());
    OLED_ShowString(0,32,(u8*)"W25Q64 Ready!",16);
    OLED_Refresh();  
    FLASH_SIZE=2*1024*1024;        //FLASH 大小为2M字节
    //W25Q64 TEST
    OLED_Clear();
    OLED_ShowString(0,0,(u8*)"Start Write...",16);
    W25QXX_Write((u8*)TEXT_Buffer2,FLASH_SIZE-100,SIZE2);                        //从倒数第100个地址处开始,写入SIZE长度的数据
    OLED_ShowString(0,16,(u8*)"Write Finished!",16);//提示传送完成
    OLED_Refresh();
    delay_ms(5000);
    OLED_Clear();
    OLED_ShowString(0,0,(u8*)"Start Read.... ",16);
    W25QXX_Read(datatemp2,FLASH_SIZE-100,SIZE2);                                        //从倒数第100个地址处开始,读出SIZE个字节
    OLED_ShowString(0,16,(u8*)"ReadeData Is:  ",16);//提示传送完成
    OLED_ShowString(16,32,(u8*)datatemp2,16);//显示读到的字符串
    OLED_Refresh();
    delay_ms(500);
    //SD TEST
    OLED_Clear();
    while(SD_Initialize())//检测不到SD卡
        {
                OLED_ShowString(0,0,(u8*)"SD Card Error!",16);
        OLED_Refresh();
                delay_ms(500);                                       
                OLED_ShowString(0,16,(u8*)"Please Check! ",16);
        OLED_Refresh();
                delay_ms(500);
                LED0=!LED0;//DS0闪烁
        }
    //检测SD卡成功
    OLED_Clear();   
    OLED_ShowString(0,16,(u8*)"SD Card OK    ",16);
    OLED_ShowString(0,32,(u8*)"Size:       MB",16);
    sd_size=SD_GetSectorCount();//得到扇区数
    OLED_ShowNum(8*6,32,sd_size>>11,5,16);//显示SD卡容量
    OLED_Refresh();
具体代码如下:
RBT6_DEMO_BASE - Send.rar (1017.46 KB)
实物现象:

PDF原理图:
Schematic_STM32F103RX_ARDUINO_DEMO copy_2021-07-12.pdf (180.65 KB)
转换的XD格式工程:
Project_STM32F103RX_ARDUINO_DEMO copy(AD).rar (530.9 KB)
LCEDA格式工程:
Project_STM32F103RX_ARDUINO_DEMO copy(LCEDA).zip (462.95 KB)
好了,就到这,谢谢大家观看~

使用特权

评论回复

打赏榜单

21小跑堂 打赏了 50.00 元 2021-07-14
理由:恭喜通过原创文章审核!请多多加油哦!

巧克力娃娃 打赏了 1.00 元 2021-07-13
理由:感谢分享!

沙发
小叶三千| | 2021-7-12 16:30 | 只看该作者
感谢分享!

使用特权

评论回复
板凳
mbutterfly| | 2021-7-13 08:48 | 只看该作者
本帖最后由 mbutterfly 于 2021-7-13 08:52 编辑

谢谢分享

使用特权

评论回复
地板
LcwSwust| | 2021-7-13 10:27 | 只看该作者
不错不错

使用特权

评论回复
5
trucyw| | 2021-7-13 12:26 | 只看该作者
不错不错

使用特权

评论回复
6
sparrow054| | 2021-7-14 13:54 | 只看该作者
平时做板子,也弄这么多丝印么

使用特权

评论回复
7
勇敢的大白菜| | 2021-7-14 15:51 | 只看该作者
现在紫色的板子是不是已经免费了?

使用特权

评论回复
8
mxkw0514| | 2021-7-15 10:10 | 只看该作者
caosix 发表于 2021-7-12 23:34
搂主 厉害 :  芯片乱涨价 、其实是暂时的 。。

为什么芯片涨价是暂时的呢?

使用特权

评论回复
9
Alina艾| | 2021-7-24 12:22 | 只看该作者
挺不错的,板子设计的挺漂亮

使用特权

评论回复
10
Betty996| | 2021-7-24 12:23 | 只看该作者
一个小建议,接插件要摆在板子周围,不能摆在板子中间,很麻烦哦 (注意TF卡就在板子中间了 插拔卡稍微有些麻烦)

使用特权

评论回复
11
Emily999| | 2021-7-24 12:24 | 只看该作者
现在typec的接口比较多了,其实改成Type-C的应该更好一些 嘿嘿

使用特权

评论回复
12
Carmen7| | 2021-7-24 12:26 | 只看该作者
arduino接口 属实真的是个标准了 好用

使用特权

评论回复
13
Belle1257| | 2021-7-24 12:27 | 只看该作者
现在MCU都涨价喽 贵到起飞

使用特权

评论回复
14
Charlene沙| | 2021-7-24 12:27 | 只看该作者
做的挺好的一块板子 支持一下

使用特权

评论回复
15
Candic12e| | 2021-7-24 12:28 | 只看该作者
**哪里来 我也搞一份 哈哈哈

使用特权

评论回复
16
alxd| | 2021-7-24 12:29 | 只看该作者
还是黑色的板子好看 排座颜色也奈斯

使用特权

评论回复
17
Allison8859| | 2021-7-24 12:30 | 只看该作者
挺漂亮 支持一下

使用特权

评论回复
18
Carina卡| | 2021-7-24 12:32 | 只看该作者
芯片价格还是被炒得,再等等就好了

使用特权

评论回复
19
Betty1299| | 2021-7-24 12:34 | 只看该作者
现在不仅仅是芯片  电子相关的 基本都不便宜呢

使用特权

评论回复
20
Annie556| | 2021-7-24 12:35 | 只看该作者
arduino开发属实有意思 很简单

使用特权

评论回复
发新帖 本帖赏金 51.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

111

主题

627

帖子

2

粉丝