目录
1、较精确的软件延时函数
2、GPIO的8种模式
3、嘀嗒定时器
4、按键软件消抖的写法
5、GPIO_CRL &=~(0x0F<<(4*0))的解析
6、灯闪程序的问题
7、按键硬件防抖的电路设计
8、基本定时器,通用定时器和高级定时器
9、三极管开关电路
10、EEPROM电路
11、OneNet平台HTTP数据报格式
12、USART中使用printf打印
13、IIC程序及EEPROM电路图
1、较精确的软件延时函数:
void delay_us ( unsigned int us ){ unsigned char n; while( us-- ) for( n=0;n<9; n++ );}void delay_ms ( unsigned int ms ){ while( ms-- ) delay_us(1000);}
2、GPIO的8种模式:
GPIO_Mode_AIN //模拟输入GPIO_Mode_IN_FLOATING //浮空输入GPIO_Mode_IPD //下拉输入GPIO_Mode_IPU //上拉输入GPIO_Mode_Out_OD //开漏输出GPIO_Mode_Out_PP //推挽输出GPIO_Mode_AF_OD //开漏复用输出 GPIO_Mode_AF_PP //推挽复用输出
3、嘀嗒定时器:
delay.c内容:
#include "delay.h"void delay_us ( unsigned int us) //微秒级延时函数,嘀嗒定时器为24位向下计数器{ SysTick -> VAL = 0; //设置计数器初值为0,一共32位,24位可用 SysTick -> LOAD = 9*us; //每计9个数才是1us,这里和TIM定时器计数有区别 SysTick -> CTRL = 0x01; //使能计数器,第0位为使能位,置1使能,置0失能 while((SysTick ->CTRL) != 0x10001); //向下产生溢出后,产生中断标志,及判断16位是否置1 SysTick -> CTRL =0; //产生溢出后,产生中断标志的第16位被置1,此时再将第16位置0,以便下次计数 SysTick -> VAL =0; //设置计数器初值为0,一共32位,24位可用}void delay_ms ( unsigned int ms) //毫秒级延时函数,嘀嗒定时器为24位向下计数器{ SysTick -> VAL = 0;//设置计数器初值为0,一共32位,24位可用 SysTick -> LOAD = 9000*ms;//这里计9000个数才是1ms,这里和TIM定时器计数有区别 SysTick -> CTRL = 0x01;//使能计数器,第0位为使能位,置1使能,置0失能 while((SysTick ->CTRL) != 0x10001);//向下产生溢出后,产生中断标志,及判断16位是否置1 SysTick -> CTRL =0;//产生溢出后,产生中断标志的第16位被置1,此时再将第16位置0,以便下次计数 SysTick -> VAL =0;//设置计数器初值为0,一共32位,24位可用}
delay.h内容:
#ifndef __DELAY_H#define __DELAY_H#include "stm32f10x.h"void delay_us(unsigned int us);void delay_ms(unsigned int ms);#endif
4、按键软件消抖的写法:
if(GPIO_ReadInputDataBit(GPIOx, GPIO_Pin_x)==0){ delay_ms(50); if(GPIO_ReadInputDataBit(GPIOx, GPIO_Pin_x)==0){ //此处为按键按下所要实现的功能 } }
5、GPIO_CRL &=~(0x0F<<(4*0))的解析:
“ ~(0x0F<<(4*0)) ” 的值为0xF0;
后写为:GPIO_CRL= GPIO_CRL & 0xF0;
假设GPIO_CRL的值为0100 1100
那么这条程序的作用:GPIO_CRL的值的低4位被置0,其他位不变。
6、灯闪程序的问题:
GPIO_SetBits(GPIOA, GPIO_Pin_8);SysTick_Delay_Ms(500); //系统精准的延时函数GPIO_ResetBits(GPIOA, GPIO_Pin_8);SysTick_Delay_Ms(500);
要想实现灯闪,必须在灯亮及灭后加延时函数,否则无法实现目的,即考虑视觉停留问题!
7、按键硬件防抖的电路设计:
注:此处参照野火板子硬件防抖设计
8、基本定时器,通用定时器和高级定时器:
1. 时钟源
定时器时钟 TIMxCLK,即内部时钟 CK_INT,经 APB1 预分频器后分频提供,如果APB1 预分频系数等于 1,则频率不变,否则频率乘以 2,库函数中 APB1 预分频的系数是 2,即 PCLK1=36M,所以定时器时钟 TIMxCLK=36*2=72M 。2. 计数器时钟定时器时钟经过 PSC 预分频器之后,即 CK_CNT,用来驱动计数器计数。PSC 是一个16 位的预分频器,可以对定时器时钟 TIMxCLK 进行 1~65536 之间的任何一个数进行分频。具体计算方式为:CK_CNT=TIMxCLK/(PSC+1)。3. 计数器计数器 CNT 是一个 16 位的计数器,只能往上计数,最大计数值为 65535。当计数达到自动重装载寄存器的时候产生更新事件,并清零从头开始计数。4. 自动重装载寄存器自动重装载寄存器 ARR 是一个 16 位的寄存器,这里面装着计数器能计数的最大数值。当计数到这个值的时候,如果使能了中断的话,定时器就产生溢出中断。5. 定时时间的计算
定时器的定时时间等于计数器的中断周期乘以中断的次数。计数器在 CK_CNT 的驱动
下,计一个数的时间则是 CK_CLK 的倒数,等于:1/(TIMxCLK/(PSC+1)),产生一次中断的时间则等于:1/(CK_CLK * ARR)。如果在中断服务程序里面设置一个变量 time,用来记录中断的次数,那么就可以计算出我们需要的定时时间等于: 1/CK_CLK *(ARR+1)*time。例:定时器的时钟为72Mhz(72000000hz),我们让计数器时钟对定时器时钟36000分频(最大65536),那么计数器时钟CK_CNT=TIMxCLK/(PSC+1);
那么 CK_CNT=72000000/(35999+1);
计一个数的时间为:CK_CLK=1/CK_CNT ;
那么计一个数的时间为:1/( 72000000/(35999+1)) 即 (35999+1)/ 72000000 = 0.0005s=500us;
高级定时器的输入捕获解析:
高级控制器功能框图
stm32f0x数据手册
现在我们用PA0端口作为输入捕获的通道:
开启TIM5的时钟、总线APB1的时钟及GPIOA的时钟:
RCC_APB1PeriphClockCmd;
RCC_APB1Periph_TIM5;
RCC_APB2Periph_GPIOA;
通道的使用:
TIM5_CH1 对应的 TIM_Channel_1
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//输入通道和捕获通道的方式为直连
TIM_ClearFlag(TIM5, TIM_FLAG_Update|TIM_IT_CC1);//直连就为TIM_IT_CC1,非直连就为TIM_IT_CC2
TIM_GetCapture1;//获取寄存器通道1的值
TIM_OC1PolarityConfig;//设置 TIM5 通道 1 极性
现在我们改用PA1端口作为输入捕获的通道2:
通道的使用:
TIM5_CH2 对应的 TIM_Channel_2
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//输入通道和捕获通道的方式为直连
TIM_ClearFlag(TIM5, TIM_FLAG_Update|TIM_IT_CC2);//直连就为TIM_IT_CC2,非直连就为TIM_IT_CC1,需要了解直连和非直连的区别
TIM_GetCapture2;//获取寄存器通道2的值
TIM_OC2PolarityConfig;//设置 TIM5 通道 2 极性
9、三极管开关电路
LED1改成5v直流电机的时候需要将R1电阻去掉
10、EEPROM电路
11、OneNet平台HTTP数据报格式
printf ( "POST /devices/设备号/datapoints HTTP/1.1\napi-key:xxxxxxxxxxxxxxxxxxxx\nHost:api.heclouds.com\nContent-Length:59\n\n{\"datastreams\":[{\"id\":\"数据流名称\",\"datapoints\":[{\"value\":xx}]}]}\r\n");
12、USART中使用printf打印
usart.c内容:
#include "usart.h"static void NVIC_Configuration(void){ NVIC_InitTypeDef NVIC_InitStructure; /* 嵌套向量中断控制器组选择 */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); /* 配置USART为中断源 */ NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ; /* 抢断优先级*/ NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; /* 子优先级 */ NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; /* 使能中断 */ NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; /* 初始化配置NVIC */ NVIC_Init(&NVIC_InitStructure);}void USART_Config(void){ GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; // 打开串口GPIO的时钟 DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE); // 打开串口外设的时钟 DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE); // 将USART Tx的GPIO配置为推挽复用模式 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure); // 将USART Rx的GPIO配置为浮空输入模式 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure); // 配置串口的工作参数 // 配置波特率 USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE; // 配置 针数据字长 USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 配置停止位 USART_InitStructure.USART_StopBits = USART_StopBits_1; // 配置校验位 USART_InitStructure.USART_Parity = USART_Parity_No ; // 配置硬件流控制 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 配置工作模式,收发一起 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 完成串口的初始化配置 USART_Init(DEBUG_USARTx, &USART_InitStructure); // 串口中断优先级配置 NVIC_Configuration(); // 使能串口接收中断 USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE); // 使能串口 USART_Cmd(DEBUG_USARTx, ENABLE); }/* 发送一个字节 */void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data)//用法:Usart_SendByte(USART1,5);{ USART_SendData(pUSARTx, data); while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET );}/* 发送两个字节的数据 */void Usart_SendHalfWord(USART_TypeDef* pUSARTx, uint16_t data){ uint8_t temp_h,temp_l; temp_h = (data&0xff00) >> 8 ; temp_l = data&0xff; USART_SendData(pUSARTx, temp_h); while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET ); USART_SendData(pUSARTx, temp_l); while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET );}/* 发送8位数据的数组 */void Usart_SendArray(USART_TypeDef* pUSARTx, uint8_t *array,uint8_t num){ uint8_t i; for( i=0; i
usart.h内容:
#ifndef __BSP_USART_H#define __BSP_USART_H#include "stm32f10x.h"#include#define DEBUG_USART1 1#define DEBUG_USART2 0#define DEBUG_USART3 0#define DEBUG_USART4 0#define DEBUG_USART5 0#if DEBUG_USART1// 串口1-USART1#define DEBUG_USARTx USART1#define DEBUG_USART_CLK RCC_APB2Periph_USART1#define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd#define DEBUG_USART_BAUDRATE 115200// USART GPIO 引脚宏定义#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd #define DEBUG_USART_TX_GPIO_PORT GPIOA #define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9#define DEBUG_USART_RX_GPIO_PORT GPIOA#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10#define DEBUG_USART_IRQ USART1_IRQn#define DEBUG_USART_IRQHandler USART1_IRQHandler#elif DEBUG_USART2//串口2-USART2#define DEBUG_USARTx USART2#define DEBUG_USART_CLK RCC_APB1Periph_USART2#define DEBUG_USART_APBxClkCmd RCC_APB1PeriphClockCmd#define DEBUG_USART_BAUDRATE 115200// USART GPIO 引脚宏定义#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd #define DEBUG_USART_TX_GPIO_PORT GPIOA #define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_2#define DEBUG_USART_RX_GPIO_PORT GPIOA#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_3#define DEBUG_USART_IRQ USART2_IRQn#define DEBUG_USART_IRQHandler USART2_IRQHandler#elif DEBUG_USART3//串口3-USART3#define DEBUG_USARTx USART3#define DEBUG_USART_CLK RCC_APB1Periph_USART3#define DEBUG_USART_APBxClkCmd RCC_APB1PeriphClockCmd#define DEBUG_USART_BAUDRATE 115200// USART GPIO 引脚宏定义#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOB)#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd #define DEBUG_USART_TX_GPIO_PORT GPIOB #define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_10#define DEBUG_USART_RX_GPIO_PORT GPIOB#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_11#define DEBUG_USART_IRQ USART3_IRQn#define DEBUG_USART_IRQHandler USART3_IRQHandler#elif DEBUG_USART4//串口4-UART4#define DEBUG_USARTx UART4#define DEBUG_USART_CLK RCC_APB1Periph_UART4#define DEBUG_USART_APBxClkCmd RCC_APB1PeriphClockCmd#define DEBUG_USART_BAUDRATE 115200// USART GPIO 引脚宏定义#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOC)#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd #define DEBUG_USART_TX_GPIO_PORT GPIOC #define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_10#define DEBUG_USART_RX_GPIO_PORT GPIOC#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_11#define DEBUG_USART_IRQ UART4_IRQn#define DEBUG_USART_IRQHandler UART4_IRQHandler#elif DEBUG_USART5//串口5-UART5#define DEBUG_USARTx UART5#define DEBUG_USART_CLK RCC_APB1Periph_UART5#define DEBUG_USART_APBxClkCmd RCC_APB1PeriphClockCmd#define DEBUG_USART_BAUDRATE 115200// USART GPIO 引脚宏定义#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD)#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd #define DEBUG_USART_TX_GPIO_PORT GPIOC #define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_12#define DEBUG_USART_RX_GPIO_PORT GPIOD#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_2#define DEBUG_USART_IRQ UART5_IRQn#define DEBUG_USART_IRQHandler UART5_IRQHandler#endifvoid USART_Config(void);void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data);void Usart_SendHalfWord(USART_TypeDef* pUSARTx, uint16_t data);void Usart_SendArray(USART_TypeDef* pUSARTx, uint8_t *array,uint8_t num);void Usart_SendStr(USART_TypeDef* pUSARTx, uint8_t *str);#endif /* __BSP_USART_H */
13、IIC程序及EEPROM电路图
iic.c内容:
#include "iic.h"#include "delay.h"//IIC初始化void iic_init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = IIC_SCL_PIN|IIC_SDA_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //IIC_SCL_PIN|IIC_SDA_PIN推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(IIC_GPIO, &GPIO_InitStructure); GPIO_SetBits(IIC_GPIO,IIC_SCL_PIN|IIC_SDA_PIN); //设置为1 IIC_SCL_H; IIC_SDA_H;}//数据线输出模式void iic_sda_out(void){ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //IIC_SCL_PIN|IIC_SDA_PIN推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(IIC_GPIO, &GPIO_InitStructure);}//数据线输入模式void iic_sda_in(void){ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //IIC_SCL_PIN|IIC_SDA_PIN推挽输出 GPIO_Init(IIC_GPIO, &GPIO_InitStructure);}//IIC数据传送启动信号//SCL为高电平的时候,SDA下降沿 void iic_start(void){ iic_sda_out(); //SDA线输出 IIC_SDA_H; //SDA为高电平 IIC_SCL_H; //SCL为高电平 delay_us(5); IIC_SDA_L; //SDA下降沿 delay_us(6); IIC_SCL_L; }//IIC数据传送停止信号//SCL为高电平时,SDA上升沿void iic_stop(void){ iic_sda_out(); //SDA线输出 IIC_SCL_L; //SDA为低电平 IIC_SDA_L; IIC_SCL_H; delay_us(6); IIC_SDA_H; //SCL高电平 delay_us(6); }//应答信号void iic_ACK(void){ IIC_SCL_L; //SCL低电平 iic_sda_out(); IIC_SDA_L; //SDA为低电平 delay_us(2); IIC_SCL_H; //SCL高电平 delay_us(5); IIC_SCL_L; //SCL低电平}//非应答信号void iic_NACK(void){ IIC_SCL_L; //SCL低电平 iic_sda_out(); IIC_SDA_H; //SDA为高电平 delay_us(2); IIC_SCL_H; //SCL高电平 delay_us(5); IIC_SCL_L; //SCL低电平}//IIC等待应答u8 iic_wf_ack(void){ u8 times=0; iic_sda_in();//设置SDA为输入模式 IIC_SDA_H; delay_us(1); IIC_SCL_H; delay_us(1); while(GPIO_ReadInputDataBit(IIC_GPIO, IIC_SDA_PIN))//超时应答判断 { times++; delay_us(1); if(times>250) { iic_stop(); return 1; } } IIC_SCL_L; return 0;}//IIC发送数据void iic_SendByte(u8 txd){ u8 i; iic_sda_out();//设置SDA为输出模式 IIC_SCL_L; for(i=0;i<8;i++) { if((txd&0x80)>>7) IIC_SDA_H; else IIC_SDA_L; txd <<=1; delay_us(2); IIC_SCL_H; delay_us(2); IIC_SCL_L; delay_us(2); }}//IIC读取数据u8 iic_ReadByte(u8 ack){ u8 i,Receive=0; iic_sda_in();//设置SDA为输入模式 for(i=0;i<8;i++) { IIC_SCL_L; delay_us(2); IIC_SCL_H; Receive<<= 1; if(GPIO_ReadInputDataBit(IIC_GPIO, IIC_SDA_PIN)) Receive|=0x01; delay_us(1); } if(ack) iic_ACK(); else iic_NACK(); return Receive;//返回最终读取到的值} void AT24C02_WriteOneByte(u8 addr,u8 dt){ iic_start(); iic_SendByte(0xA0); iic_wf_ack(); iic_SendByte(addr); iic_wf_ack(); iic_SendByte(dt); iic_wf_ack(); iic_stop(); delay_ms(10);} u8 AT24C02_ReadOneByte(u8 addr){ u8 temp=0; iic_start(); iic_SendByte(0xA0); iic_wf_ack(); iic_SendByte(addr); iic_wf_ack(); iic_start(); iic_SendByte(0xA1); iic_wf_ack(); temp=iic_ReadByte(0); iic_stop(); return temp; }
iic.h内容:
#ifndef __IIC_H#define __IIC_H#include "stm32f10x.h"#define IIC_GPIO GPIOB#define IIC_SCL_PIN GPIO_Pin_6#define IIC_SDA_PIN GPIO_Pin_7#define IIC_SCL_H GPIO_SetBits(GPIOB, GPIO_Pin_6)#define IIC_SCL_L GPIO_ResetBits(GPIOB, GPIO_Pin_6)#define IIC_SDA_H GPIO_SetBits(GPIOB, GPIO_Pin_7)#define IIC_SDA_L GPIO_ResetBits(GPIOB, GPIO_Pin_7)void iic_init(void);void iic_sda_out(void);void iic_sda_in(void);void iic_start(void);void iic_stop(void); void iic_ACK(void);void iic_NACK(void);u8 iic_wf_ack(void);void iic_SendByte(u8 txd);u8 iic_ReadByte(u8 ack);void AT24C02_WriteOneByte(u8 addr,u8 dat);u8 AT24C02_ReadOneByte(u8 addr);#endif
main.c内容:
#include "stm32f10x.h"#include "bsp_usart.h"#include "delay.h"#include "iic.h"int main(void){ u16 value=12;//想要写进去的值 u16 ADD=16;//写EEPROM的指定地址 USART_Config(); iic_init(); while(1){ AT24C02_WriteOneByte(ADD,value); printf("写进去的数据是:%d\r\n",value); printf("读出来的数据是:%d\r\n",AT24C02_ReadOneByte(0)+1); delay_ms(1000); }}/*********************************************END OF FILE**********************/