STM32

一、工程搭建

1.1 启动文件

工程目录->Library->startup

工程目录->Library->

内核文件

寄存器地址映射文件

 系统文件 如时钟频率

1.2 添加文件

 

 1.3 添加头文件路径

  

1.4 测试

 添加User文件夹

 

1.5 库函数

 inc存放头文件   src存放c文件

添加到工程

库函数连接文件

 库函数头文件的包含关系

存放中断函数

条件编译,只有添加USE_STDPERIPH_DRIVER宏定义,才会编译这个头文件

 添加其它头文件路径

二、GPIO

2.1  电平

最高可输出3.3v

部分引脚可容忍5v

2.2  GPIOA、GPIOB、GPIOC

它们都是32位的寄存器,但是每个只有16个IO口,所以只用到了低16位

2.3  上拉输入和下拉输入

确保IO口什么都不接时的电平的状态,且电阻比较大,尽量不影响输入电平

上拉输入:默认高电平

下拉输入:默认低电平

2.4  施密特触发器

 对输入信号进行整形,高于阈值为1,低于为0

2.5  推挽输出

 有较高的输出能力,高低电平STM32说了算

2.6  开漏模式

1、低电平有驱动能力,而高电平无驱动能力,用于IIc通信协议避免各个设备的相互干扰

2、外接VCC加电阻可以输出5V电平,用来兼容5V的设备

2.7  8钟模式

 

1、输入模式下,输出无效

2、输出模式下,可以输入 

#include "stm32f10x.h"                  // Device header

void Init1(void)//初始化灯
{
	GPIO_InitTypeDef GPIO_InitStruct;//结构图定义

	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;//引脚
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;//模式为推挽输出
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//设置频率为50HZ
	GPIO_Init(GPIOB,  &GPIO_InitStruct);//GPIO初始化
 
}

int main()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//总线2时钟开关
	//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//总线2时钟开关
	Init1();
	GPIO_ResetBits(GPIOB, GPIO_Pin_6);
	while(1)
	{
		
	}
	
}

 三、按键控制

3.1  数据类型

3.2 结构体、枚举

 

3.3 按键程序

Key.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
void Key_Init()
{
	GPIO_InitTypeDef TypeDef;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	TypeDef.GPIO_Mode = GPIO_Mode_IPU; //上拉输入模式,默认高电平
	TypeDef.GPIO_Pin = GPIO_Pin_0;;
	TypeDef.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&TypeDef);
}
uint8_t Get_KeyNum()
{
	uint8_t keyNum = 0;
	if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==0){
		delay_ms(20);
		while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==0);//按下不放手
		delay_ms(20);
		keyNum = 1;
	}
	return keyNum;
}
}

Key.h

#ifndef __KEY_H
#define __KEY_H
void Key_Init(void);
uint8_t Get_KeyNum(void);
#endif

LED.c

#include "stm32f10x.h"                  // Device header
void LED_Init(void)
{ 
	GPIO_InitTypeDef typedefine;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	typedefine.GPIO_Mode = GPIO_Mode_Out_PP;
	typedefine.GPIO_Pin =  GPIO_Pin_5;
	typedefine.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&typedefine);\
	//GPIO_SetBits(GPIOB,GPIO_Pin_6);
	GPIO_WriteBit(GPIOB,GPIO_Pin_5,Bit_SET);
}

void LED_ON(void)
{
	GPIO_ResetBits(GPIOB,GPIO_Pin_5);
}

void LED_OFF(void)
{
	GPIO_SetBits(GPIOB,GPIO_Pin_5);
}
void LED_Turn() //电平翻转
{
	if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_5)==0) //如果输出的数据是0
	{
		GPIO_SetBits(GPIOB,GPIO_Pin_5);
	}else
	{
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);
	}
	
}

LED.h

#ifndef __LED_H
#define __LED_H
void LED_Init(void);
void LED_ON(void);
void LED_OFF(void);
void LED_Turn();

#endif

四、中断

4.1 NVIC

帮助CPU管理中断

4.2 响应优先级和抢占优先级

 响应优先级:触发中断时等之前的中断结束后才执行,按照优先级顺序进行执行

抢占优先级:触发中断时可以打断正在执行的中断,实现中断嵌套

4.3 EXTI外部中断

 

PA0和PB0不能同时触发中断

 PA1和PB1,PC1不能同时触发中断

相同的Pin口不能同时触发中断 

16(GPIO)+4(PVD,RTC,USB,ETH) = 20个中断源 

 4.4 代码

EXTI.c

#include "stm32f10x.h"                  // Device header
void EXIT_Init(void)
{
	GPIO_InitTypeDef TypeDef;
	EXTI_InitTypeDef  InitTypeDef;
	NVIC_InitTypeDef NVIC_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	TypeDef.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
	TypeDef.GPIO_Pin = GPIO_Pin_0;
	TypeDef.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&TypeDef);
	
	//AFIO,选择数据引脚
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
	
	//EXTI 选择中断触发方式
	InitTypeDef.EXTI_Line = EXTI_Line0;
	InitTypeDef.EXTI_LineCmd = ENABLE;
	InitTypeDef.EXTI_Mode = EXTI_Mode_Interrupt;
	InitTypeDef.EXTI_Trigger = EXTI_Trigger_Falling;
	EXTI_Init(&InitTypeDef);
	
	//NVIC 配置中断优先级
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_Init(&NVIC_InitStructure);
	
}
void EXTI0_IRQHandler()
{
	if(EXTI_GetITStatus(EXTI_Line0) == SET)//是否是该中断
	{
		EXTI_ClearITPendingBit(EXTI_Line0); //清除中断标志位,不然会卡死在这里
	}
	
}



五、定时器

5.1 类型

5.2 基本定时器

 系统内部时钟:72MHZ

PSC:预分频  范围0—65535  减小系统频率,延长计数时间   72/65535

CNT:对分频完的频率进行计数,当计数值到达自动重装寄存器的值就触发中断

基本定时器仅支持向下计数这一种模式,且时钟源只能选择内部时钟

5.3 通用定时器

通用定时器以及高级定时器支持向下计数,向上计数,中央对齐,可以选择外部时钟

5.4 高级定时器

基本定时器,通用定时器一旦到达设定值就会触发中断,但是高级定时器可以设置到达几个周期后再触发中断   

周期:0-65535

 5.5 定时中断结构

5.6 溢出频率

频率:72MHZ/(PSC+1)/(ARR+1)

时间:取到数 

5.7 流程

5.8 代码

void delay_ms(uint16_t num)
{    
    //定时1秒
	NVIC_InitTypeDef NVIC_InitTypeStructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	
	//初始化时基单元
	
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //一般默认不分频
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
	TIM_TimeBaseStructure.TIM_Period = 10000-1; //ARR
	TIM_TimeBaseStructure.TIM_Prescaler = 7200-1; //PSC
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;//是否周期计数
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);
	
	//中断输出控制
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //更新中断
	
	//NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //2位抢先,2位优先
	
	NVIC_InitTypeStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitTypeStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitTypeStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitTypeStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_Init(&NVIC_InitTypeStructure);
	//启动定时器
	TIM_Cmd(TIM2,ENABLE);
}

void TIM2_IRQHandler()
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
	{
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}

六、PWM输出比较

6.1 pwm简介 

频率:一般几KHz

6.2 原理图

 

CNT和CRR比较后输出REF 

 

 

黄线:ARR

红线:CCR

6.3 PWM引脚

 6.4 重映射

6.5 代码

#include "stm32f10x.h"                  // Device header
void PWM_Init(void)
{
	
	//只有复用引脚,PB5才能重定义TIM3的通道2
	GPIO_InitTypeDef typedefine;
	NVIC_InitTypeDef NVIC_InitTypeStructure;
	TIM_OCInitTypeDef TIM_OCInitStrucyure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	                   
	//重定义  TIM3_CH2 由PA7 变为PB5 用AFIO完成
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);
	
	//初始化PB5引脚
	
	typedefine.GPIO_Mode = GPIO_Mode_AF_PP; //使用复用推挽输出模式
	typedefine.GPIO_Pin =  GPIO_Pin_5;
	typedefine.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&typedefine);

	
	//初始化时基单元
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //一般默认不分频
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
	TIM_TimeBaseStructure.TIM_Period = 100-1; //ARR
	TIM_TimeBaseStructure.TIM_Prescaler = 720-1; //PSC
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;//是否周期计数
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
	
	//初始化PWM
	TIM_OCStructInit(&TIM_OCInitStrucyure); //为没有初始化的结构体变量成员赋值
	TIM_OCInitStrucyure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStrucyure.TIM_OCNPolarity = TIM_OCPolarity_High; //ref输出极性不变
	TIM_OCInitStrucyure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStrucyure.TIM_Pulse = 50;//1kHz占空比为50%
	TIM_OC2Init(TIM3,&TIM_OCInitStrucyure);//通道2

	//启动定时器产生PWM波
	TIM_Cmd(TIM3,ENABLE);
}
void Set_CCR(uint16_t ccr) //更改CCR的值
{
	TIM_SetCompare2(TIM3,ccr);
}

七、串口

7.1 分类

 

8位字长就设置为无校验

9位字长就设置1位校验位

USART1是APB2的外设

USART2和USART3是APB1的外设

发送的数据是一个字节8位二进制数,一般是HEX读取,就是二位16进制数 

7.2 发送代码

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
uint8_t dat;
void Uart_Init()
{
	GPIO_InitTypeDef   GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
		NVIC_InitTypeDef NVIC_InitTypeStructure;
	//PA9发送数据
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
  GPIO_Init(GPIOA, &GPIO_InitStructure);

	//PA10接收数据
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入或浮空
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	//USART初始化
  USART_InitStructure.USART_BaudRate = 115200; //设置波特率;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式
  USART_InitStructure.USART_StopBits = USART_StopBits_1; //1位停止位
  USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制
  USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //收发模式
  USART_Init(USART1, &USART_InitStructure); //初始化串口
	//USART输出中断
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
	//设置接收中断
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitTypeStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitTypeStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitTypeStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitTypeStructure);
	//启动
	USART_Cmd(USART1, ENABLE);
}
void Send_Byte(uint8_t dat)
{
	USART_SendData(USART1,dat);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)== RESET);
}

void Send_String(char * String)
{
	for(uint8_t i=0;String[i] != 0;i++)
	{
		Send_Byte(String[i]);
	}
	//Send_String("how are you\r\n"); 换行
}
uint8_t Rec_Byte()
{
	uint8_t dat;
	while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == RESET);
	dat = USART_ReceiveData(USART1);
	return dat;
}
void Rec_Str(uint8_t * str)
{
	*str = Rec_Byte();
	while(*str != '\r' && *str != '\n' && *str != '\0')
	{
		str++;
		*str = Rec_Byte();
	}
	str++;
	*str = '\0';
}
void USART1_IRQHandler()
{
	uint8_t str[100];
	if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET)
	{
		//如果读取了数据标志位自动清除,如果未读取标志位需要手动清除
		Rec_Str(str);
		Send_Str(str);
	}
}

7.3 使用printf

#include "stm32f10x.h"                  // Device header
#include "stdio.h"

int fputc(int ch,FILE * f)
{
	Send_Byte(ch);
	return ch;
}

 printf只能有一个,重定向到串口1,串口2和3就不能用,此时用sprintf

	char String[100];
	sprintf(String,"num = %d\r\n",10);
	Send_String(String);

八、函数库

8.1 精确延时

System_Delay.h

#ifndef __SYSTEM_DELAY_H
#define __SYSTEM_DELAY_H

#include "stm32f10x.h"
#include "core_cm3.h"

void delay_us(uint32_t us);//微秒级别
void delay_ms(uint32_t ms);//毫秒级别

#endif

System_Delay.c

#include "System_Delay.h"
#if 0
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
	//  判断 tick 的值是否大于 2^24,如果大于,则不符合规则
  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);

  // 初始化reload寄存器的值	
  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;
	
  // 配置中断优先级,配置为15,默认为最低的优先级
	NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); 

  // 初始化counter的值为0	
  SysTick->VAL   = 0; 
  
  // 配置 systick 的时钟为 72M
  // 使能中断
  // 使能systick
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk   | 
                   SysTick_CTRL_ENABLE_Msk;                    
  return (0);                                                 
}
#endif

void delay_us(uint32_t us)
{
	uint32_t i; //定义一个变量
	SysTick_Config(72);//直接写ticks,这样比较好理解 72*(1/72M)=1us
	
	for(i=0; i<us; i++) //循环,当us=1000时,则执行1000次循环
	{
		while( !((SysTick->CTRL) & (1<<16)) );  //位与,当递减到0,置1,则!1=0
	}
	
	SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;//关闭定时器
}

void delay_ms(uint32_t ms)
{
	uint32_t i;
	SysTick_Config(72000);
	
	for(i=0; i<ms; i++)
	{
		while( !((SysTick->CTRL) & (1<<16)) );
	}
	
	SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
}

九、 IIC

9.1 简介

有应答机制 

9.2 硬件电路

 

对于SCL线时钟线,从机在什么时刻只能被动得读取

对于SDA数据线,只有主机读取从机数据时或者从机应答时,才能获得短暂的控制权 

 9.3 时序

起始条件:SCL保持高电平,SDA产生一个下降沿,之后拉低SCL

终止条件:SCL先变为高电平,SDA再变为高电平 

串口时序是低位先行,这里是高位先行 

释放SDA即主机切换为输入模式 

9.4 从机地址

 从机地址一般是7位,如MPU6050是:1101000,如果发生地址冲突,则一般可以通过相应的引脚之置高低电平,改变地址的后几位,高位(一般是高4位)由产商确定,低三位可以自由切换

9.5 三种基本模式

1、第一个字节:从机地址7位+1位读写位,0表示写,1表示读

2、从机应答位 0表示应答,1表示未应答

3、要操作的寄存器的地址

4、从机应答位 0表示应答,1表示未应答

5、写入寄存器的数据

6、从机应答位 0表示应答,1表示未应答

7、终止信号

当前地址读:例如写数据时向地址为0x01的寄存器写入了数据,下一次操作时,从机地址的指针就会自增1,如果这时读取数据读到的将是0x02地址的数据

1、第一个字节:从机地址7位+1位读写位,0表示写,1表示读

2、从机应答位 0表示应答,1表示未应答

5、读取寄存器的数据

6、主机发送应答 0表示应答,1表示未应答

7、终止信号

 

1、第一个字节:从机地址7位+1位写位

2、要操作的寄存器的地址

3、起始信号

4、从机地址7位+1位读位 (读取当前地址)

总结:就是指定地址写+当前地址读

指定地址写多条数据,由于指针是会自增的,所以写入的是一片连续区域:如0x00,0x01,0x02

读多条数据 ,由于指针是会自增的,所以读出的是一片连续区域:如0x00,0x01,0x02

发送的多条数据中,最后一个数据给非应答,让从机交出控制权

9.6 代码 

IIc.h

#ifndef __IIC_H
#define __IIC_H

#define IIC_SCL_H   GPIO_SetBits( GPIOA,  GPIO_Pin_1)
#define IIC_SCL_L   GPIO_ResetBits(GPIOA,  GPIO_Pin_1)
 
#define IIC_SDA_H   GPIO_SetBits( GPIOA,  GPIO_Pin_2)
#define IIC_SDA_L   GPIO_ResetBits( GPIOA,  GPIO_Pin_2)
 
#define READ_SDA    GPIO_ReadInputDataBit(GPIOA,  GPIO_Pin_2)
 
void IIC_Init(void);
void SDA_OUT(void);
void SDA_IN(void);
void IIC_Start(void);
void IIC_Stop(void);
u8 IIC_Wait_Ack(void);
void IIC_Ack(void);
void IIC_NAck(void);
void IIC_Send_Byte(u8 txd);
u8 IIC_Read_Byte(unsigned char ack);
//void delay_us(int Time);

#endif

IIc.c

#include "stm32f10x.h"
#include "IIC.h"
#include "System_Delay.h"
void IIC_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_Init( GPIOA, &GPIO_InitStructure);
}
void SDA_OUT(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_Init( GPIOA, &GPIO_InitStructure);
}
void SDA_IN(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_Init( GPIOA, &GPIO_InitStructure);
}
/*关于IIC的底层直接套用就可以*/
 
 
//产生IIC起始信号
void IIC_Start(void)
{
	SDA_OUT();     //sda线输出
	IIC_SDA_H;	  	  
	IIC_SCL_H;
	delay_us(4);
 	IIC_SDA_L;//START:when CLK is high,DATA change form high to low 
	delay_us(4);
	IIC_SCL_L;//钳住I2C总线,准备发送或接收数据 
}	  
//产生IIC停止信号
void IIC_Stop(void)
{
	SDA_OUT();//sda线输出
	IIC_SCL_L;
	IIC_SDA_L;//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	IIC_SCL_H; 
	IIC_SDA_H;//发送I2C总线结束信号
	delay_us(4);							   	
}
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();      //SDA设置为输入  
	IIC_SDA_H;delay_us(1);	   
	IIC_SCL_H;delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL_L;//时钟输出0 	   
	return 0;  
} 
//产生ACK应答
void IIC_Ack(void)
{
	IIC_SCL_L;
	SDA_OUT();
	IIC_SDA_L;
	delay_us(2);
	IIC_SCL_H;
	delay_us(2);
	IIC_SCL_L;
}
//不产生ACK应答		    
void IIC_NAck(void)
{
	IIC_SCL_L;
	SDA_OUT();
	IIC_SDA_H;
	delay_us(2);
	IIC_SCL_H;
	delay_us(2);
	IIC_SCL_L;
}					 				     
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答			  
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT(); 	    
    IIC_SCL_L;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        if((txd&0x80)>>7)
		{
		    IIC_SDA_H;
		}
		else
		{
		    IIC_SDA_L;
		}
        txd<<=1; 	  
		delay_us(2);   //对TEA5767这三个延时都是必须的
		IIC_SCL_H;
		delay_us(2); 
		IIC_SCL_L;	
		delay_us(2);
    }	 
} 	    
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        IIC_SCL_L; 
        delay_us(2);
		IIC_SCL_H;
        receive<<=1;
        if(READ_SDA)receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}
//void delay_us(int Time)
//{
//	int i=0;
//	while(Time--)
//	{
//		i=8;
//		while(i--);
//	}
//	return;
//}

10、SPI

10.1 简介 

同步:收发双方需要时钟同步

异步:收发双发不需要时钟同步,利用比特率,即高低电平的持续时间,来判断发送的是0还是1

SCLK:对应SCL

MOSI:主机写

MISO::主机读

CS:寻址  使能谁就选中谁通信

10.2 寻址方式

 IIC是通过发送从机地址进行寻址的

10.3 通信过程

 

和IIC一样先发高位再发低位,但没有应答机制,也没有起始信号和终止信号

IIC是在SCL为低电平时,主机写入数据,SCL为高电平时,从机读取数据

SPI是在SCLK上升沿或下降沿发送数据,在紧接着的下降沿或上升沿读取数据

10.4 工作模式

 

空闲时IIC的SCL和SDA都是高电平

根据不同的极性和相位SPI共有4钟工作模式 

1、

下降沿写,上升沿读 

2、

上升沿发,下降沿读 

 3、

 上升沿写,下降沿读

 4、

 下降沿写,上升沿读

 

10.5 与IIC异同

 

10.6 STM32的SPI

#include "stm32f10x.h"                  // Device header
//SPI2初始化
void SPI2_Init()
{
	SPI_InitTypeDef SPI_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	//GPIO初始化
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; //PB14  MISO
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15; //PB13 SCLK PB15 MOSI
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //PB12 CS
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	//SPI2初始化
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线输入输出全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主机模式
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8位数据
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //空闲时SCK的状态
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//时钟相位
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//由软件控制CS 一个从设备可以这样自动化控制,多个时就要切换为手动操作
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//分频系数越大,速度越慢,通信越稳定
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前
	SPI_InitStructure.SPI_CRCPolynomial = 7; //不使用CRC校验
	
	SPI_Init(SPI2,&SPI_InitStructure);
	SPI_Cmd(SPI2,ENABLE);
}
//SPI2数据收发程序
uint8_t SPI2_SendByte(uint8_t Byte)
{
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE) == RESET); //如果发送寄存器为空
	SPI_I2S_SendData(SPI2,Byte);
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE) == RESET); //如果接收寄存器为空
	return SPI_I2S_ReceiveData(SPI2);
}

11、OLED

11.1 简介

0.96寸的是:128X64个点阵

写1亮  写0灭

 11.2 初始化

指定地址写 

11.3 点阵

 

一个字节的低位在上高位在下

64行分为0-7PAGE 

11.4 寻址模式

 页寻址模式

水平寻址模式

 垂直寻址模式

 11.5 写入数据

 确定寻址模式

 

确定第几页 

 

确定列 

最后写入数据

OLED.c 

#include "stm32f10x.h"
#include "OLED_Font.h"

/*引脚配置*/
#define OLED_W_SCL(x)		GPIO_WriteBit(GPIOA, GPIO_Pin_11, (BitAction)(x))
#define OLED_W_SDA(x)		GPIO_WriteBit(GPIOA, GPIO_Pin_12, (BitAction)(x))

/*引脚初始化*/
void OLED_I2C_Init(void)
{
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	OLED_W_SCL(1);
	OLED_W_SDA(1);
}

/**
  * @brief  I2C开始
  * @param  无
  * @retval 无
  */
void OLED_I2C_Start(void)
{
	OLED_W_SDA(1);
	OLED_W_SCL(1);
	OLED_W_SDA(0);
	OLED_W_SCL(0);
}

/**
  * @brief  I2C停止
  * @param  无
  * @retval 无
  */
void OLED_I2C_Stop(void)
{
	OLED_W_SDA(0);
	OLED_W_SCL(1);
	OLED_W_SDA(1);
}

/**
  * @brief  I2C发送一个字节
  * @param  Byte 要发送的一个字节
  * @retval 无
  */
void OLED_I2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i++)
	{
		OLED_W_SDA(Byte & (0x80 >> i));
		OLED_W_SCL(1);
		OLED_W_SCL(0);
	}
	OLED_W_SCL(1);	//额外的一个时钟,不处理应答信号
	OLED_W_SCL(0);
}

/**
  * @brief  OLED写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void OLED_WriteCommand(uint8_t Command)
{
	OLED_I2C_Start();
	OLED_I2C_SendByte(0x78);		//从机地址
	OLED_I2C_SendByte(0x00);		//写命令
	OLED_I2C_SendByte(Command); 
	OLED_I2C_Stop();
}

/**
  * @brief  OLED写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void OLED_WriteData(uint8_t Data)
{
	OLED_I2C_Start();
	OLED_I2C_SendByte(0x78);		//从机地址
	OLED_I2C_SendByte(0x40);		//写数据
	OLED_I2C_SendByte(Data);
	OLED_I2C_Stop();
}

/**
  * @brief  OLED设置光标位置
  * @param  Y 以左上角为原点,向下方向的坐标,范围:0~7
  * @param  X 以左上角为原点,向右方向的坐标,范围:0~127
  * @retval 无
  */
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
	OLED_WriteCommand(0xB0 | Y);					//设置Y位置
	OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4));	//设置X位置低4位
	OLED_WriteCommand(0x00 | (X & 0x0F));			//设置X位置高4位
}

/**
  * @brief  OLED清屏
  * @param  无
  * @retval 无
  */
void OLED_Clear(void)
{  
	uint8_t i, j;
	for (j = 0; j < 8; j++)
	{
		OLED_SetCursor(j, 0);
		for(i = 0; i < 128; i++)
		{
			OLED_WriteData(0x00);
		}
	}
}

/**
  * @brief  OLED显示一个字符
  * @param  Line 行位置,范围:1~4
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的一个字符,范围:ASCII可见字符
  * @retval 无
  */
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{      	
	uint8_t i;
	OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);		//设置光标位置在上半部分
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i]);			//显示上半部分内容
	}
	OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);	//设置光标位置在下半部分
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]);		//显示下半部分内容
	}
}

/**
  * @brief  OLED显示字符串
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串,范围:ASCII可见字符
  * @retval 无
  */
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i++)
	{
		OLED_ShowChar(Line, Column + i, String[i]);
	}
}

/**
  * @brief  OLED次方函数
  * @retval 返回值等于X的Y次方
  */
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y--)
	{
		Result *= X;
	}
	return Result;
}

/**
  * @brief  OLED显示数字(十进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~4294967295
  * @param  Length 要显示数字的长度,范围:1~10
  * @retval 无
  */
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

/**
  * @brief  OLED显示数字(十进制,带符号数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-2147483648~2147483647
  * @param  Length 要显示数字的长度,范围:1~10
  * @retval 无
  */
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
	uint8_t i;
	uint32_t Number1;
	if (Number >= 0)
	{
		OLED_ShowChar(Line, Column, '+');
		Number1 = Number;
	}
	else
	{
		OLED_ShowChar(Line, Column, '-');
		Number1 = -Number;
	}
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

/**
  * @brief  OLED显示数字(十六进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFFFFFF
  * @param  Length 要显示数字的长度,范围:1~8
  * @retval 无
  */
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i, SingleNumber;
	for (i = 0; i < Length; i++)							
	{
		SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
		if (SingleNumber < 10)
		{
			OLED_ShowChar(Line, Column + i, SingleNumber + '0');
		}
		else
		{
			OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
		}
	}
}

/**
  * @brief  OLED显示数字(二进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
	}
}

/**
  * @brief  OLED初始化
  * @param  无
  * @retval 无
  */
void OLED_Init(void)
{
	uint32_t i, j;
	
	for (i = 0; i < 1000; i++)			//上电延时
	{
		for (j = 0; j < 1000; j++);
	}
	
	OLED_I2C_Init();			//端口初始化
	
	OLED_WriteCommand(0xAE);	//关闭显示
	
	OLED_WriteCommand(0xD5);	//设置显示时钟分频比/振荡器频率
	OLED_WriteCommand(0x80);
	
	OLED_WriteCommand(0xA8);	//设置多路复用率
	OLED_WriteCommand(0x3F);
	
	OLED_WriteCommand(0xD3);	//设置显示偏移
	OLED_WriteCommand(0x00);
	
	OLED_WriteCommand(0x40);	//设置显示开始行
	
	OLED_WriteCommand(0xA1);	//设置左右方向,0xA1正常 0xA0左右反置
	
	OLED_WriteCommand(0xC8);	//设置上下方向,0xC8正常 0xC0上下反置

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

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

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

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

	OLED_WriteCommand(0xA6);	//设置正常/倒转显示

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

	OLED_WriteCommand(0xAF);	//开启显示
		
	OLED_Clear();				//OLED清屏
}

 OLED.h

#ifndef __OLED_H
#define __OLED_H

void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);

#endif

 OLED_Font.h

#ifndef __OLED_FONT_H
#define __OLED_FONT_H

/*OLED字模库,宽8像素,高16像素*/
const uint8_t OLED_F8x16[][16]=
{
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//  0
	
	0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00,//! 1
	
	0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//" 2
	
	0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,
	0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00,//# 3
	
	0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,
	0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00,//$ 4
	
	0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,
	0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00,//% 5
	
	0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,
	0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10,//& 6
	
	0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//' 7
	
	0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,
	0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00,//( 8
	
	0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,
	0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00,//) 9
	
	0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,
	0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00,//* 10
	
	0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,
	0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00,//+ 11
	
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00,//, 12
	
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,//- 13
	
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00,//. 14
	
	0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,
	0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00,/// 15
	
	0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,
	0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,//0 16
	
	0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,
	0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//1 17
	
	0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,
	0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,//2 18
	
	0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,
	0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,//3 19
	
	0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,
	0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,//4 20
	
	0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,
	0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,//5 21
	
	0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,
	0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,//6 22
	
	0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,
	0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,//7 23
	
	0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,
	0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,//8 24
	
	0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,
	0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,//9 25
	
	0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,
	0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,//: 26
	
	0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,
	0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00,//; 27
	
	0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,
	0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00,//< 28
	
	0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,
	0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00,//= 29
	
	0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,
	0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00,//> 30
	
	0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,
	0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00,//? 31
	
	0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,
	0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00,//@ 32
	
	0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,
	0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,//A 33
	
	0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,
	0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00,//B 34
	
	0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,
	0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00,//C 35
	
	0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,
	0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00,//D 36
	
	0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,
	0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00,//E 37
	
	0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,
	0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00,//F 38
	
	0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,
	0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00,//G 39
	
	0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,
	0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20,//H 40
	
	0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,
	0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//I 41
	
	0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,
	0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00,//J 42
	
	0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,
	0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00,//K 43
	
	0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,
	0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00,//L 44
	
	0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,
	0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00,//M 45
	
	0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,
	0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00,//N 46
	
	0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,
	0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00,//O 47
	
	0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,
	0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00,//P 48
	
	0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,
	0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00,//Q 49
	
	0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,
	0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20,//R 50
	
	0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,
	0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00,//S 51
	
	0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,
	0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//T 52
	
	0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,
	0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//U 53
	
	0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,
	0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00,//V 54
	
	0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,
	0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00,//W 55
	
	0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,
	0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20,//X 56
	
	0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,
	0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//Y 57
	
	0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,
	0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00,//Z 58
	
	0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,
	0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00,//[ 59
	
	0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00,//\ 60
	
	0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,
	0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00,//] 61
	
	0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//^ 62
	
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,//_ 63
	
	0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//` 64
	
	0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
	0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20,//a 65
	
	0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,
	0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00,//b 66
	
	0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,
	0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00,//c 67
	
	0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,
	0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20,//d 68
	
	0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
	0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00,//e 69
	
	0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,
	0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//f 70
	
	0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,
	0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00,//g 71
	
	0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,
	0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//h 72
	
	0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,
	0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//i 73
	
	0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,
	0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,//j 74
	
	0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,
	0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00,//k 75
	
	0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,
	0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//l 76
	
	0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,
	0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F,//m 77
	
	0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,
	0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//n 78
	
	0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
	0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//o 79
	
	0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,
	0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00,//p 80
	
	0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,
	0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80,//q 81
	
	0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,
	0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00,//r 82
	
	0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,
	0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00,//s 83
	
	0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,
	0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00,//t 84
	
	0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,
	0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20,//u 85
	
	0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,
	0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00,//v 86
	
	0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,
	0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00,//w 87
	
	0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,
	0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00,//x 88
	
	0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,
	0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00,//y 89
	
	0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,
	0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00,//z 90
	
	0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,
	0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40,//{ 91
	
	0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,//| 92
	
	0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,
	0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00,//} 93
	
	0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//~ 94
};

#endif

十二、时钟

12.1 框图

五个蓝色的框图是时钟源

HSI:内部时钟源,RC震荡产生,不太稳定,约8MHZ 

LSI:低速的内部时钟,约40KHZ

HSE:外部时钟,需要接一个晶振,一般接8MHZ

LSE:外部时钟,一般接低速晶振,32.768KHZ

PLL:锁相环,倍频,2~16倍 但最高是72MHZ

MCO:可以输出内部时钟 PA8

AHB、APB、APB2也可以分频

12.2 滴答定时器

内核级别的定时器

 

 

 
十三、 RTC时钟

13.1 简介

 13.2 框图

RTC CLK一般用外部低频率时钟:32.768KHZ

经分频后得到的TR_CLK,一般是1HZ,即是1秒

RTC_Second:秒中断,1秒一个中断

RTC_Overflow:32位,设置初值,每一个TR_CLK就加1,溢出产生中断

RTC_Alarm:设置的闹钟中断

当前时间计算方法:1970+RTC_CNT里面的秒数

 13.3 备份寄存器

13.4 配置步骤

13.5 代码

RTC.h

#ifndef __RTC_H
#define __RTC_H	    
 
//时间结构体
typedef struct 
{
	vu8 hour;
	vu8 min;
	vu8 sec;			
	//公历日月年周
	vu16 w_year;
	vu8  w_month;
	vu8  w_date;
	vu8  week;		 
}_calendar_obj;		
			 
extern _calendar_obj calendar;	//日历结构体
extern u8 const mon_table[12];	//月份日期数据表
 
void Disp_Time(u8 x,u8 y,u8 size);//在制定位置开始显示时间
void Disp_Week(u8 x,u8 y,u8 size,u8 lang);//在指定位置显示星期
u8 RTC_Init(void);        //初始化RTC,返回0,失败;1,成功;
u8 Is_Leap_Year(u16 year);//平年,闰年判断
u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);
u8 RTC_Get(void);         //更新时间   
u8 RTC_Get_Week(u16 year,u8 month,u8 day);
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);//设置时间	
		 
#endif

RTC.c

#include "stm32f10x.h"                  // Device header  
#include "RTC.h"
#include "System_Delay.h"
#include "UART.h"
_calendar_obj calendar;//时钟结构体 
 
static void RTC_NVIC_Config(void)
{	
  NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;		//RTC全局中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	//先占优先级1位,从优先级3位
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;	//先占优先级0位,从优先级4位
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;		//使能该通道中断
	NVIC_Init(&NVIC_InitStructure);		//根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}
 
//实时时钟配置
//初始化RTC时钟,同时检测时钟是否工作正常
//BKP->DR1用于保存是否第一次配置的设置
//返回0:正常
//其他:错误代码
 
u8 RTC_Init(void)
{
	//检查是不是第一次配置时钟
	u8 temp=0;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟   
	PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器访问  
	if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050)		//从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
		{	 			
		BKP_DeInit();	//复位备份区域 	
		RCC_LSEConfig(RCC_LSE_ON);	//设置外部低速晶振(LSE),使用外设低速晶振
		while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250)	//检查指定的RCC标志位设置与否,等待低速晶振就绪
			{
			temp++;
			delay_ms(10);
			}
		if(temp>=250)return 1;//初始化时钟失败,晶振有问题	    
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);		//设置RTC时钟(RTCCLK),选择LSE作为RTC时钟    
		RCC_RTCCLKCmd(ENABLE);	//使能RTC时钟  
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
		RTC_WaitForSynchro();		//等待RTC寄存器同步  
		RTC_ITConfig(RTC_IT_SEC, ENABLE);		//使能RTC秒中断
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
		RTC_EnterConfigMode();/// 允许配置	
		RTC_SetPrescaler(32767); //设置RTC预分频的值
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
		RTC_Set(2015,1,14,17,42,55);  //设置时间	
		RTC_ExitConfigMode(); //退出配置模式  
		BKP_WriteBackupRegister(BKP_DR1, 0X5050);	//向指定的后备寄存器中写入用户程序数据
		}
	else//系统继续计时
		{
 
		RTC_WaitForSynchro();	//等待最近一次对RTC寄存器的写操作完成
		RTC_ITConfig(RTC_IT_SEC, ENABLE);	//使能RTC秒中断
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
		}
	RTC_NVIC_Config();//RCT中断分组设置		    				     
	RTC_Get();//更新时间	
	return 0; //ok
 
}		 				    
//RTC时钟中断
//每秒触发一次  
//extern u16 tcnt; 
void RTC_IRQHandler(void)
{		 
	if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒钟中断
	{							
		RTC_Get();//更新时间   
 	}
	if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断
	{
		RTC_ClearITPendingBit(RTC_IT_ALR);		//清闹钟中断	  	
	  RTC_Get();				//更新时间   
  	printf("Alarm Time:%d-%d-%d %d:%d:%d\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);//输出闹铃时间	
		
  	} 				  								 
	RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);		//清闹钟中断
	RTC_WaitForLastTask();	  	    						 	   	 
}
//判断是否是闰年函数
//月份   1  2  3  4  5  6  7  8  9  10 11 12
//闰年   31 29 31 30 31 30 31 31 30 31 30 31
//非闰年 31 28 31 30 31 30 31 31 30 31 30 31
//输入:年份
//输出:该年份是不是闰年.1,是.0,不是
u8 Is_Leap_Year(u16 year)
{			  
	if(year%4==0) //必须能被4整除
	{ 
		if(year%100==0) 
		{ 
			if(year%400==0)return 1;//如果以00结尾,还要能被400整除 	   
			else return 0;   
		}else return 1;   
	}else return 0;	
}	 			   
//设置时钟
//把输入的时钟转换为秒钟
//以1970年1月1日为基准
//1970~2099年为合法年份
//返回值:0,成功;其他:错误代码.
//月份数据表											 
u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表	  
//平年的月份日期表
const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{
	u16 t;
	u32 seccount=0;
	if(syear<1970||syear>2099)return 1;	   
	for(t=1970;t<syear;t++)	//把所有年份的秒钟相加
	{
		if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
		else seccount+=31536000;			  //平年的秒钟数
	}
	smon-=1;
	for(t=0;t<smon;t++)	   //把前面月份的秒钟数相加
	{
		seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
		if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数	   
	}
	seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加 
	seccount+=(u32)hour*3600;//小时秒钟数
    seccount+=(u32)min*60;	 //分钟秒钟数
	seccount+=sec;//最后的秒钟加上去
 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟  
	PWR_BackupAccessCmd(ENABLE);	//使能RTC和后备寄存器访问 
	RTC_SetCounter(seccount);	//设置RTC计数器的值
 
	RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成  	
	return 0;	    
}
 
//初始化闹钟		  
//以1970年1月1日为基准
//1970~2099年为合法年份
//syear,smon,sday,hour,min,sec:闹钟的年月日时分秒   
//返回值:0,成功;其他:错误代码.
u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{
	u16 t;
	u32 seccount=0;
	if(syear<1970||syear>2099)return 1;	   
	for(t=1970;t<syear;t++)	//把所有年份的秒钟相加
	{
		if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
		else seccount+=31536000;			  //平年的秒钟数
	}
	smon-=1;
	for(t=0;t<smon;t++)	   //把前面月份的秒钟数相加
	{
		seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
		if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数	   
	}
	seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加 
	seccount+=(u32)hour*3600;//小时秒钟数
    seccount+=(u32)min*60;	 //分钟秒钟数
	seccount+=sec;//最后的秒钟加上去 			    
	//设置时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟   
	PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器访问  
	//上面三步是必须的!
	
	RTC_SetAlarm(seccount);
 
	RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成  	
	
	return 0;	    
}
 
 
//得到当前的时间
//返回值:0,成功;其他:错误代码.
u8 RTC_Get(void)
{
	static u16 daycnt=0;
	u32 timecount=0; 
	u32 temp=0;
	u16 temp1=0;	  
    timecount=RTC_GetCounter();	 
 	temp=timecount/86400;   //得到天数(秒钟数对应的)
	if(daycnt!=temp)//超过一天了
	{	  
		daycnt=temp;
		temp1=1970;	//从1970年开始
		while(temp>=365)
		{				 
			if(Is_Leap_Year(temp1))//是闰年
			{
				if(temp>=366)temp-=366;//闰年的秒钟数
				else {temp1++;break;}  
			}
			else temp-=365;	  //平年 
			temp1++;  
		}   
		calendar.w_year=temp1;//得到年份
		temp1=0;
		while(temp>=28)//超过了一个月
		{
			if(Is_Leap_Year(calendar.w_year)&&temp1==1)//当年是不是闰年/2月份
			{
				if(temp>=29)temp-=29;//闰年的秒钟数
				else break; 
			}
			else 
			{
				if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
				else break;
			}
			temp1++;  
		}
		calendar.w_month=temp1+1;	//得到月份
		calendar.w_date=temp+1;  	//得到日期 
	}
	temp=timecount%86400;     		//得到秒钟数   	   
	calendar.hour=temp/3600;     	//小时
	calendar.min=(temp%3600)/60; 	//分钟	
	calendar.sec=(temp%3600)%60; 	//秒钟
	calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);//获取星期  
printf("Current Time:%d-%d-%d %d:%d:%d\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);//输出闹铃时间		
	return 0;
}	 
//获得现在是星期几
//功能描述:输入公历日期得到星期(只允许1901-2099年)
//输入参数:公历年月日 
//返回值:星期号																						 
u8 RTC_Get_Week(u16 year,u8 month,u8 day)
{	
	u16 temp2;
	u8 yearH,yearL;
	
	yearH=year/100;	yearL=year%100; 
	// 如果为21世纪,年份数加100  
	if (yearH>19)yearL+=100;
	// 所过闰年数只算1900年之后的  
	temp2=yearL+yearL/4;
	temp2=temp2%7; 
	temp2=temp2+day+table_week[month-1];
	if (yearL%4==0&&month<3)temp2--;
	return(temp2%7);
}			  

十四、DHT11

 14.1 数据格式

14.2 复位信号

 

14.3 DHT11响应

 

14.4 数据传输

 

14.5 代码

 dht11.c

#include "stm32f10x.h"                  // Device header
#include  "dht11.h"
#include  "Delay.h"

//对于stm32来说,是输出
void DH11_GPIO_Init_OUT(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP; //推挽输出
	GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOB, &GPIO_InitStructure);

}

//对于stm32来说,是输入
void DH11_GPIO_Init_IN(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING; //浮空输入
	GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOB, &GPIO_InitStructure);

}



//主机发送开始信号
void DHT11_Start(void)
{
	DH11_GPIO_Init_OUT(); //输出模式
	
	dht11_high; //先拉高
	Delay_us(30);
	
	dht11_low; //拉低电平至少18us
	Delay_ms(20);
	
	dht11_high; //拉高电平20~40us
	Delay_us(30);
	
	DH11_GPIO_Init_IN(); //输入模式
}


//获取一个字节
char DHT11_Rec_Byte(void)
{
	unsigned char i = 0;
	unsigned char data;
	
	for(i=0;i<8;i++) //1个数据就是1个字节byte,1个字节byte有8位bit
	{
		while( Read_Data == 0); //从1bit开始,低电平变高电平,等待低电平结束
		Delay_us(30); //延迟30us是为了区别数据0和数据1,0只有26~28us
		
		data <<= 1; //左移
		
		if( Read_Data == 1 ) //如果过了30us还是高电平的话就是数据1
		{
			data |= 1; //数据+1
		}
		
		while( Read_Data == 1 ); //高电平变低电平,等待高电平结束
	}
	
	return data;
}

//获取数据

void DHT11_REC_Data(uint16_t * rec_data)
{
	unsigned int R_H,R_L,T_H,T_L;
	unsigned char RH,RL,TH,TL,CHECK;
	
	DHT11_Start(); //主机发送信号
	dht11_high; //拉高电平
	
	if( Read_Data == 0 ) //判断DHT11是否响应
	{
		while( Read_Data == 0); //低电平变高电平,等待低电平结束
		while( Read_Data == 1); //高电平变低电平,等待高电平结束
		
		R_H = DHT11_Rec_Byte();
		R_L = DHT11_Rec_Byte();
		T_H = DHT11_Rec_Byte();
		T_L = DHT11_Rec_Byte();
		CHECK = DHT11_Rec_Byte(); //接收5个数据
		
		dht11_low; //当最后一bit数据传送完毕后,DHT11拉低总线 50us
		Delay_us(55); //这里延时55us
		dht11_high; //随后总线由上拉电阻拉高进入空闲状态。
		
		if(R_H + R_L + T_H + T_L == CHECK) //和检验位对比,判断校验接收到的数据是否正确
		{
			RH = R_H;
			RL = R_L;
			TH = T_H;
			TL = T_L;
		}
	}
	rec_data[0] = RH;
	rec_data[1] = RL;
	rec_data[2] = TH;
	rec_data[3] = TL;
}

dht11.h

#ifndef __DHT11_H
#define __DHT11_H
#include "stm32f10x.h"                  // Device header



#define dht11_high GPIO_SetBits(GPIOB, GPIO_Pin_12)
#define dht11_low GPIO_ResetBits(GPIOB, GPIO_Pin_12)
#define Read_Data GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12)

void DHT11_GPIO_Init_OUT(void);
void DHT11_GPIO_Init_IN(void);
void DHT11_Start(void);
unsigned char DHT11_REC_Byte(void);
void DHT11_REC_Data(uint16_t * rec_data);

#endif


十五、ADC

15.1 简介

分辨率:12位 0~2的12次方-1

频率:1us 1MHz

基准电压:1.2V  不随外接电压变换

18路ADC:16路GPIO口+1个内部温度传感器(测量CPU温度)+ 1个内部基准电压

stm32f103c8t6:2个ADC 10个外部输入通道

规则组:一次性可以测16路,但对于寄存器只能存一组数据,然后就会被覆盖,配合DMA可以 在数据未覆盖前读取16路数据

注入组:可以同时读取4路

定时器触发ADC转换

ADC预分频器:输入72MHZ,但输出最大14MHZ 

15.2 ADC基本结构图

 

1、选择输入通道

2、选择规则组还是注入组,规则组16路,但只有一个数据寄存器

3、启动ADC,硬件启动需要配合定时器,也可选择软件启动 

4、转换完成会状态寄存器相应的标志位会置位,也可以申请中断

15.3 工作模式

1、单次转换:只转换一次就停止

2、连续转换:启动之后一直转换

3、非扫描模式:每次只转换一个通道

4、扫描模式:转换所有通道

共有4中组合

15.4 触发控制

 

 12ADC:结果分为右对齐和左对齐,一般用右对齐,这样读取的寄存器的值就是转换结果

15.5 单通道单独转换非扫描模式代码

AD.h

#ifndef __AD_H
#define __AD_H
void AD_Init();
uint16_t AD_Get_Val();
#endif

AD.c

#include "stm32f10x.h"                  // Device header
void AD_Init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 72/6=12MHZ
	
	GPIO_InitTypeDef GPIO_InitTypeDef_Define;
	GPIO_InitTypeDef_Define.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitTypeDef_Define.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitTypeDef_Define.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitTypeDef_Define);
	
	ADC_RegularChannelConfig(ADC1,ADC_Channel_8,1,ADC_SampleTime_71Cycles5);// 1是放在菜单的序列号1-16 ADC_SampleTime_是采样时间
	
	ADC_InitTypeDef ADC_InitTypeDef_def;
	ADC_InitTypeDef_def.ADC_ContinuousConvMode =DISABLE;//连续转换
	ADC_InitTypeDef_def.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
	ADC_InitTypeDef_def.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//软件触发控制源 
	ADC_InitTypeDef_def.ADC_Mode = ADC_Mode_Independent;  //ADC是独立工作,还是ADC双工作模式
	ADC_InitTypeDef_def.ADC_NbrOfChannel = 1;//几个通道
	ADC_InitTypeDef_def.ADC_ScanConvMode = DISABLE;//非扫描工作模式
	ADC_Init(ADC1,&ADC_InitTypeDef_def);
	//启动ADC
	ADC_Cmd(ADC1,ENABLE);
	//ADC校准
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1));
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1));
}
uint16_t AD_Get_Val()
{
	//软件启动ADC
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);
	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);
	return ADC_GetConversionValue(ADC1);
}

16、RS232、RS485

16.1 简介

原来是25根线,现在变为9根线,但一般只用其中的3根线,TXD,RXD,GND

16.2 连接 

16.3  RS485

 

通信距离:1500m

抗干扰能力增强

可以实现一对多通信 

16.4 传输

16.5 电平转换

 

17、DMA

 17.1 简介

存储器到存储器使用软件触发

外设到存储器使用硬件触发 

17.2 存储器映射

 

 17.3 基本结构

数据宽度:字节 u8  半字 u16  字u32

传输计数器:传输次数 向下计数

自动重载器:是否重载  不重载DMA传输一次完成就结束

软件触发:尽快把传输计数器清0 如果开启了自动重载,将陷入无限循环

硬件触发:必须指定对应的通道

17.4 代码

 MyDMA.h

#ifndef __MYDMA_H
#define __MYDMA_H
void MyDMA_Init(uint32_t DMA_Base_Addr,uint32_t DMA_Data_Addr,uint16_t size);
void MyDMA_Transfer(uint16_t size);
#endif

 MyDMA.c

#include "stm32f10x.h"                  // Device header
void MyDMA_Init(uint32_t DMA_Base_Addr,uint32_t DMA_Data_Addr,uint16_t size)
{
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	DMA_InitTypeDef  DMA_InitTypeDefDefine;
	DMA_InitTypeDefDefine.DMA_PeripheralBaseAddr = DMA_Base_Addr; //起始地址
	DMA_InitTypeDefDefine.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据宽度
	DMA_InitTypeDefDefine.DMA_PeripheralInc = DMA_PeripheralInc_Enable;      //是否自增
	DMA_InitTypeDefDefine.DMA_MemoryBaseAddr =DMA_Data_Addr ;
	DMA_InitTypeDefDefine.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_InitTypeDefDefine.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitTypeDefDefine.DMA_DIR = DMA_DIR_PeripheralSRC;//传输方向 外设到存储器
	DMA_InitTypeDefDefine.DMA_BufferSize = size;//传输次数
	DMA_InitTypeDefDefine.DMA_Mode = DMA_Mode_Normal; //不重载
	DMA_InitTypeDefDefine.DMA_M2M = DMA_M2M_Enable;//软件触发
	DMA_InitTypeDefDefine.DMA_Priority = DMA_Priority_High;
	DMA_Init(DMA1_Channel1,&DMA_InitTypeDefDefine);
	
	DMA_Cmd(DMA1_Channel1,DISABLE);
	
}
void MyDMA_Transfer(uint16_t size)
{
	DMA_Cmd(DMA1_Channel1,DISABLE);
	DMA_SetCurrDataCounter(DMA1_Channel1,size);//传输次数
	DMA_Cmd(DMA1_Channel1,ENABLE);
	while(DMA_GetFlagStatus(DMA1_FLAG_TC1)== RESET);
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值