一、工程搭建
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);
}