stm32、IO口、中断、串口、定时器讲解

目录

一、IO口的八种模式

二、IO口的配置

三、外部中断配置

四、串口配置

五、定时器配置

六、项目


一、IO口的八种模式

输入

浮空输入:浮空,顾名思义,就相当与此端口在默认情况下什么都不接,呈高阻态,这种设置在数据传输时用的比较多。

上拉输入:即通过一个上拉电阻,使它接到vcc

下拉输入:即通过一个下拉电阻,使它接到gnd

模拟输入:一般用于adc数模转换

输出

推挽输出:既可以输出高电平,也可以输出低电平

复用推挽输出:一般是一些复用端口再用,比如uart、iic等

开漏输出::输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行. 适合于做电流型的驱动,其吸收电流的能力相对强(一般20mA以内).

复用开漏输出 :一般用于内外设功能(TX1,MOSI,MISO.SCK.SS)

二、IO口的配置

1. 时钟使能

2. 配置GPIO

3. 初始化GPIO

此图片用于M3内核 

d7ba40a19ab145038a248de6a285f2f7.png

/*-------led-------*/

#include "led.h"

void led_Init(void)
{
	GPIO_InitTypeDef led;
	
	//1.时钟使能

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC ,  ENABLE);
	
	//2.配置GPIO
	
		led.GPIO_Mode = GPIO_Mode_Out_PP ;
		led.GPIO_Pin  = GPIO_Pin_13;
		led.GPIO_Speed = GPIO_Speed_50MHz;
	
	//3.初始化GPIO
	
	 GPIO_Init(GPIOC,  &led);
}

/*-------shake-------*/

#include "shake.h"

void shake_Init()
{
		GPIO_InitTypeDef shake;

	
	//1.使能时钟
	
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,  ENABLE);
	
	//2.配置GPIO
	
	shake.GPIO_Mode = GPIO_Mode_IPD;
 	shake.GPIO_Pin  =   GPIO_Pin_4;
	shake.GPIO_Speed = GPIO_Speed_10MHz;
	
	//3.初始化GPIO
	
		GPIO_Init(GPIOA,  &shake);
	
}

/*-------relay-------*/

#include "relay.h"

void relay_Init()
{
	GPIO_InitTypeDef relay;
	
	//1.使能时钟
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,  ENABLE);

	
	//2.配置GPIO
	
	relay.GPIO_Mode = GPIO_Mode_Out_PP;
	relay.GPIO_Pin  = GPIO_Pin_3;
	relay.GPIO_Speed = GPIO_Speed_10MHz;
	
	//3.初始化GPIO

	GPIO_Init(GPIOA,  &relay);
		

}

/*-------delay-------*/

#include "delay.h"


//us

void delay_us(int delay_us)
{    
  volatile unsigned int num;
  volatile unsigned int t;
 
  
  for (num = 0; num < delay_us; num++)
  {
    t = 11;
    while (t != 0)
    {
      t--;
    }
  }
}

//ms
void delay_ms( int delay_ms)
{    
  volatile unsigned int num;
  for (num = 0; num < delay_ms; num++)
  {
    delay_us(1000);
  }

}

三、外部中断配置

1. 配置GPIO并使能时钟

2. 把GPIO映射到对应的中断线

3. 配置外部中断

4. 配置NVIC组

5. 配置NVIC中断控制器

6. 配置中断服务函数

#include "exit.h"
#include "shake.h"

void exit_Init()
{
	EXTI_InitTypeDef exti;
	NVIC_InitTypeDef nvic;
	
	//1.配置GPIO
	
	shake_Init();
	
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO,  ENABLE); //打开中断用到的时钟
	GPIO_EXTILineConfig( GPIO_PortSourceGPIOA, GPIO_PinSource4 ); //把GPIO映射到对应的中断线---->在GPIO.H
  NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2); //配置NVIC优先级组--->在misc.h


	
	//2.配置外部中断
	
	exti.EXTI_Line      = EXTI_Line4;
	exti.EXTI_Mode      = EXTI_Mode_Interrupt;
	exti.EXTI_Trigger   = EXTI_Trigger_Falling;
	exti.EXTI_LineCmd = ENABLE;   
	
	//3.初始化外部中断
	
  EXTI_Init(&exti);

	//4.配置NVIC中断控制器
	
	nvic.NVIC_IRQChannel     = EXTI4_IRQn; //中断源---->在misc.c--->stm32f10x.h           
	nvic.NVIC_IRQChannelPreemptionPriority  = 1;
	nvic.NVIC_IRQChannelSubPriority    = 1;
	nvic.NVIC_IRQChannelCmd  = ENABLE;
	
	//5.初始化NVIC
	
  NVIC_Init(&nvic);

	//6.配置中断服务函数  ----> 在启动文件----用的那个中断源就用那个中断服务函数
}

四、串口配置 

1. 配置GPIO并使能对应时钟

2. 配置串口

3. 配置串口中断

4. 配置NVIC中断控制器

5. 配置中断服务函数

#include "uart.h"
#include "stdio.h"

typedef struct __FILE FILE;

void uart_Init()
{
	GPIO_InitTypeDef txrx;
	USART_InitTypeDef uart;
	NVIC_InitTypeDef nvic;

	
	//1.GPIO配置并使能时钟

	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA,  ENABLE); 
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1,  ENABLE); 
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2); //配置NVIC优先级组--->在misc.h

	
	txrx.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出,因为txrx是复用端口
	txrx.GPIO_Pin  = GPIO_Pin_9;
	txrx.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOA,  &txrx); //初始化TX

	txrx.GPIO_Mode = GPIO_Mode_IN_FLOATING; //因为是接受,所以输入用浮空
	txrx.GPIO_Pin  = GPIO_Pin_10;
	//txrx.GPIO_Speed = GPIO_Speed_50MHz; //要不要都一样,因为速度是输出才用
	
	GPIO_Init(GPIOA,  &txrx); //初始化RX

	
	//2.配置串口
	
	uart.USART_BaudRate = 9600;
	uart.USART_WordLength  = USART_WordLength_8b;
	uart.USART_StopBits = USART_StopBits_1;
	uart.USART_Parity = USART_Parity_No;
	uart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	uart.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
  
	 USART_Cmd(USART1,ENABLE  ); //使能串口

	//3.初始化串口
	
	 USART_Init(USART1, &uart);

	//4.设置串口中断类型

	 USART_ITConfig(USART1, USART_IT_RXNE ,  ENABLE); //接收中断


	//5.配置NVIC
	
	nvic.NVIC_IRQChannel     = USART1_IRQn; //中断源---->在misc.c--->stm32f10x.h           
	nvic.NVIC_IRQChannelPreemptionPriority  = 1;
	nvic.NVIC_IRQChannelSubPriority    = 1;
	nvic.NVIC_IRQChannelCmd  = ENABLE;
		
     NVIC_Init(&nvic);
		
	
}


void sendBits(USART_TypeDef* USARTx, uint16_t Data) //发送一个字符
{
	
		 USART_SendData( USARTx,  Data);
		 while( USART_GetFlagStatus(USART1, USART_FLAG_TXE )!= SET); 
	
}

void sendStr(USART_TypeDef* USARTx, char *Data) //发送一个字符串
{
	while(*Data != '\0')
	{
		sendBits(USARTx,*Data);
		Data++;
		
	}
}
	

int fputc(int ch , FILE * p) //printf重定向----<把打印到显示屏的数据,改成打印到串口>
{
		USART_SendData( USART1,  ch);
		 while( USART_GetFlagStatus(USART1, USART_FLAG_TXE )!= SET);		
	
	
	return ch;
	
}
	
void USART1_IRQHandler() //6.中断服务函数
{
	char buf;
	
	if( ( USART_GetITStatus(USART1,  USART_IT_RXNE)) == SET) //等于SET就说明已经产生一个中断标志位
	{
		buf =  USART_ReceiveData( USART1);

	if(buf == 'o')
	{
		sendStr(USART1, "ok");
		printf("666"); //---<因为printf本身就可以携带字符串,所以执行fputc,是一位一位给SendData发送的>
		
		//while( USART_GetFlagStatus(USART1, USART_FLAG_TXE )!= SET); //USART_FLAG_TXE是发送完成标志,== 0,说明发送完成
	}

	}
	
	 USART_ClearFlag( USART1,USART_FLAG_TXE  );//清除中断标志
}

printf重定义 

就是把fputc这个函数原本打印到显示器的数据,给它改到串口

五、定时器

1. 定时器配置并使能时钟

2. 配置定时器中断 (T=(重装值)*(预分频系数)/72Mhz)

3. 配置NVIC中断控制器

4. 编写中断服务函数

时钟树 

ba0af313096f4bb683caddc001ece9e9.png

#include "time.h"
#include "uart.h"

void time_Init()
{
	
	TIM_TimeBaseInitTypeDef time;
	NVIC_InitTypeDef nvic;
  RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2,ENABLE  );
  NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2);

	 
	//1.定时器配置
		
	time.TIM_Prescaler  = 7200-1; //预分频系数
	time.TIM_Period     = 10000-1; //自动重装
	time.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频,设置定时器时钟CK_INT频率与数字滤波采样
	time.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
	
	//2.定时器初始化并使能
	
  TIM_TimeBaseInit( TIM2, &time);
	TIM_Cmd(TIM2,  ENABLE);
	
	//3.配置定时器中断
	
	 TIM_ITConfig( TIM2,  TIM_IT_Update,  ENABLE);


	//4.配置NVIC中断控制器
	
	nvic.NVIC_IRQChannel     = TIM2_IRQn; //中断源---->在misc.c--->stm32f10x.h           
	nvic.NVIC_IRQChannelPreemptionPriority  = 1;
	nvic.NVIC_IRQChannelSubPriority    = 1;
	nvic.NVIC_IRQChannelCmd  = ENABLE;
	
  NVIC_Init(&nvic);
	
	
}

	//5.中断服务函数

void TIM2_IRQHandler()
{
	 
	
	if(TIM_GetITStatus( TIM2, TIM_IT_Update ) == SET)
	 {
		 
		  TIM_ClearITPendingBit( TIM2, TIM_IT_Update );//清除定时器中断标志位
		
			
					USART_SendData( USART1,  'a');
					while( USART_GetFlagStatus(USART1, USART_FLAG_TXE )!= SET);
			
	 }
	 


}

systick定时器

1. 配置时钟源(可以72mhz/9mhz)

2. 配置重装值 

3. 向下递减到0触发中断退出定时器

4. 关闭定时器

#include "systick.h"

void Delay_ms(int ms) //最大24位
{
	int i;
	SysTick_Config(72000);
	
	for(i = 0; i < ms;i++ )
	{
		while(!(SysTick->CTRL)& 1<<16 );
		
	}
	
		SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;
	
}

void Delay_us(int us) //最大24位
{
	int i;
	SysTick_Config(72);//重装定时器值,t = 重装值*(1/72mhz),反过来讲7200就是,72000*(1/72MHz)=1/1000=1(ms)
	 
	for(i = 0; i < us;i++ )
	{
		while(!(SysTick->CTRL)& 1<<16 );//如果数到了0,则该为为1
		
	}
	
		SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk; //关闭计数器 置0
	
}

PWM模式

1 .使能时钟和使能AFIO时钟(因为需要引脚复用)

2. 调用函数进行引脚映射

3. GPIO配置 (这里我们用的定时器3通道2)

4. 定时器配置

5. pwm配置

6. pwm初始化,并使能pwm和使能rcc

 d421918434584778abe9530e3a1a30c8.jpeg

 ea1f60ffa25e4b018097b81c6b7d540e.png

#include "pwm.h"

void pwm_Init()
{
	GPIO_InitTypeDef dj;
	TIM_TimeBaseInitTypeDef time;
	TIM_OCInitTypeDef pwm;

	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,  ENABLE); //用于映射到这个引脚,打开这个引脚的时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,  ENABLE); //引脚复用,必须打开这个时钟
	RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM3,ENABLE  ); //使能定时器3的时钟
	
  GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3 , ENABLE ); //指定管脚映射

	//1.GPIO配置
	
	dj.GPIO_Mode = GPIO_Mode_AF_PP;
	dj.GPIO_Pin  = GPIO_Pin_5;        //*****用的定时器3通道2******
	dj.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init( GPIOB, &dj);
	
	//2.定时器配置
	
	time.TIM_Prescaler  = 7200-1; //自动重装
	time.TIM_Period     = 200-1; //预分频系数
	time.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频,设置定时器时钟CK_INT频率与数字滤波采样
	time.TIM_CounterMode = TIM_CounterMode_Up; //向上计数

  TIM_TimeBaseInit( TIM3, &time);
	
	//3.PWM配置
	
	pwm.TIM_OCMode = TIM_OCMode_PWM1;  //选择pwm1模式,如果定时器向上计数,一旦cnt<crr时,通道为有效电平,定时器向下则无效电平,pwm2则相反
	pwm.TIM_OutputState = TIM_OutputState_Enable; //使能比较输出,就是打开把输出的电平给到重映射的引脚上
	pwm.TIM_OCPolarity = TIM_OCPolarity_Low; //选择有效电平为低电平
	
	//4.pwm初始化并使能ccr并使能定时器3
	
	 TIM_OC2Init( TIM3, &pwm );
	 TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);
	 TIM_Cmd(TIM3,  ENABLE);

	 
	
}

#include "stm32f10x.h"
#include "delay.h"
#include "pwm.h"

int main()
{

	 pwm_Init();
		
	 TIM_SetCompare2(TIM3,  195); //设置比较值
	
		while(1)
		{
			 delay_ms(1000);
			 TIM_SetCompare2(TIM3,  175); //设置比较值,180°
			 delay_ms(1000);
			 TIM_SetCompare2(TIM3,  195); //设置比较值,0°
			 			 


		
		
		} 

}

DMA(直接寄存器访问)

DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输

我们知道CPU有转移数据、计算、控制程序转移等很多功能,系统运作的核心就是CPU,那我们可不可以减轻消耗cpu的资源呢?

有,我们可以直接让内存->外设、外设->内存,内存->内存,因为有dma,它不需要经过cpu,可以直接访问flash或SRAM。

f4cc9457be094549b6c7a5aad28cd230.png 83a953cdf89243449c33be1468715717.png

40753888afd14a4d80dc7b63aa5563b4.png

1. 配置DMA

2. 初始化/使能DMA

3. 使能串口DMA(这里用的内存->串口)

4. 获取DMA标志位、判断是否发送完成

/*
	
		内存->外设

*/

#include "dma.h"

void dma_Init(DMA_Channel_TypeDef* DMAy_Channelx,u32 *paddr,u32 *maddr,u16 size)
{
		DMA_InitTypeDef dma;
		RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,  ENABLE);
		
		//1.配置dma
		
		dma.DMA_PeripheralBaseAddr = (u32)paddr; //外设地址,是u32位,所以要强转
		dma.DMA_MemoryBaseAddr = (u32)maddr; //内存地址,是u32位
		dma.DMA_DIR = DMA_DIR_PeripheralDST; //传输方向,我们选择从内存到外设
		dma.DMA_BufferSize = size; //设置一次传输的大小,最大传输65536
	    dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //设置传输数据外设地址是否递增
		dma.DMA_MemoryInc = DMA_MemoryInc_Enable; //设置传输数据内存地址是否递增
		dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //设置外设的数据长度为多少字节传输
		dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //设置内存的数据长度为多少地址传输 
		dma.DMA_Mode = DMA_Mode_Normal; //设置模式,是循环发送,还是只发送一次
		dma.DMA_Priority = DMA_Priority_High; //设置dma的通道优先级,一共有低、中、高、很高4个优先级
		dma.DMA_M2M = DMA_M2M_Disable; //这是设置内存到内存的使能
	
		//2.初始化dma
	
		 DMA_Init(DMAy_Channelx , &dma); //用参数传递通道,提高使用性

}


void dma_Enable(DMA_Channel_TypeDef* DMAy_Channelx,uint16_t size)
{
	 DMA_Cmd(DMAy_Channelx,  DISABLE); //先失能
	 DMA_SetCurrDataCounter( DMAy_Channelx,  size); //设置数据传输量
	 DMA_Cmd(DMAy_Channelx,  ENABLE);  //使能

}
#include "stm32f10x.h"
#include "delay.h"
#include "dma.h"
#include "uart.h"

#define SIZE 2000

u8 maddr[SIZE]; //定义maddr

void maddr_Init(u8 * p)
{
	int i = 0;
	for(i = 0; i < SIZE;++i)
	{
		*p = '5';
		 p++;
	}
}

int main()
{
	 uart_Init();
	 dma_Init( DMA1_Channel4,(u32 *)&USART1->DR, (u32 *)maddr, SIZE); //初始化dma---USART1->DR---(串口的数据寄存器,也就是这里的外设地址)
		
	 maddr_Init(maddr); //给内存地址写值
	 USART_DMACmd( USART1,  USART_DMAReq_Tx,  ENABLE); //使能外设,并配置成发送
	 dma_Enable(DMA1_Channel4, SIZE); //使能dma1通道
	
	while(1)
	{
			 if(DMA_GetFlagStatus( DMA1_FLAG_TC4) == SET) //判断是否发送完成
			 {
				  DMA_ClearFlag( DMA1_FLAG_TC4); //发送完成清除标志
				 
					break; //打破while,只执行一次

			 }

	}
		

}

  • 2
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
STM32IO映射是指将GPIO复用为内置外设的功能引脚。每个GPIO都可以被配置为不同的功能,比如ADC、DAC、串口定时器等。当一个GPIO被配置为某个内置外设的功能引脚时,就称为复用。例如,当PA9和PA10被配置为串口1时,它们就是复用为串口功能的引脚。在配置IO映射时,需要进行一系列的设置,包括使能GPIO时钟、使能复用外设时钟、配置GPIO为复用功能,并将IO连接到所需的复用功能。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [STM32学习笔记之IO引脚复用和映射](https://blog.csdn.net/xiaolaoban0413/article/details/108367539)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [【STM32IO引脚复用器和映射原理与配置](https://blog.csdn.net/qq_44744164/article/details/106577986)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [STM32学习(正点原子23):IO引脚复用和映射](https://blog.csdn.net/zimengsss/article/details/120490597)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

No Iverson

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值