文章目录[隐藏]
重要引脚
GPIO | 功能 |
PA1 | TIM2_CH2 / SER(74LS595串行数据输入引脚) / 频率可调脉冲 |
PA2 | TIM2_CH3 / USART2_TX / RCK(74LS595串行存储时钟输入引脚) / 频率可调脉冲 |
PA3 | USART2_RX / SCK(74LS595串行移位时钟输入引脚) / 光敏电阻读入引脚 |
PA4 | ADC1_IN4 / SCL(三轴传感器时钟引脚) |
PA5 | ADC1_IN5 / SDA(三轴传感器数据引脚) / 4*2按键矩阵 |
PA6 | TIM3_CH1 / 占空比可调脉冲 / 温度传感器 |
PA7 | TIM3_CH2 / 占空比可调脉冲 / 温湿度传感器 |
PBO ADC1_Channel8
PA0 key1
PA8 key2
PB1 key3
PB2 key4
0.文件配置
例程文件配置过程中要在Library中加入libraries文件
1.LED
void led_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* GPIOD Periph clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* Configure PD0 and PD2 in output pushpull mode */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9| GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
void led_control(u16 led,u8 state)
{
if(state==1)
{
GPIO_ResetBits(GPIOC,led<<8);
GPIO_SetBits(GPIOD,GPIO_Pin_2);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
}
else
{
GPIO_SetBits(GPIOC,led<<8);
GPIO_SetBits(GPIOD,GPIO_Pin_2);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
}
}
令pd2置高(1)后,输入是什么,输出就是什么,把pd2再置低(0),输入是什么,输出保持不变
起到锁存器的功能,但一般程序里只要是pd2置高就可以了,不需要再置低,即不需要锁存功能
2.Buzzer
当我们stm32复位后,PB4引脚默认是作为JTAG接口的RST引脚,因此我们再使用蜂鸣器之前要先把PB4复用回我们的普通IO口。
因此就要加上这条语句
GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST,ENABLE);
同时我们还要使能复用IO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB,ENABLE);
接下来蜂鸣器的用法就跟我们的普通LED是一样的了。
void buzzer_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* GPIOD Periph clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
3.AT24c02
I2C代码需要在参考例程里面复制粘贴,需要自己编写的是AT24C02代码
void Write_AT24c02(unsigned char add,unsigned char data1)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(add);
I2CWaitAck();
I2CSendByte(data1);
I2CWaitAck();
I2CStop();
}
unsigned char Read_AT24c02(unsigned char add)
{
unsigned char temp;
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(add);
I2CWaitAck();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
temp=I2CReceiveByte();
I2CWaitAck();
I2CStop();
return temp;
}
使用AT24c02写代码时候要延迟5ms
E2PROM一共有256个字节
如果要存储u16位数据,要用2个地址来分来存储。/256储存高八位,%256存储低八位。
利用共同体储存不同数据类型
// 声明共用体eeprom_float
union eeprom_float
{
float a;
u8 b[4];
}float_write,float_read;
// 将共用体中的数据以字节的方式存放到eeprom中
for(i=0;i<sizeof(float);i++)
{
Write_AT24c02(0x10+i,float_write.b[i]);
Delay_Ms(5);
}
for(i=0;i<sizeof(float);i++)
{
float_read.b[i]=Read_AT24c02(0x10+i);
}
// 此时共用体float_read.a就等于float_write.a,而不需要在存放和读取的时候进行数据类型的转换
sprintf(str,"float_read=%f",float_read.a);
LCD_DisplayStringLine(Line0,str);
/*
不同类型数据 需要根据字节大小 改变数组长度
u8 u16 u32 s16 float double
1 2 4 2 4 8
字节大小
*/
省赛中会遇到赋初值情况,第一次读取是初值,后面掉电读取是修改值
if(Read_AT24c02(0x06)!=2&&Read_AT24c02(0xaa)!=15) //地址和数值都是瞎写的
{
Write_AT24c02(0x01,30); Delay_MS(5);
Write_AT24c02(0x02,50); Delay_MS(5);
Write_AT24c02(0x03,70); Delay_MS(5);
Write_AT24c02(0x06,2); //加了这个语句之后,就可以让这个判断只执行一次
Delay_MS(5);
Write_AT24c02(0xaa,15);
Delay_MS(5);
}
4.串口通信
stm32f10x_stdperiph_lib\STM32F10x_StdPeriph_Lib_V3.5.0\Utilities\STM32_EVAL\STM3210B_EVAL (328行)
STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\USART\Printf
DMA_Interrupt
DMA_Interrupt\stm32f10x_it.c(中断函数)
void STM_EVAL_COMInit( USART_InitTypeDef* USART_InitStruct)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIO clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA| RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
/* Configure USART Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure USART Rx as input floating */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* USART configuration */
USART_Init(USART2, USART_InitStruct);
/* Enable USART */
USART_Cmd(USART2, ENABLE);
}
void USART2_Init(void)
{
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitStructure.USART_BaudRate = 115200;
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;
STM_EVAL_COMInit(&USART_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //注意加上最后一句话
}
PS:第一个字符打印不出来
直接修改while的等待条件,while(USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET);把TC改为TXE,因为TXE只能硬件清零,在数据写进DR时就置0,数据移出时置1,可以保证数据不会在DR被覆盖;
int fputc(int ch, FILE *f)
{
/* Place your implementation of fputc here */
/* e.g. write a character to the USART */
USART_SendData(USART2, (uint8_t) ch);
/* Loop until the end of transmission */
while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET)
{}
return ch;
}
u8 rx_buf[10];
u8 rx_count=0;
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
/* Read one byte from the receive data register */
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
rx_buf[rx_count++]=USART_ReceiveData(USART2);
}
}
使用printf注意:1.打开 Use MicroLIB2.#include"stdio.h"
5.LCD
LCD操作常用函数
STM3210B_LCD_Init(); //初始化LCD相关IO,并对液晶进行初始化
LCD_Clear(Blue);//清屏操作
LCD_SetBackColor(Blue);//设置显示文字的背景色
LCD_SetTextColor(White);//设置字体颜色
u8 str[20];
sprintf(str,"helloworld");
LCD_DisplayStringLine(Line2,str);
//在第二行输出helloworld
void Display_val(u16 val)
{
u8 temp[20];
sprintf(temp,"val:%5d",val);
LCD_DisplayStringLine(Line1,temp);
}
Display_val(77);//在第一行输出77
使用sprintf 注意添加#include "stdio.h"
关于LCD与LED冲突问题
方法一:
void LED_control_ALL(u16 flagstatus)
{
GPIOC->ODR=flagstatus<<8;
GPIO_SetBits(GPIOD,GPIO_Pin_2);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
}
//使用这个函数点灯 可以避免LED冲突
//输入LED_control_ALL(0xfe) 点亮第一个灯
方法二:
void LCD_WriteRAM_Prepare(void)
void LCD_WriteRAM(u16 RGB_Code)
void LCD_WriteReg(u8 LCD_Reg, u16 LCD_RegValue)
开头加上 u16 pcout =GPIOC->ODR
结尾加上 GPIOC->ODR=pcout
即可
关于LCD长数据对短数据覆盖问题
方法一:在前面空格
方法二:格式化输出,针对数据
sprintf((char *)str,"%5dHz",5000);
LCD_DisplayStringLine(Line1,str);
sprintf((char *)str,"%5dHz",10);
LCD_DisplayStringLine(Line1,str);
//如果是%d则会是10HzHz
sprintf((char *)str,"%-5dHz",10);
LCD_DisplayStringLine(Line2,str);
//数据左对齐
sprintf((char *)str,"%05dHz",10);
LCD_DisplayStringLine(Line3,str);
//数据前面补0
//%x输出十六进制 %o输出八进制 %%输出百分号 %s输出字符串 %c输出字符 %.3f输出小数点后三位
关于LCD字符闪烁与高亮
_Bool lcd_flag=0;
if(lcd_flag==1)
{
memset(lcd_buf,0,sizeof(lcd_buf));
sprintf((char *)lcd_buf," %0.2d:%0.2d:%0.2d",Report_HH, Report_MM, Report_SS);
LCD_DisplayStringLine(Line5,lcd_buf);
}
else
{
memset(lcd_buf,0,sizeof(lcd_buf));
sprintf((char *)lcd_buf," :%0.2d:%0.2d",Report_MM, Report_SS);
LCD_DisplayStringLine(Line5,lcd_buf);
}
//实现LCD字符闪烁 lcd_flag是定时器4设置flag 每0.3s翻转一次
if(time_sel==0)
{
LCD_SetTextColor(Red);//设置字符显示颜色
LCD_DisplayChar(Line4,200,Report_HH/10+'0');
LCD_DisplayChar(Line4,200-16,Report_HH%10+'0');
LCD_SetTextColor(White);//显示单个字符的显示函数
}
//实现字符变红
6.ADC
PB0对应着ADC的Channel_8
STM32F10x_StdPeriph_Examples\ADC\ADC1_DMA
void adc_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOB, &GPIO_InitStructure);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
//初始化的时候注意要把ENABLE改为DISABLE
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_13Cycles5);
//要注意是ADC1和第八通道
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
u16 get_adc()
{
u16 value;
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
value = ADC_GetConversionValue(ADC1);
return value;
}
while(1)
{
adc_value=get_adc();
sprintf(str,"adc: %d",(adc_value/(4096/4))); //分档位 0 1 2 3
LCD_DisplayStringLine(Line2,str);
Delay_Ms(100);
}
//float(adc_value)/4096*3.3 0~3.3V
ADC模块读到的数据是12位的数据,STM32读到的ADC值,是从0到4095。当把ADC引脚接了GND,读到的是0;当把ADC引脚接了VDD,读到的是4095。
tmp=(float)adc_value/4096*3.3
注意事项
关于get_adc()函数,当使用ADC_SoftwareStartConvCmd()使能软件转换之后,需要使用ADC_GetFlagStatus()函数等待转换结束后,再进行ADC_GetConversionValue()输出。
关于adc_init()函数,初始化的时候注意要把ENABLE改为DISABLE,ADC_RegularChannelConfig() 改成第八通道
7.RTC时钟
注意事项
1.RTC配置的时候结合要选择:LSI内部晶振
2.调用配置NVIC函数
3.中断处理函数
需要用到的文件
RTC/LSI_Calib/main.c (178开始
void RTC_Configuration(void)
{
/* Enable PWR and BKP clocks */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
/* Allow access to BKP Domain */
PWR_BackupAccessCmd(ENABLE);
/* Reset Backup Domain */
BKP_DeInit();
/* Enable the LSI OSC */
RCC_LSICmd(ENABLE);
/* Wait till LSI is ready */
while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET)
{}
/* Select the RTC Clock Source */
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
/* Enable RTC Clock */
RCC_RTCCLKCmd(ENABLE);
/* Wait for RTC registers synchronization */
RTC_WaitForSynchro();
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
/* Enable the RTC Second */
RTC_ITConfig(RTC_IT_SEC, ENABLE);
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
/* Set RTC prescaler: set RTC period to 1sec */
RTC_SetPrescaler(40000);
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
/* To output second signal on Tamper pin, the tamper functionality
must be disabled (by default this functionality is disabled) */
BKP_TamperPinCmd(DISABLE);
/* Enable the RTC Second Output on Tamper Pin */
// BKP_RTCOutputConfig(BKP_RTCOutputSource_Second);
//屏蔽最后一句话,解决白屏问题
NVIC_Configuration();
}
//最后需要加上NVIC_Configuration
RTC/Calendar/main.c
在Time_Display函数中加入TimeVar=0 和 改为0x00015180 会显示出00:00:00
void RTC_IRQHandler(void)
{
if (RTC_GetITStatus(RTC_IT_SEC) != RESET)
{
/* Clear the RTC Second interrupt */
RTC_ClearITPendingBit(RTC_IT_SEC);
/* Enable time update */
TimeDisplay = 1;
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
}
}
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Configure one bit for preemption priority */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* Enable the RTC Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void Time_Display(uint32_t TimeVar)
{
u8 str[20];
uint32_t THH = 0, TMM = 0, TSS = 0;
/* Reset RTC Counter when Time is 23:59:59 */
if (RTC_GetCounter() == 0x0001517F)
{
RTC_SetCounter(0x0);
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
}
/* Compute hours */
THH = TimeVar / 3600;
/* Compute minutes */
TMM = (TimeVar % 3600) / 60;
/* Compute seconds */
TSS = (TimeVar % 3600) % 60;
sprintf(str,"Time: %0.2d:%0.2d:%0.2d", THH, TMM, TSS);
LCD_DisplayStringLine(Line1,str);
}
//需要自己修改的只有后俩句
void Time_Adjust(u8 HH,u8 MM,u8 SS)
{
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
/* Change the current time */
RTC_SetCounter(HH*3600+MM*60+SS);
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
}
/*
.......
while (1)
{
/* If 1s has been elapsed */
if (TimeDisplay == 1)
{
/* Display current time */
Time_Display(RTC_GetCounter());
TimeDisplay = 0;
}
}
.......
*/
8.PWM
需要用到文件: 以上是常用GPIO
Project\STM32F10x_StdPeriph_Examples\TIM\PWM_Output
PWM输出相同频率,不同占空比
// 设置输出比较寄存器的值,用于改变PWM的占空比。
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1)
// 用于设置ARR的值,通过设置ARR的值来改变定时器的频率
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload)
ARR的周期值对应 TIM_TimeBaseStructure.TIM_Period 的设置值
PSC的分频值对应 TIM_TimeBaseStructure.TIM_Prescaler 的设置值
void TIM2_PWM_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
uint16_t PrescalerValue = 0;
uint16_t CCR1_Val = 500;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
PrescalerValue = (uint16_t) (SystemCoreClock / 1000000) - 1;
TIM_TimeBaseStructure.TIM_Period = 1000 - 1;
TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM2, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}
注:ARP及PSC的设置值都是减1的,所以这里要再加上1,才能还原单片机内部的处理值。
PrescalerValue = (uint16_t) (SystemCoreClock / 1000000) -1 理解
现在我们知道定时器时钟的频率是72MHz,SystemCoreClock是72MHz,则PrescalerValue计算出来是71,我们将预分频器设置为71,查看数据手册,如下图,可以知道会对时钟的频率进行72分频,得到的频率是1MHz。然后把TIM_Period 设置为1000-1,即设置ARR(自动重装载寄存器)为999,即定时器计数器从0开始,计数到999,则会自动重新装载,前面我们得到了频率为1MHz,那么定时器每进行一次重转载的周期是1MHz/1000=1kHz,也就是1ms。
while(1)
{
TIM_SetAutoreload(TIM2,1000-1); //pwm频率为1khz
TIM_SetCompare2(TIM2,500); //PA1(TIM2_CH2)占空比为50%
TIM_SetCompare3(TIM2,100); //PA2(TIM2_CH3)占空比为10%
}
不同频率,固定占空比
STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\TIM\OCToggle
__IO uint16_t TIM3_CCR1_Val = 32768;
__IO uint16_t TIM3_CCR2_Val = 16384;
void tim3_pwm_octoggle(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
uint16_t PrescalerValue = 0;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB|
RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Compute the prescaler value */
PrescalerValue = (uint16_t) (SystemCoreClock / 24000000) - 1;
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 65535;
TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = TIM3_CCR1_Val;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable);
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = TIM3_CCR2_Val;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Disable);
/* TIM enable counter */
TIM_Cmd(TIM3, ENABLE);
/* TIM IT enable */
TIM_ITConfig(TIM3, TIM_IT_CC1 | TIM_IT_CC2 , ENABLE);
}
**频率和占空比的计算:**占空比很简单,每计数16384翻转一下电平,一个周期就要计数32768次,占空比毫无疑问为50%。对于实际的输出频率而言,定时器的时钟是72MHz,然后经过3分频为24MHz,一个周期计数32768次,则实际频率就是24MHz/32768=732.4Hz
uint16_t TIM3_capture = 0;
void TIM3_IRQHandler(void)
{
/* TIM3_CH1 toggling with frequency = 183.1 Hz */
if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1 );
TIM3_capture = TIM_GetCapture1(TIM3);
TIM_SetCompare1(TIM3, TIM3_capture + TIM3_CCR1_Val );
}
/* TIM3_CH2 toggling with frequency = 366.2 Hz */
if (TIM_GetITStatus(TIM3, TIM_IT_CC2) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
TIM3_capture = TIM_GetCapture2(TIM3);
TIM_SetCompare2(TIM3, TIM3_capture + TIM3_CCR2_Val);
}
}
.......
while(1)
{
TIM3_CCR1_Val =2400 ; //TIM3_CH1频率 50KHz
//三分频 24Mhz / 2400 =10 000 =10Khz = 0.1us 整个周期是0.2us 所以是50Khz
TIM3_CCR2_Val =4800 ; //TIM3_CH1频率 25KHz
//同理上面
}
.......
可调频率,占空比
前面可调频率,固定占空比的初始化代码一样,主要不同是在中断函数中。通过改变TIMx_CHx_duty来控制占空比,频率通过控制CCR的值
uint16_t TIM2_capture = 0;
_Bool TIM2_CH2_flag=0,TIM2_CH3_flag=0;
float TIM2_CH2_duty=0.3,TIM2_CH3_duty=0.7;
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
TIM2_capture = TIM_GetCapture2(TIM3);
if(TIM2_CH2_flag==1)
{
TIM2_CH2_flag=0;
TIM_SetCompare2(TIM2, TIM2_capture +(u16)( TIM2_CH2_duty * TIM2_CCR2_Val));
}
else
{
TIM2_CH2_flag=1;
TIM_SetCompare2(TIM2, TIM2_capture +(u16)( TIM2_CCR2_Val*(1-TIM2_CH2_duty)));
}
}
if (TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);
TIM2_capture = TIM_GetCapture3(TIM2);
TIM_SetCompare3(TIM2, TIM2_capture + TIM2_CCR3_Val);
}
}
PWM捕获占空比和频率
Project\STM32F10x_StdPeriph_Examples\TIM\InputCapture 初始化链接
原理:
首先把捕获的模式设置为上升沿捕获,发生第一次捕获到上升沿的中断,以此中断时刻作为一个起点,得到的计数值清0,此时将捕获模式设置为下降沿捕获,在发生第二次中断的时候,捕获到了下降沿,得到的计数值为TIM3_CH2_ReadValue1就是一个周期中高电平的时间,然后我们在中断中又将捕获的方式设置为上升沿捕获,那么在第三次产生中断的时候,得到的计数值为TIM3_CH2_ReadValue2。所以到此为止,我们知道了一个周期中高电平和整个周期的时间,那么这个PWM方波的频率和占空比就得到了
PWM方波的频率应该为72MHz/TIM3_CH2_ReadValue2
PWM方波的占空比应该为TIM3_CH2_ReadValue1*100/TIM3_CH2_ReadValue2
//乘一百是为了让占空比显示0-100 而不是小数
void TIM3_Input_Mode_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //容易忘记通道一
TIM_ICInit(TIM3, &TIM_ICInitStructure);
/* TIM enable counter */
TIM_Cmd(TIM3, ENABLE);
/* Enable the CC2 Interrupt Request */
TIM_ITConfig(TIM3, TIM_IT_CC1|TIM_IT_CC2, ENABLE);
}
__IO uint16_t TIM3_CH2_ReadValue1 = 0, TIM3_CH2_ReadValue2 = 0;
__IO uint16_t TIM3_CH2_CaptureNumber = 0;
__IO uint32_t TIM3_CH2_Freq = 0;
__IO uint16_t TIM3_CH1_ReadValue1 = 0, TIM3_CH1_ReadValue2 = 0;
__IO uint16_t TIM3_CH1_CaptureNumber = 0;
__IO uint32_t TIM3_CH1_Freq = 0;
u8 TIM3_CH1_Duty,TIM3_CH2_Duty;
u8 capture_flag = 1;
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_CC2) == SET)
{
/* Clear TIM3 Capture compare interrupt pending bit */
TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
if(capture_flag == 2)
{
if(TIM3_CH2_CaptureNumber == 0)
{
TIM_SetCounter(TIM3, 0);
TIM_OC2PolarityConfig(TIM3,TIM_ICPolarity_Falling);
TIM3_CH2_CaptureNumber = 1;
}
else if (TIM3_CH2_CaptureNumber == 1)
{
/* Get the Input Capture value */
TIM3_CH2_ReadValue1 = TIM_GetCounter(TIM3);
TIM_OC2PolarityConfig(TIM3,TIM_ICPolarity_Rising);
TIM3_CH2_CaptureNumber = 2;
}
else if(TIM3_CH2_CaptureNumber == 2)
{
/* Get the Input Capture value */
TIM3_CH2_ReadValue2 = TIM_GetCounter(TIM3);
/* Frequency computation */
TIM3_CH2_Freq = (uint32_t) SystemCoreClock / TIM3_CH2_ReadValue2;
TIM3_CH2_Duty = TIM3_CH2_ReadValue1 * 100 / TIM3_CH2_ReadValue2;
TIM3_CH2_CaptureNumber = 0;
}
}
}
if(TIM_GetITStatus(TIM3, TIM_IT_CC1) == SET)
{
/* Clear TIM3 Capture compare interrupt pending bit */
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
if(capture_flag == 1)
{
if(TIM3_CH1_CaptureNumber == 0)
{
TIM_SetCounter(TIM3, 0);
TIM_OC1PolarityConfig(TIM3,TIM_ICPolarity_Falling);
TIM3_CH1_CaptureNumber = 1;
}
else if (TIM3_CH1_CaptureNumber == 1)
{
/* Get the Input Capture value */
TIM3_CH1_ReadValue1 = TIM_GetCounter(TIM3);
TIM_OC1PolarityConfig(TIM3,TIM_ICPolarity_Rising);
TIM3_CH1_CaptureNumber = 2;
}
else if(TIM3_CH1_CaptureNumber == 2)
{
/* Get the Input Capture value */
TIM3_CH1_ReadValue2 = TIM_GetCounter(TIM3);
/* Frequency computation */
TIM3_CH1_Freq = (uint32_t) SystemCoreClock / TIM3_CH1_ReadValue2;
TIM3_CH1_Duty = TIM3_CH1_ReadValue1 * 100 / TIM3_CH1_ReadValue2;
TIM3_CH1_CaptureNumber = 0;
}
}
}
}
void TIM4_IRQHandler(void)
{
static u16 pwm_capture_count=0;;
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
pwm_capture_count++;
if(pwm_capture_count==100)
{
pwm_capture_count=0;
capture_flag = capture_flag % 2 + 1;
TIM3_CH1_CaptureNumber=0;
TIM3_CH2_CaptureNumber=0;
}
}
}
9.KEY
输出模式是浮空输出
void key_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* GPIOD Periph clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_8 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure PD0 and PD2 in output pushpull mode */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
unsigned char Trg;
unsigned char Cont;
void key_read(void)
{
unsigned char ReadData = (KEYPORT)^0xff;
Trg = ReadData & (ReadData ^ Cont);
Cont = ReadData;
}
/*(1) 没有按键的时候 ReadData = 0 Trg = 0 Cont = 0
(2) 第一次PB0按下的情况 ReadData = 0x01 Trg = 0x01 Cont = 0x01
(3) PB0长按 ReadData = 0x01 Trg = 0 Cont = 0x01
(4) 按键松开的情况 ReadData = 0 Trg = 0 Cont = 0x00
*/
key.h
#ifndef __KEY_H
#define __KEY_H
#include "stm32f10x.h"
void key_init(void);
void key_read(void);
#define KB1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)
#define KB2 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8)
#define KB3 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)
#define KB4 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_2)
#define KEYPORT KB1 | (KB2 << 1) | (KB3 << 2) | (KB4 << 3) | 0xf0
extern unsigned char Trg;
extern unsigned char Cont;
#endif
需要自己配置 记住三行按键代码 需要多练习
关于按键长短按判断
int main(void)
{
......//初始化
while(1)
{
if(key_flag)
{
key_flag=0;//定时器4 10ms判断一次
key_read();//三行代码
if(Cont == 0x01) //按键按下判断
{
key_number++;
if(key_number>=50&&key_false==1) //50 * 10 ms = 500ms
{
key_false=0;
key_number=0;
lcd_number+=10;
}
}
if(Cont ==0x00 && Trg ==0x00) //松手监测
{
if(key_number>0 && key_number<50 && key_false==1)
{
lcd_number++;
}
key_number=0;
key_false=1;
}
}
}
}
10.TIM
在比赛提供的V3.5库的"Project->STM32F10x_StdPeriph_Examples->TIM->TimeBase"文件夹
void tim4_init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_TimeBaseStructure.TIM_Period =999 ;
TIM_TimeBaseStructure.TIM_Prescaler = 71; //72 000 000 /72 = 1 000 000
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_ITConfig(TIM4,TIM_IT_Update, ENABLE);
TIM_Cmd(TIM4, ENABLE);
}
void TIM4_IRQHandler(void)
{
static u16 buzzer_count =0;
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
buzzer_count++;
if(buzzer_count==500)
{
buzzer_count=0;
buzzer_flag=1;
}
}
}
- 中断设置改为TIM_IT_Update
- 时钟设置为TIM4
使用TIM4原因
TIM2,TIM3做硬件PWM输出和捕获时会用到,TIM1是高级定时器。所以用Tim4
PWM与USART冲突
由于usart接受引脚为PA2,而PWM2的输出引脚也是PA2这就导致了,当我们需要PWM波时需要关闭USART功能,即失能usart的接受中断,开启命令和时钟
#define PWM_ENABLE 1
#define UART_ENABLE 2
void PWM_UART_Enable(u8 flag)
{
if(flag==PWM_ENABLE)
{
USART_Cmd(USART2, DISABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, DISABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}
if(flag==UART_ENABLE)
{
TIM_Cmd(TIM2, DISABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, DISABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
USART_Cmd(USART2, ENABLE);
}
}
省赛代码
gitee 代码:https://gitee.com/yin-shijie111/code.git
版权声明:本文为CSDN博主「Yinzz2」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_51062259/article/details/122521002
暂无评论