自主学习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输出:PB53.安装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网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算