自主学习STM32已有一周,先实现一个小demo,算是给自己一个动力叭,有目标的学习收获会更多。虽然本科也修了嵌入式课程,但那种走马观花式的学习,最后真正得到的知识实在寥寥无几。个人理解,学习STM32不只是学习编程,更多的是学习查资料、查数据手册、软件的使用和调试方法上,真正需要自己从头造的部分不是很多,吸取前人的经验,搬过来取自己所需即可。用农夫山泉的话来说就是,我们不生产代码,我们只是代码的搬运工! 这次主要跟着正点原子的开发资料进行学习,没有使用战舰开发板,而是使用STM32F103C8T6板子。一是避免自己直接把例程的代码烧进开发板,最后啥也没学到,在不同的板子间移植代码过程中,能够掌握理解更多的基础知识和调试经验;二是这个小板子廉价易得,只要十块钱,和大几百的开发板相比,它体积小、资源可观,很适合我的小项目,以后准备用来制作航模遥控器,敬请关注哈~ 1.STM32F103C8T6蓝色开发板*1(黑色板也可以) 2.USB转TTL模块*1 3. ST-LINK V2仿真器下载器*1(调试STM32性价比极高) 4. OLED屏幕(4管脚)*1 5.10k电位器*1(10k以上都可) 6. 杜邦线、面包板、导线、插针若干 电位器:GND – PA0 – 3.3V ST-LINK V2接法: 实物连接图如下:     安装及破解MDK(Keil5)教程 https://blog.csdn.net/weixin_42911200/article/details/81590158 注意要安装Keil.STM32F1xx_DFP.2.3.0.pack支持包,因为我们要用STM32F103C8T6芯片的库函数编写。 新建keil库函数工程 https://www.cnblogs.com/zeng-1995/p/11308622.html 与链接里面不同的是以下几个设置: 点击图标按钮1,打开Manage Run-Time Environment窗口,Device如下勾选,其他栏与链接中相同; 点击图标按钮2,打开Manage Project Items窗口,Groups和 Files如下设置: 点击图标按钮3,打开Options for Target窗口,点击顶部菜单按钮切换子窗口,依次如下设置: 点击Setting,打开Cortex-M Target Driver Setup窗口,如果SWDIO里面未显示序列号,则电脑需要更新ST-LINK驱动。 解决方法见链接 https://blog.csdn.net/qq_42041980/article/details/92015997 控制舵机的PWM:周期20ms,高电平时间0.5ms~2.5ms变化,可控制舵机0~180°的角度变化,即每个高电平时间都对应舵机的一个角度。但航模舵面的实际控制中,不可能有180°变化,所以通用的高电平宽度其实是1ms~2ms。 控制无刷电调所用的PWM信号高电平时间也是1ms~2ms,所以我们要实现的PWM信号周期20ms,高电平时间1ms~2ms。 我们使用ADC1读取电位器的电压采样值,并从0~4095范围的采样值转换到1000~2000,赋值给PWM输出。 TIM2定时触发ADC采样,通过DMA传输给变量所在的寄存器,取10次进行均值滤波,消除抖动。 定时器触发ADC,DMA传输 https://www.openedv.com/forum.php?mod=viewthread&tid=277863&extra=&page=1 定时器TIM触发ADC采样,DMA搬运到内存 https://blog.csdn.net/qq_38410730/article/details/89921413 TIM3定时触发产生PWM信号,预分频72,频率1MHz,周期1us;自动装载值20 000,故PWM周期1us*20 000=20ms。 主要代码如下: main.c文件-包含程序说明、主函数  config.c文件-包含TIM/ GPIO/ ADC等初始化函数  config.h-包含函数预定义和全局变量预定义  oled.c-包含各种显示函数和IIC初始化 oled.h-包含函数预定义和OLED显示所需的宏定义   其他代码基本就是正点原子官方的文件了,整个工程文件已上传天翼云盘:  https://cloud.189.cn/t/uYniA3iM3iei(访问码:g914) 串口调试助手查看串口输出   OLED显示  
1.材料清单




2.电路连接
 OLED显示屏:
                 GND   电源地
                 VCC   接3.3v电源
                 SCL   接PB8(SCL)
                 SDA   接PB9(SDA)
                 GND   电源地
                 3V3     接3.3v
                 SWCLK 接DCLK
                 SWDIO 接DIO
 串口USB-TTL接法:
                 GND   电源地
                 3V3   接3.3v
                 TXD   接PB7
                 RXD   接PB6
 PWM输出:PB5
3.安装keil5
4.新建工程










 5.程序实现

 
/* =============舵机测试仪==============  芯片STM32F103C8T6,使用ADC读取电位器的电压采样值,0~4095转换到1000~2000,赋值给PWM输出。       TIM2定时触发ADC采样,通过DMA传输给变量ch1Value,取10次进行均值滤波。  控制舵机的PWM:周期20ms,高电平时间0.5ms~2.5ms变化,可控制舵机0~180°的角度变化,        但航模舵面的实际控制中,不可能有180°变化,所以通用的高电平宽度其实是1ms~2ms  电位器:GND - PA0 - 3.3V  OLED显示屏:     GND   电源地     VCC   接3.3v电源     SCL   接PB8(SCL)     SDA   接PB9(SDA)  串口USB-TTL接法:      GND   电源地     3V3   接3.3v     TXD   接PB7     RXD   接PB6  ST-LINK V2接法:     GND   电源地     3V3   接3.3v     SWCLK 接DCLK     SWDIO 接DIO  PWM输出:PB5  by Bilibili 蔡子CaiZi */ #include "config.h" #include "delay.h" #include "usart.h" #include "stm32f10x.h" #include "oled.h" #include "rtc.h"  #include "stdio.h" #include "string.h"  int main() {  u8 txt[16]={0};  delay_init();//初始化延时函数  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2,2位抢占优先级和2位子优先级  usart_init(115200);//初始化串口1,波特率为115200  TIM3_PWM_Init(19999,71);//预分频72,频率1MHz,周期1us;自动装载值20 000,故PWM周期1us*20 000  TIM2_Init(499,71);//1MHz,每500us采集一次;可设置9us以上,但过小影响OLED显示  DMA1_Init(); //DMA初始化  GPIOA_Init(); //PA初始化  Adc_Init();  //ADC初始化  RTC_Init();    //RTC初始化  OLED_Init(); //初始化OLED    OLED_Clear();   while (1){   itoa(PWM1value,txt,10);//将int类型转换成10进制字符串 //  printf("采样值:%dt舵量:%st",ch1Value,txt); //  printf("当前时间:%d:%d:%dn",calendar.hour,calendar.min,calendar.sec);   //OLED_Clear();//一直清屏会造成闪烁   strcat(txt," us");//合并字符串   OLED_ShowString(6,3,txt,24); //位置6,3;字符大小24*24点阵   OLED_Refresh_Gram();   delay_ms(1);  } }  
#include "config.h" #include "delay.h" #include "usart.h" #include "sys.h" #include "rtc.h"  volatile u16 ch1Value[10];//ADC采样值 volatile u16 PWM1value;//控制PWM占空比 #define ADC1_DR_Address    ((u32)0x4001244C)  //ADC1的地址 //通用定时器2中断初始化 //这里时钟选择为APB1的2倍,而APB1为36M //arr:自动重装值。 //psc:时钟预分频数 //这里使用的是定时器2控制ADC定时采样 void TIM2_Init(u16 arr,u16 psc) {  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  TIM_OCInitTypeDef TIM_OCInitStructure;    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);   //时钟使能   //定时器TIM2初始化  TIM_TimeBaseStructure.TIM_Period = arr;   //设置在下一个更新事件装入活动的自动重装载寄存器周期的值  TIM_TimeBaseStructure.TIM_Prescaler =psc;    //设置用来作为TIMx时钟频率除数的预分频值  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;   //设置时钟分割:TDTS = Tck_tim  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;   //TIM向上计数模式  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);   //根据指定的参数初始化TIMx的时间基数单位    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;  //选择定时器模式:TIM脉冲宽度调制模式1  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  //比较输出使能  TIM_OCInitStructure.TIM_Pulse = 9;       //计数达到9产生中断  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;  //输出极性:TIM输出比较极性低  TIM_OC2Init(TIM2, & TIM_OCInitStructure);  //初始化外设TIM2_CH2    TIM_Cmd(TIM2, ENABLE);    //使能TIMx  TIM_CtrlPWMOutputs(TIM2, ENABLE);  }   //DMA1配置 void DMA1_Init(void) {  DMA_InitTypeDef DMA_InitStructure;  NVIC_InitTypeDef NVIC_InitStructure;   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);      //使能ADC1通道时钟    //DMA1初始化  DMA_DeInit(DMA1_Channel1);  DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;    //ADC1地址  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ch1Value;   //ch1Value的内存地址  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;     //方向(从外设到内存)  DMA_InitStructure.DMA_BufferSize = 10;       //DMA缓存大小,存放10次采样值  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址固定,接收一次数据后,设备地址禁止后移  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;    //内存地址不固定,接收多次数据后,目标内存地址后移  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord ; //外设数据单位,定义外设数据宽度为16位  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord ;    //内存数据单位,HalfWord就是为16位  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular  ;   //DMA模式:循环传输  DMA_InitStructure.DMA_Priority = DMA_Priority_High ;  //DMA优先级:高  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;     //禁止内存到内存的传输  DMA_Init(DMA1_Channel1, &DMA_InitStructure);  //配置DMA1    DMA_ITConfig(DMA1_Channel1,DMA_IT_TC, ENABLE);  //使能传输完成中断   NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  NVIC_Init(&NVIC_InitStructure);    DMA_Cmd(DMA1_Channel1,ENABLE); }  //中断处理函数 void  DMA1_Channel1_IRQHandler(void) {  int sum=0;  if(DMA_GetITStatus(DMA1_IT_TC1)!=RESET){   //中断处理代码   for(int i=0;i<10;i++){    sum += ch1Value[i];   }//均值滤波   PWM1value = (int)map(sum/10,0,4092,1000,2000);   sum=0;   printf("%dt",PWM1value);   printf("当前时间:%d:%d:%drn",calendar.hour,calendar.min,calendar.sec);   TIM_SetCompare2(TIM3,PWM1value);//输出给PWM   DMA_ClearITPendingBit(DMA1_IT_TC1);//清除标志  } } //GPIO配置,PA0 void GPIOA_Init(void) {  GPIO_InitTypeDef GPIO_InitStructure;    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   //使能GPIOA时钟   //PA6 作为模拟通道输入引脚     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;  GPIO_Init(GPIOA, &GPIO_InitStructure);   }  //初始化ADC //这里我们仅以规则通道为例 //我们默认将开启通道0~3                     void  Adc_Init(void) {    ADC_InitTypeDef ADC_InitStructure;   RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);   //使能ADC1通道时钟   //ADC1初始化  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;    //独立ADC模式  ADC_InitStructure.ADC_ScanConvMode = DISABLE;      //关闭扫描方式  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;   //关闭连续转换模式  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;    //使用外部触发模式  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;    //采集数据右对齐  ADC_InitStructure.ADC_NbrOfChannel = 1;    //要转换的通道数目  ADC_Init(ADC1, &ADC_InitStructure);    RCC_ADCCLKConfig(RCC_PCLK2_Div6);    //配置ADC时钟,为PCLK2的6分频,即12MHz  ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);  //配置ADC1通道0为239.5个采样周期     //使能ADC、DMA  ADC_DMACmd(ADC1,ENABLE);  ADC_Cmd(ADC1,ENABLE);    ADC_ResetCalibration(ADC1);    //复位校准寄存器  while(ADC_GetResetCalibrationStatus(ADC1));    //等待校准寄存器复位完成    ADC_StartCalibration(ADC1);    //ADC校准  while(ADC_GetCalibrationStatus(ADC1));    //等待校准完成    ADC_ExternalTrigConvCmd(ADC1, ENABLE);  //设置外部触发模式使能 }  //获得ADC值 //ch:通道值 0~9 u16 Get_Adc(u8 ch)    {    //设置指定ADC的规则组通道,一个序列,采样时间  ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5个周期   ADC_SoftwareStartConvCmd(ADC1, ENABLE);  //使能指定的ADC1的软件转换启动功能   while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束  return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果 }  //ch:通道值 0~9,采样times次后作均值滤波 u16 Get_Adc_Average(u8 ch,u8 times) {  u32 temp_val=0;  u8 t;  for(t=0;t<times;t++)  {   temp_val+=Get_Adc(ch);   delay_ms(5);  }  return temp_val/times; } //TIM3 PWM部分初始化  //PWM输出初始化 //arr:自动重装值 //psc:时钟预分频数 void TIM3_PWM_Init(u16 arr,u16 psc) {    GPIO_InitTypeDef GPIO_InitStructure;  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;  TIM_OCInitTypeDef  TIM_OCInitStructure;     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟    GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射  TIM3_CH2->PB5          //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //推挽输出  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB      //初始化TIM3  TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值  TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值   TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位    //初始化TIM3 Channel2 PWM模式    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1,计数值<自动重装载值时,输出高电平   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高  TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC2   TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的预装载寄存器   TIM_Cmd(TIM3, ENABLE);  //使能TIM3 }   /*函数说明:仿Arduino,将一个数字从一个范围重新映射到另一个范围 也就是说,fromLow的值将映射到toLow,fromlhigh到toHigh的值等等。 */ float map(float value,float fromLow,float fromHigh,float toLow,float toHigh) {  return ((value-fromLow)*(toHigh-toLow)/(fromHigh-fromLow)+toLow); }   
#ifndef __CONFIG_H #define __CONFIG_H #include "stm32f10x.h" //记得添加此头文件,因为config.c用到GPIO相关函数等 #include "sys.h" extern volatile u16 ch1Value[10];//ADC采样值 extern volatile u16 PWM1value;//控制PWM占空比 void TIM2_Init(u16 arr,u16 psc);//TIM2定时器初始化 void TIM3_PWM_Init(u16 arr,u16 psc);//PB5定时器初始化 void DMA1_Init(void); void GPIOA_Init(void); void Adc_Init(void);//ADC1初始化 u16 Get_Adc(u8 ch); //获取一次ADC的值 u16 Get_Adc_Average(u8 ch,u8 times);//ADC采样值进行均值滤波 float map(float value,float fromLow,float fromHigh,float toLow,float toHigh);//映射函数 #endif 
//////////////////////////////////////////////////////////////////////////////////   //  功能描述   : 0.69寸OLED 接口演示例程(STM32F103C8T6 IIC) //              说明:  //              ---------------------------------------------------------------- //              GND   电源地 //              VCC   接3.3v电源 //              SCL   接PB8(SCL) //              SDA   接PB9(SDA)      //////////////////////////////////////////////////////////////////////////////////?  #include "oled.h" #include "stdlib.h" #include "oledfont.h"     #include "delay.h" //OLED的显存 //存放格式如下. //[0]0 1 2 3 ... 127  //[1]0 1 2 3 ... 127  //[2]0 1 2 3 ... 127  //[3]0 1 2 3 ... 127  //[4]0 1 2 3 ... 127  //[5]0 1 2 3 ... 127  //[6]0 1 2 3 ... 127  //[7]0 1 2 3 ... 127  /********************************************** //IIC Start **********************************************/ void IIC_Start(void) {   OLED_SCLK_Set() ;  OLED_SDIN_Set();  OLED_SDIN_Clr();  OLED_SCLK_Clr(); }  /********************************************** //IIC Stop **********************************************/ void IIC_Stop(void) { OLED_SCLK_Set() ; // OLED_SCLK_Clr();  OLED_SDIN_Clr();  OLED_SDIN_Set();   }  void IIC_Wait_Ack(void) {  OLED_SCLK_Set() ;  OLED_SCLK_Clr(); } /********************************************** // IIC Write byte **********************************************/  void Write_IIC_Byte(unsigned char IIC_Byte) {  unsigned char i;  unsigned char m,da;  da=IIC_Byte;  OLED_SCLK_Clr();  for(i=0;i<8;i++)    {    m=da;   // OLED_SCLK_Clr();   m=m&0x80;   if(m==0x80)   {OLED_SDIN_Set();}   else OLED_SDIN_Clr();    da=da<<1;   OLED_SCLK_Set();   OLED_SCLK_Clr();   }   } /********************************************** // IIC Write Command **********************************************/ void Write_IIC_Command(unsigned char IIC_Command) {  IIC_Start();  Write_IIC_Byte(0x78);            //Slave address,SA0=0  IIC_Wait_Ack();   Write_IIC_Byte(0x00);   //write command  IIC_Wait_Ack();   Write_IIC_Byte(IIC_Command);   IIC_Wait_Ack();   IIC_Stop(); } /********************************************** // IIC Write Data **********************************************/ void Write_IIC_Data(unsigned char IIC_Data) {    IIC_Start();    Write_IIC_Byte(0x78);   //D/C#=0; R/W#=0  IIC_Wait_Ack();     Write_IIC_Byte(0x40);   //write data  IIC_Wait_Ack();     Write_IIC_Byte(IIC_Data);  IIC_Wait_Ack();     IIC_Stop(); } void OLED_WR_Byte(unsigned dat,unsigned cmd) {  if(cmd){   Write_IIC_Data(dat);  }  else {   Write_IIC_Command(dat);  }   }   /******************************************** // fill_Picture ********************************************/ void fill_picture(unsigned char fill_Data) {  unsigned char m,n;  for(m=0;m<8;m++)  {   OLED_WR_Byte(0xb0+m,0);  //page0-page1   OLED_WR_Byte(0x00,0);  //low column start address   OLED_WR_Byte(0x10,0);  //high column start address   for(n=0;n<128;n++)    {     OLED_WR_Byte(fill_Data,1);    }  } }   //坐标设置  void OLED_Set_Pos(unsigned char x, unsigned char y)  {  OLED_WR_Byte(0xb0+y,OLED_CMD);  OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);  OLED_WR_Byte((x&0x0f),OLED_CMD);  }       //开启OLED显示     void OLED_Display_On(void) {  OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令  OLED_WR_Byte(0X14,OLED_CMD);  //DCDC ON  OLED_WR_Byte(0XAF,OLED_CMD);  //DISPLAY ON } //关闭OLED显示      void OLED_Display_Off(void) {  OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令  OLED_WR_Byte(0X10,OLED_CMD);  //DCDC OFF  OLED_WR_Byte(0XAE,OLED_CMD);  //DISPLAY OFF }          //清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!    void OLED_Clear(void)   {    u8 i,n;        for(i=0;i<8;i++)    {     OLED_WR_Byte (0xb0+i,OLED_CMD);    //设置页地址(0~7)   OLED_WR_Byte (0x00,OLED_CMD);      //设置显示位置—列低地址   OLED_WR_Byte (0x10,OLED_CMD);      //设置显示位置—列高地址      for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA);   } //更新显示 } //更新显存到OLED   u8 OLED_GRAM[128][8]; void OLED_Refresh_Gram(void) {  u8 i,n;        for(i=0;i<8;i++)    {     OLED_WR_Byte (0xb0+i,OLED_CMD);    //设置页地址(0~7)   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);   }    }  //画点  //x:0~127 //y:0~63 //t:1 填充 0,清空      void OLED_DrawPoint(u8 x,u8 y,u8 t) {  u8 pos,bx,temp=0;  if(x>127||y>63)return;//超出范围了.  pos=7-y/8;  bx=y%8;  temp=1<<(7-bx);  if(t)OLED_GRAM[x][pos]|=temp;  else OLED_GRAM[x][pos]&=~temp;      } //x1,y1,x2,y2 填充区域的对角坐标 //确保x1<=x2;y1<=y2 0<=x1<=127 0<=y1<=63     //dot:0,清空;1,填充    void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot)   {    u8 x,y;    for(x=x1;x<=x2;x++)  {   for(y=y1;y<=y2;y++)OLED_DrawPoint(x,y,dot);  }                   OLED_Refresh_Gram();//更新显示 }  //在指定位置显示一个字符,包括部分字符 //x:0~127 //y:0~63 //mode:0,反白显示;1,正常显示      //size:选择字体 12/16/24 void OLED_ShowChar(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=asc2_1206[chr][t];    //调用1206字体   else if(size==16)temp=asc2_1608[chr][t]; //调用1608字体   else if(size==24)temp=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;    }   }         }           } //m^n函数 u32 mypow(u8 m,u8 n) {  u32 result=1;    while(n--)result*=m;      return result; }       //显示2个数字 //x,y :起点坐标   //len :数字的位数 //size:字体大小12/16/24 //mode:模式 0,填充模式;1,叠加模式 //num:数值(0~4294967295);       void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size) {            u8 t,temp;  u8 enshow=0;           for(t=0;t<len;t++)  {   temp=(num/mypow(10,len-t-1))%10;   if(enshow==0&&t<(len-1))   {    if(temp==0)    {     OLED_ShowChar(x+(size/2)*t,y,' ',size,1);     continue;    }else enshow=1;          }    OLED_ShowChar(x+(size/2)*t,y,temp+'0',size,1);   } }  //显示字符串 //x,y:起点坐标   //size:字体大小12/16/24 //*p:字符串起始地址  void OLED_ShowString(u8 x,u8 y, u8 *p,u8 size) {      while((*p<='~')&&(*p>=' '))//判断是不是非法字符!     {                if(x>(128-(size/2))){x=0;y+=size;}         if(y>(64-size)){y=x=0;OLED_Clear();}         OLED_ShowChar(x,y,*p,size,1);           x+=size/2;         p++;     }   }    //显示汉字 void OLED_ShowCHinese(u8 x,u8 y,u8 no) {               u8 t,adder=0;  OLED_Set_Pos(x,y);      for(t=0;t<16;t++)   {     OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);     adder+=1;      }    OLED_Set_Pos(x,y+1);      for(t=0;t<16;t++)    {      OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);     adder+=1;       }      } /***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/ void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[]) {    unsigned int j=0;  unsigned char x,y;      if(y1%8==0) y=y1/8;         else y=y1/8+1;  for(y=y0;y<y1;y++)  {   OLED_Set_Pos(x0,y);     for(x=x0;x<x1;x++)      {             OLED_WR_Byte(BMP[j++],OLED_DATA);            }  } }   //初始化SSD1306          void OLED_Init(void) {     GPIO_InitTypeDef  GPIO_InitStructure;    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);  //使能B端口和AFIO复用功能模块时钟    GPIO_PinRemapConfig(GPIO_Remap_I2C1, ENABLE);//IIC1重映射 -> PB8,9    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9;     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz   GPIO_Init(GPIOB, &GPIO_InitStructure);   //初始化GPIOB8,9   GPIO_SetBits(GPIOB,GPIO_Pin_8|GPIO_Pin_9);     delay_ms(800);  OLED_WR_Byte(0xAE,OLED_CMD);//--显示关闭  OLED_WR_Byte(0x00,OLED_CMD);//---设置最小列地址  OLED_WR_Byte(0x10,OLED_CMD);//---设置最大列地址  OLED_WR_Byte(0x40,OLED_CMD);//--set start line address    OLED_WR_Byte(0xB0,OLED_CMD);//--set page address  OLED_WR_Byte(0x81,OLED_CMD); // contract control  OLED_WR_Byte(0xFF,OLED_CMD);//--128     OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap   OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverse  OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)  OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 duty  OLED_WR_Byte(0xC0,OLED_CMD);//Com扫描方向,若显示的是镜对称,改为C8  OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset  OLED_WR_Byte(0x00,OLED_CMD);//    OLED_WR_Byte(0xD5,OLED_CMD);//set osc division  OLED_WR_Byte(0x80,OLED_CMD);//    OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode off  OLED_WR_Byte(0x05,OLED_CMD);//    OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge Period  OLED_WR_Byte(0xF1,OLED_CMD);//    OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartion  OLED_WR_Byte(0x12,OLED_CMD);//    OLED_WR_Byte(0xDB,OLED_CMD);//set Vcomh  OLED_WR_Byte(0x30,OLED_CMD);//    OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enable  OLED_WR_Byte(0x14,OLED_CMD);//    OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel }    u8 *itoa(int num,u8 *str,int radix) {  char index[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";//索引表  unsigned unum;//存放要转换的整数的绝对值,转换的整数可能是负数  int i=0,j,k;//i用来指示设置字符串相应位,转换之后i其实就是字符串的长度;转换后顺序是逆序的,有正负的情况,k用来指示调整顺序的开始位置;j用来指示调整顺序时的交换。    //获取要转换的整数的绝对值  if(radix==10&&num<0)//要转换成十进制数并且是负数  {   unum=(unsigned)-num;//将num的绝对值赋给unum   str[i++]='-';//在字符串最前面设置为'-'号,并且索引加1  }  else unum=(unsigned)num;//若是num为正,直接赋值给unum    //转换部分,注意转换后是逆序的  do  {   str[i++]=index[unum%(unsigned)radix];//取unum的最后一位,并设置为str对应位,指示索引加1   unum/=radix;//unum去掉最后一位    }while(unum);//直至unum为0退出循环    str[i]=' ';//在字符串最后添加' '字符,c语言字符串以' '结束。    //将顺序调整过来  if(str[0]=='-') k=1;//如果是负数,符号不用调整,从符号后面开始调整  else k=0;//不是负数,全部都要调整    u8 temp;//临时变量,交换两个值时用到  for(j=k;j<=(i-1)/2;j++)//头尾一一对称交换,i其实就是字符串的长度,索引最大值比长度少1  {   temp=str[j];//头部赋值给临时变量   str[j]=str[i-1+k-j];//尾部赋值给头部   str[i-1+k-j]=temp;//将临时变量的值(其实就是之前的头部值)赋给尾部  }    return str;//返回转换后的字符串 }   
//////////////////////////////////////////////////////////////////////////////////  //  功能描述   : 0.69寸OLED 接口演示例程(STM32F103C8T6 IIC) //              说明:  //              ---------------------------------------------------------------- //              GND   电源地 //              VCC   接3.3v电源 //              SCL   接PB8(SCL) //              SDA   接PB9(SDA)             //              ---------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////////////// #ifndef __OLED_H #define __OLED_H        #include "sys.h" #include "stdlib.h"       #define OLED_MODE 0 #define SIZE 8 #define XLevelL  0x00 #define XLevelH  0x10 #define Max_Column 128 #define Max_Row  64 #define Brightness 0xFF  #define X_WIDTH  128 #define Y_WIDTH  64              //-----------------OLED IIC端口定义----------------            #define OLED_SCLK_Clr() GPIO_ResetBits(GPIOB,GPIO_Pin_8)//SCL #define OLED_SCLK_Set() GPIO_SetBits(GPIOB,GPIO_Pin_8)  #define OLED_SDIN_Clr() GPIO_ResetBits(GPIOB,GPIO_Pin_9)//SDA #define OLED_SDIN_Set() GPIO_SetBits(GPIOB,GPIO_Pin_9)           #define OLED_CMD  0 //写命令 #define OLED_DATA 1 //写数据   //OLED控制用函数 void OLED_WR_Byte(unsigned dat,unsigned cmd);   void OLED_Display_On(void); void OLED_Display_Off(void);                     void OLED_Init(void); void OLED_Clear(void); void OLED_Refresh_Gram(void); void OLED_DrawPoint(u8 x,u8 y,u8 t); void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot); void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode); void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size); void OLED_ShowString(u8 x,u8 y, u8 *p,u8 size);   void OLED_Set_Pos(unsigned char x, unsigned char y); void OLED_ShowCHinese(u8 x,u8 y,u8 no); void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[]); void fill_picture(unsigned char fill_Data); void IIC_Start(void); void IIC_Stop(void); void Write_IIC_Command(unsigned char IIC_Command); void Write_IIC_Data(unsigned char IIC_Data); void Write_IIC_Byte(unsigned char IIC_Byte); void IIC_Wait_Ack(void); u8 *itoa(int num,u8 *str,int radix); #endif     6.实现效果


本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算
官方软件产品操作指南 (170)