1.GPIO,LED的点亮
IDR端口输入寄存器,读取输入状态GPIO的值。(只读并只能以字(16位)的形式读出)
ODR端口输出寄存器,设置输出状态的GPIO的值。(可读可写并只能以字(16位)的形式操作)
BSRR端口位设置/清除寄存器,设置单个位的输出值。设置单个位的值,可以置1和0。(只能写入并只能以字(16位)的形式操作)
BRR端口位清除寄存器,清除单个位的输出值。只能用于置0。(只能写入并只能以字(16位)的形式操作)
注:BSRR和BRR本质是操作ODR寄存器。
贴上代码如下:LED_Display传入的值1为亮0为灭
//led.h
#define LED1 (1)
#define LED2 (1<<1)
#define LED3 (1<<2)
#define LED4 (1<<3)
#define LED5 (1<<4)
#define LED6 (1<<5)
#define LED7 (1<<6)
#define LED8 (1<<7)
extern u8 LED_data;
//led.c
u8 LED_data=0;
void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD, ENABLE);
{
GPIO_InitTypeDef GPIO_InitStructure;
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);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All;//PC 8-15
GPIO_Init(GPIOC,&GPIO_InitStructure);
}
LED_Display(0x00);
}
/*******************************************************************************
* Function Name : LED_Display
* Description :
* Input : 8位值,1为亮0为灭
* Output : None
* Return : None
*******************************************************************************/
//LED_Display( LED_data ^= LED1 ); 单个LED翻转
//LED_Display( LED_data |= LED1 ); 点亮单个LED
//LED_Display( LED_data &= ~LED1 ); 关闭单个LED
void LED_Display(u8 data)
{
GPIOC->ODR = (~data)<<8;
// GPIOD->BRR = GPIO_Pin_2;//573置0
GPIOD->BSRR = GPIO_Pin_2;//573置1
GPIOD->BRR = GPIO_Pin_2;//573置0 防止写lCD干扰到LED的状态
}
2.KEY,按键的使用
前言:在学习按键的时候看到网络上有两大类按键的配置
一类是扫描、占用CPU时间、反应不是非常及时、
一类是中断、不好消抖、进中断后打断主程序、
经考虑后还是决定使用扫描模式。
贴上代码如下:主要思路是在Systick_Hander里置位标志位,在main函数主循环里调用按键扫描函数。
void SysTick_Handler(void)
{
static u8 num_20ms=1;
if(++num_20ms >= 20)
{
num_20ms = 0;
KeyScan_flag = SET;
}
}
int main(void)
{
SysTick_Config(SystemCoreClock/1000);//1ms
Key_Init();
while(1)
{
if( KeyScan_flag != RESET )
{
KeyScan_flag = RESET;
Key_Scan();
}
}
}
void Key_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE);
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_8; //PA0 PA8
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2; //PB1 PB2
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
}
void Key_Scan(void)//20ms 扫描一次 支持长按
{
static u8 key_temp=KEY_OFF,key_clear=0,key_weit=0;
if( (KEY_READ_B1==RESET)||(KEY_READ_B2==RESET)||(KEY_READ_B3==RESET)||(KEY_READ_B4==RESET) )
{
++key_clear;
// if( key_clear == 2 )
// {
// if(KEY_READ_B1==0x00) {key_temp = KEY_B1;}
// else if(KEY_READ_B2==0x00) {key_temp = KEY_B2;}
// else if(KEY_READ_B3==0x00) {key_temp = KEY_B3;}
// else if(KEY_READ_B4==0x00) {key_temp = KEY_B4;}
// key_weit = 1;
// }
// else if( key_clear >= 40)//20*40=800ms 长按
// {
// key_long_data = key_temp;
// key_clear = 2+1;
// }
if( key_clear >= 40)//20*40=800ms 长按
{
key_long_data = key_temp;
key_clear = 2+1;//不进入短按读取按键程序节约CPU时间
}
else if( key_clear == 2 )//短按
{
if(KEY_READ_B1==RESET) {key_temp = KEY_B1;}
else if(KEY_READ_B2==RESET) {key_temp = KEY_B2;}
else if(KEY_READ_B3==RESET) {key_temp = KEY_B3;}
else if(KEY_READ_B4==RESET) {key_temp = KEY_B4;}
key_weit = 1;
}
}
else
{
key_clear = 0;
if( key_weit == 1 )//按下后松手
{
key_weit = 0;
if( key_long_data == KEY_OFF ) { key_data = key_temp; }//短按
else { key_long_data = KEY_OFF; }
key_temp = KEY_OFF;
}
}
}
3.USART的配置
printf重定向
其实printf重定向就是我们将printf重新定向到串口,也就是我们可以自己重写C的库函数,当连接器检查到用户编写了与C库函数相同的名字,优先采用 用户编写的函数这样用户就可以实现对库的修改了。printf函数实际是一个宏,最终调用的是 fputc(int ch,FILE *f)这个函数,所以我们需要修改这个函数。
下面我们着重分析一下fputc函数:
这个库函数调用了两个ST库函数,分别是:USART_GetFlagStatus()与USART_SendData(),形参ch表示串口将要发送的数据,也就是说。当使用printf()时,它先调用fpuc()函数,然后使用ST库的串口发送函数USART_SendData(),把数据转移到发送数据寄存器TDR.触发我们的串口向PC发送一个相应的数据,调用完USART_SendData()之后,使用 while(USART_GetFlagStatus(USART2,USART_FLAG_TC) != SET)语句不停的检查串口的数据是否发送完成的标志位TC,一直检查到标志为“完成”,才进行下一步操作,
注意:由于fputc()函数的形参调用了C库的FILE,所以在程序中加入stdio.h这个头文件,便且在keil的编译器的设置中勾选Use MicroLIB(使用微库)正点原子的重定向与这个类似不过他没有用微库(Use MicroLIB)。
int fputc(int ch, FILE *f) //串口重定向标准输出函数
{
USART_SendData(USART2, (uint8_t)ch);
while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
return ch;
}
贴上代码
//usart.h
#define USART2_RX_BUF_SIZE 64
typedef struct
{
u8 RX_BUF[USART2_RX_BUF_SIZE];//数据缓存
vu8 SIZE;//接收到字节数
vu8 FLAG;//接收完一帧标志
}Usart2_Data_Structure;
extern Usart2_Data_Structure USART2_DATA;
//usart.c
Usart2_Data_Structure USART2_DATA={0};
void USART2_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
{
GPIO_InitTypeDef GPIO_InitStructure; //PA2
...
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
{
USART_InitTypeDef USART_InitStructure;
...
USART_Init(USART2, &USART_InitStructure);
}
{
NVIC_InitTypeDef NVIC_InitStructure;
...
NVIC_Init(&NVIC_InitStructure);
}
USART_Cmd(USART2, ENABLE);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//接受中断
USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);//空闲中断
}
void USART2_IRQHandler(void)
{
static u8 size=0;
if( USART_GetITStatus(USART2, USART_IT_RXNE) != RESET )
{
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
USART2_DATA.RX_BUF[size] = USART_ReceiveData(USART2);
++size;
}
if( USART_GetITStatus(USART2, USART_IT_IDLE) != RESET )
{/*USART_IT_IDLE 空闲中断要用 USART_ReceiveData(USARTx);
或者 USARTx->SR; USARTx->DR; 清除中断。USART_ClearITPendingBit清除不了。*/
USART_ReceiveData(USART2);//清除中断
USART2_DATA.RX_BUF[size] = '\0';
USART2_DATA.SIZE = size;
size = 0;
USART2_DATA.FLAG = 1;
}
}
4.ADC的配置
ADC初始化
{
使能ADC时钟
ADC时钟6分频(ADC时钟超过14MHz,可能不准)
配置GPIO
配置ADC
使能ADC
ADC校准
}
ADC读值
{
ADC规则组通道配置
设置软件触发
等待转换完成
读值
}
extern void Delay_Ms(u32 nTime);
void ADC1_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;//
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
{
ADC_InitTypeDef ADC_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);
}
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while( ADC_GetResetCalibrationStatus(ADC1) );
ADC_StartCalibration(ADC1);
while( ADC_GetCalibrationStatus(ADC1) );
}
u16 ADC_GetVal(uint8_t ADC_Channel)
{
ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_239Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while( ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET );
return ADC_GetConversionValue(ADC1);
}
u16 ADC_GetAverage(uint8_t ADC_Channel, uint8_t times)
{
u8 i;
u32 temp_val=0;
for(i=0; i<times; i++)
{
temp_val += ADC_GetVal(ADC_Channel);
Delay_Ms(5);
}
return (temp_val/times);
}
5.TIM时钟的应用
5.1.PWM
时钟使能
GPIO配置
TIM_Base配置
TIM_Channel配置
使能CCR预装载寄存器
使能TIM
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);// TIM3 CH1 PA6 TIM3 CH2 PA7 TIM2 CH2 PA1
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7|GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructrue;
TIM_TimeBaseInitStructrue.TIM_Prescaler = 72-1;// 1MHz
TIM_TimeBaseInitStructrue.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructrue.TIM_Period = 100-1; // 1MHz/100=10KHz
TIM_TimeBaseInitStructrue.TIM_ClockDivision = TIM_CKD_DIV1;
// TIM_TimeBaseInitStructrue.TIM_RepetitionCounter = ;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructrue);
}
{
TIM_OCInitTypeDef TIM_OCInitStructrue;
TIM_OCInitStructrue.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructrue.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructrue.TIM_Pulse = 20;
TIM_OCInitStructrue.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStructrue);//CH1 PA6
TIM_OCInitStructrue.TIM_Pulse = 50;
TIM_OC2Init(TIM3, &TIM_OCInitStructrue);//CH2 PA7
}
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_Cmd(TIM3, ENABLE); //使能TIM3
// TIM_SetCompare2(TIM3,20);
5.2.比较捕获模式(特殊模式PWM捕获模式)
详细配置要求参考STM32中文参考手册V10 14.2.6节PWM输入模式
void TIM2_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);// TIM2 CH2 PA1
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
// GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructrue;
TIM_TimeBaseInitStructrue.TIM_Prescaler = 72-1;// 1MHz
TIM_TimeBaseInitStructrue.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructrue.TIM_Period = 0xffff;
TIM_TimeBaseInitStructrue.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructrue);
}
{
TIM_ICInitTypeDef TIM_ICInitStructrue;
TIM_ICInitStructrue.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructrue.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructrue.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructrue.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructrue.TIM_ICFilter = 0x00;
TIM_PWMIConfig(TIM2, &TIM_ICInitStructrue);//CH2 PA1
}
TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);//选择有效的触发输入信号:置TIMx_SMCR寄存器中的TS=101(选择TI1FP1)。
TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);//配置从模式控制器为复位模式:置TIMx_SMCR中的SMS=100。
TIM_Cmd(TIM2, ENABLE); //使能TIM3
}
频率与占空比的计算公式
Frequency = 1000000.0/(IC2Value+1);
DutyCycle = (IC1Value+1)*1.0/(IC2Value+1);
单片机组,和嵌入式组的相关代码资源可以在我的主页下载。