• 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

    1. /*-------led-------*/
    2. #include "led.h"
    3. void led_Init(void)
    4. {
    5. GPIO_InitTypeDef led;
    6. //1.时钟使能
    7. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC , ENABLE);
    8. //2.配置GPIO
    9. led.GPIO_Mode = GPIO_Mode_Out_PP ;
    10. led.GPIO_Pin = GPIO_Pin_13;
    11. led.GPIO_Speed = GPIO_Speed_50MHz;
    12. //3.初始化GPIO
    13. GPIO_Init(GPIOC, &led);
    14. }
    15. /*-------shake-------*/
    16. #include "shake.h"
    17. void shake_Init()
    18. {
    19. GPIO_InitTypeDef shake;
    20. //1.使能时钟
    21. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
    22. //2.配置GPIO
    23. shake.GPIO_Mode = GPIO_Mode_IPD;
    24. shake.GPIO_Pin = GPIO_Pin_4;
    25. shake.GPIO_Speed = GPIO_Speed_10MHz;
    26. //3.初始化GPIO
    27. GPIO_Init(GPIOA, &shake);
    28. }
    29. /*-------relay-------*/
    30. #include "relay.h"
    31. void relay_Init()
    32. {
    33. GPIO_InitTypeDef relay;
    34. //1.使能时钟
    35. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
    36. //2.配置GPIO
    37. relay.GPIO_Mode = GPIO_Mode_Out_PP;
    38. relay.GPIO_Pin = GPIO_Pin_3;
    39. relay.GPIO_Speed = GPIO_Speed_10MHz;
    40. //3.初始化GPIO
    41. GPIO_Init(GPIOA, &relay);
    42. }
    43. /*-------delay-------*/
    44. #include "delay.h"
    45. //us
    46. void delay_us(int delay_us)
    47. {
    48. volatile unsigned int num;
    49. volatile unsigned int t;
    50. for (num = 0; num < delay_us; num++)
    51. {
    52. t = 11;
    53. while (t != 0)
    54. {
    55. t--;
    56. }
    57. }
    58. }
    59. //ms
    60. void delay_ms( int delay_ms)
    61. {
    62. volatile unsigned int num;
    63. for (num = 0; num < delay_ms; num++)
    64. {
    65. delay_us(1000);
    66. }
    67. }

    三、外部中断配置

    1. 配置GPIO并使能时钟

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

    3. 配置外部中断

    4. 配置NVIC组

    5. 配置NVIC中断控制器

    6. 配置中断服务函数

    1. #include "exit.h"
    2. #include "shake.h"
    3. void exit_Init()
    4. {
    5. EXTI_InitTypeDef exti;
    6. NVIC_InitTypeDef nvic;
    7. //1.配置GPIO
    8. shake_Init();
    9. RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO, ENABLE); //打开中断用到的时钟
    10. GPIO_EXTILineConfig( GPIO_PortSourceGPIOA, GPIO_PinSource4 ); //把GPIO映射到对应的中断线---->在GPIO.H
    11. NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2); //配置NVIC优先级组--->在misc.h
    12. //2.配置外部中断
    13. exti.EXTI_Line = EXTI_Line4;
    14. exti.EXTI_Mode = EXTI_Mode_Interrupt;
    15. exti.EXTI_Trigger = EXTI_Trigger_Falling;
    16. exti.EXTI_LineCmd = ENABLE;
    17. //3.初始化外部中断
    18. EXTI_Init(&exti);
    19. //4.配置NVIC中断控制器
    20. nvic.NVIC_IRQChannel = EXTI4_IRQn; //中断源---->在misc.c--->stm32f10x.h
    21. nvic.NVIC_IRQChannelPreemptionPriority = 1;
    22. nvic.NVIC_IRQChannelSubPriority = 1;
    23. nvic.NVIC_IRQChannelCmd = ENABLE;
    24. //5.初始化NVIC
    25. NVIC_Init(&nvic);
    26. //6.配置中断服务函数 ----> 在启动文件----用的那个中断源就用那个中断服务函数
    27. }

    四、串口配置 

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

    2. 配置串口

    3. 配置串口中断

    4. 配置NVIC中断控制器

    5. 配置中断服务函数

    1. #include "uart.h"
    2. #include "stdio.h"
    3. typedef struct __FILE FILE;
    4. void uart_Init()
    5. {
    6. GPIO_InitTypeDef txrx;
    7. USART_InitTypeDef uart;
    8. NVIC_InitTypeDef nvic;
    9. //1.GPIO配置并使能时钟
    10. RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE);
    11. RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1, ENABLE);
    12. NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2); //配置NVIC优先级组--->在misc.h
    13. txrx.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出,因为txrx是复用端口
    14. txrx.GPIO_Pin = GPIO_Pin_9;
    15. txrx.GPIO_Speed = GPIO_Speed_50MHz;
    16. GPIO_Init(GPIOA, &txrx); //初始化TX
    17. txrx.GPIO_Mode = GPIO_Mode_IN_FLOATING; //因为是接受,所以输入用浮空
    18. txrx.GPIO_Pin = GPIO_Pin_10;
    19. //txrx.GPIO_Speed = GPIO_Speed_50MHz; //要不要都一样,因为速度是输出才用
    20. GPIO_Init(GPIOA, &txrx); //初始化RX
    21. //2.配置串口
    22. uart.USART_BaudRate = 9600;
    23. uart.USART_WordLength = USART_WordLength_8b;
    24. uart.USART_StopBits = USART_StopBits_1;
    25. uart.USART_Parity = USART_Parity_No;
    26. uart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    27. uart.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
    28. USART_Cmd(USART1,ENABLE ); //使能串口
    29. //3.初始化串口
    30. USART_Init(USART1, &uart);
    31. //4.设置串口中断类型
    32. USART_ITConfig(USART1, USART_IT_RXNE , ENABLE); //接收中断
    33. //5.配置NVIC
    34. nvic.NVIC_IRQChannel = USART1_IRQn; //中断源---->在misc.c--->stm32f10x.h
    35. nvic.NVIC_IRQChannelPreemptionPriority = 1;
    36. nvic.NVIC_IRQChannelSubPriority = 1;
    37. nvic.NVIC_IRQChannelCmd = ENABLE;
    38. NVIC_Init(&nvic);
    39. }
    40. void sendBits(USART_TypeDef* USARTx, uint16_t Data) //发送一个字符
    41. {
    42. USART_SendData( USARTx, Data);
    43. while( USART_GetFlagStatus(USART1, USART_FLAG_TXE )!= SET);
    44. }
    45. void sendStr(USART_TypeDef* USARTx, char *Data) //发送一个字符串
    46. {
    47. while(*Data != '\0')
    48. {
    49. sendBits(USARTx,*Data);
    50. Data++;
    51. }
    52. }
    53. int fputc(int ch , FILE * p) //printf重定向----<把打印到显示屏的数据,改成打印到串口>
    54. {
    55. USART_SendData( USART1, ch);
    56. while( USART_GetFlagStatus(USART1, USART_FLAG_TXE )!= SET);
    57. return ch;
    58. }
    59. void USART1_IRQHandler() //6.中断服务函数
    60. {
    61. char buf;
    62. if( ( USART_GetITStatus(USART1, USART_IT_RXNE)) == SET) //等于SET就说明已经产生一个中断标志位
    63. {
    64. buf = USART_ReceiveData( USART1);
    65. if(buf == 'o')
    66. {
    67. sendStr(USART1, "ok");
    68. printf("666"); //---<因为printf本身就可以携带字符串,所以执行fputc,是一位一位给SendData发送的>
    69. //while( USART_GetFlagStatus(USART1, USART_FLAG_TXE )!= SET); //USART_FLAG_TXE是发送完成标志,== 0,说明发送完成
    70. }
    71. }
    72. USART_ClearFlag( USART1,USART_FLAG_TXE );//清除中断标志
    73. }

    printf重定义 

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

    五、定时器

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

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

    3. 配置NVIC中断控制器

    4. 编写中断服务函数

    时钟树 

    ba0af313096f4bb683caddc001ece9e9.png

    1. #include "time.h"
    2. #include "uart.h"
    3. void time_Init()
    4. {
    5. TIM_TimeBaseInitTypeDef time;
    6. NVIC_InitTypeDef nvic;
    7. RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2,ENABLE );
    8. NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2);
    9. //1.定时器配置
    10. time.TIM_Prescaler = 7200-1; //预分频系数
    11. time.TIM_Period = 10000-1; //自动重装
    12. time.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频,设置定时器时钟CK_INT频率与数字滤波采样
    13. time.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
    14. //2.定时器初始化并使能
    15. TIM_TimeBaseInit( TIM2, &time);
    16. TIM_Cmd(TIM2, ENABLE);
    17. //3.配置定时器中断
    18. TIM_ITConfig( TIM2, TIM_IT_Update, ENABLE);
    19. //4.配置NVIC中断控制器
    20. nvic.NVIC_IRQChannel = TIM2_IRQn; //中断源---->在misc.c--->stm32f10x.h
    21. nvic.NVIC_IRQChannelPreemptionPriority = 1;
    22. nvic.NVIC_IRQChannelSubPriority = 1;
    23. nvic.NVIC_IRQChannelCmd = ENABLE;
    24. NVIC_Init(&nvic);
    25. }
    26. //5.中断服务函数
    27. void TIM2_IRQHandler()
    28. {
    29. if(TIM_GetITStatus( TIM2, TIM_IT_Update ) == SET)
    30. {
    31. TIM_ClearITPendingBit( TIM2, TIM_IT_Update );//清除定时器中断标志位
    32. USART_SendData( USART1, 'a');
    33. while( USART_GetFlagStatus(USART1, USART_FLAG_TXE )!= SET);
    34. }
    35. }

    systick定时器

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

    2. 配置重装值 

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

    4. 关闭定时器

    1. #include "systick.h"
    2. void Delay_ms(int ms) //最大24位
    3. {
    4. int i;
    5. SysTick_Config(72000);
    6. for(i = 0; i < ms;i++ )
    7. {
    8. while(!(SysTick->CTRL)& 1<<16 );
    9. }
    10. SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;
    11. }
    12. void Delay_us(int us) //最大24位
    13. {
    14. int i;
    15. SysTick_Config(72);//重装定时器值,t = 重装值*(1/72mhz),反过来讲7200就是,72000*(1/72MHz)=1/1000=1(ms)
    16. for(i = 0; i < us;i++ )
    17. {
    18. while(!(SysTick->CTRL)& 1<<16 );//如果数到了0,则该为为1
    19. }
    20. SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk; //关闭计数器 置0
    21. }

    PWM模式

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

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

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

    4. 定时器配置

    5. pwm配置

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

     d421918434584778abe9530e3a1a30c8.jpeg

     ea1f60ffa25e4b018097b81c6b7d540e.png

    1. #include "pwm.h"
    2. void pwm_Init()
    3. {
    4. GPIO_InitTypeDef dj;
    5. TIM_TimeBaseInitTypeDef time;
    6. TIM_OCInitTypeDef pwm;
    7. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //用于映射到这个引脚,打开这个引脚的时钟
    8. RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //引脚复用,必须打开这个时钟
    9. RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM3,ENABLE ); //使能定时器3的时钟
    10. GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3 , ENABLE ); //指定管脚映射
    11. //1.GPIO配置
    12. dj.GPIO_Mode = GPIO_Mode_AF_PP;
    13. dj.GPIO_Pin = GPIO_Pin_5; //*****用的定时器3通道2******
    14. dj.GPIO_Speed = GPIO_Speed_50MHz;
    15. GPIO_Init( GPIOB, &dj);
    16. //2.定时器配置
    17. time.TIM_Prescaler = 7200-1; //自动重装
    18. time.TIM_Period = 200-1; //预分频系数
    19. time.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频,设置定时器时钟CK_INT频率与数字滤波采样
    20. time.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
    21. TIM_TimeBaseInit( TIM3, &time);
    22. //3.PWM配置
    23. pwm.TIM_OCMode = TIM_OCMode_PWM1; //选择pwm1模式,如果定时器向上计数,一旦cnt
    24. pwm.TIM_OutputState = TIM_OutputState_Enable; //使能比较输出,就是打开把输出的电平给到重映射的引脚上
    25. pwm.TIM_OCPolarity = TIM_OCPolarity_Low; //选择有效电平为低电平
    26. //4.pwm初始化并使能ccr并使能定时器3
    27. TIM_OC2Init( TIM3, &pwm );
    28. TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);
    29. TIM_Cmd(TIM3, ENABLE);
    30. }
    1. #include "stm32f10x.h"
    2. #include "delay.h"
    3. #include "pwm.h"
    4. int main()
    5. {
    6. pwm_Init();
    7. TIM_SetCompare2(TIM3, 195); //设置比较值
    8. while(1)
    9. {
    10. delay_ms(1000);
    11. TIM_SetCompare2(TIM3, 175); //设置比较值,180°
    12. delay_ms(1000);
    13. TIM_SetCompare2(TIM3, 195); //设置比较值,0°
    14. }
    15. }

    DMA(直接寄存器访问)

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

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

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

    f4cc9457be094549b6c7a5aad28cd230.png 83a953cdf89243449c33be1468715717.png

    40753888afd14a4d80dc7b63aa5563b4.png

    1. 配置DMA

    2. 初始化/使能DMA

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

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

    1. /*
    2. 内存->外设
    3. */
    4. #include "dma.h"
    5. void dma_Init(DMA_Channel_TypeDef* DMAy_Channelx,u32 *paddr,u32 *maddr,u16 size)
    6. {
    7. DMA_InitTypeDef dma;
    8. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    9. //1.配置dma
    10. dma.DMA_PeripheralBaseAddr = (u32)paddr; //外设地址,是u32位,所以要强转
    11. dma.DMA_MemoryBaseAddr = (u32)maddr; //内存地址,是u32位
    12. dma.DMA_DIR = DMA_DIR_PeripheralDST; //传输方向,我们选择从内存到外设
    13. dma.DMA_BufferSize = size; //设置一次传输的大小,最大传输65536
    14. dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //设置传输数据外设地址是否递增
    15. dma.DMA_MemoryInc = DMA_MemoryInc_Enable; //设置传输数据内存地址是否递增
    16. dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //设置外设的数据长度为多少字节传输
    17. dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //设置内存的数据长度为多少地址传输
    18. dma.DMA_Mode = DMA_Mode_Normal; //设置模式,是循环发送,还是只发送一次
    19. dma.DMA_Priority = DMA_Priority_High; //设置dma的通道优先级,一共有低、中、高、很高4个优先级
    20. dma.DMA_M2M = DMA_M2M_Disable; //这是设置内存到内存的使能
    21. //2.初始化dma
    22. DMA_Init(DMAy_Channelx , &dma); //用参数传递通道,提高使用性
    23. }
    24. void dma_Enable(DMA_Channel_TypeDef* DMAy_Channelx,uint16_t size)
    25. {
    26. DMA_Cmd(DMAy_Channelx, DISABLE); //先失能
    27. DMA_SetCurrDataCounter( DMAy_Channelx, size); //设置数据传输量
    28. DMA_Cmd(DMAy_Channelx, ENABLE); //使能
    29. }
    1. #include "stm32f10x.h"
    2. #include "delay.h"
    3. #include "dma.h"
    4. #include "uart.h"
    5. #define SIZE 2000
    6. u8 maddr[SIZE]; //定义maddr
    7. void maddr_Init(u8 * p)
    8. {
    9. int i = 0;
    10. for(i = 0; i < SIZE;++i)
    11. {
    12. *p = '5';
    13. p++;
    14. }
    15. }
    16. int main()
    17. {
    18. uart_Init();
    19. dma_Init( DMA1_Channel4,(u32 *)&USART1->DR, (u32 *)maddr, SIZE); //初始化dma---USART1->DR---(串口的数据寄存器,也就是这里的外设地址)
    20. maddr_Init(maddr); //给内存地址写值
    21. USART_DMACmd( USART1, USART_DMAReq_Tx, ENABLE); //使能外设,并配置成发送
    22. dma_Enable(DMA1_Channel4, SIZE); //使能dma1通道
    23. while(1)
    24. {
    25. if(DMA_GetFlagStatus( DMA1_FLAG_TC4) == SET) //判断是否发送完成
    26. {
    27. DMA_ClearFlag( DMA1_FLAG_TC4); //发送完成清除标志
    28. break; //打破while,只执行一次
    29. }
    30. }
    31. }

  • 相关阅读:
    一文读懂Diffusion model
    深入了解 Axios 的 put 请求:使用技巧与最佳实践
    tortoise 快捷操作分支合并
    springboot+vue3+微信小程序记账本源码
    【Linux operation 42】Linux 系统的时间
    代码随想录 Day7 字符串1 LeetCode T344反转字符串 T541 反转字符串II 151翻转字符串的单词
    蓝桥杯嵌入式总结
    IOT设备情况数据分析
    图像去噪滤波算法汇总(Python)
    新移科技发布基于联发科MT8390(Genio 700)平台的物联网 AI 核心板
  • 原文地址:https://blog.csdn.net/lijianhua003/article/details/126554887