精华内容
下载资源
问答
  • 没写博客感悟:昨天没有写博客,...今天主要总结和复习三个知识点,NVIC中断优先级管理、串口通信、及串口通信简单配置实例: 第一部分: NVIC中断优先级管理: 首先是中断分组,我们知道所用CM3内核支持

    没写博客的感悟:昨天没有写博客,今天就倒霉了,得写两篇,果然不能偷懒,当天没有做的事,无论如何你都得要做,为了改掉这个拖延的小毛病,给自己定了一条规则,无论多晚,哪怕没有网没有电也得写完每天更新的博客,以此勉励。

    今天主要总结和复习三个知识点,NVIC中断优先级管理、串口通信、及串口通信的简单配置实例:

    第一部分:

    NVIC中断优先级管理:

    首先是中断分组,我们知道所用的CM3内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。然而STM32并没有使用CM3内核的全部东西,而是只用了它的一部分。STM32有84个中断,包括16个内核中断和68个可屏蔽中断,具有16级可编程的中断优先级。而我所用的STM32F103系列上面,又只有60个可屏蔽中断(在107系列才有68个),而STM系列把中断分为5个组,如下图一所示

    图一

    这么多大概60个中断如何管理,这是我一开始想到的问题,当然是用寄存器进行管理,有七组寄存器对所有的中断进行管理,中断寄存器分别如下,引用MDK对寄存器组的分类:

    typedef struct
    {
      __IO uint32_t ISER[8];                   //中断使能寄存器组:设置为:8个32位寄存器来控制256个中断,由于我用的只有60个故只需设置ISER[0]-ISER[2]即可
           uint32_t RESERVED0[24];                                   
      __IO uint32_t ICER[8];                      //中断除能寄存器组:设置和中断使能一样
           uint32_t RSERVED1[24];                                    
      __IO uint32_t ISPR[8];                      //中断挂起寄存器组:设置和中断使能一样,通过置一把正在执行的中断挂起从而执行同级别或更高级别的中断,写0无效
           uint32_t RESERVED2[24];                                   
      __IO uint32_t ICPR[8];                      //中断解挂控制寄存器组:设置和中断使能一样作用和挂起寄存器组相反
           uint32_t RESERVED3[24];                                   
      __IO uint32_t IABR[8];                   //   中断激活标志位寄存器组:这是一个只读寄存器,如果置一可以知道该位所对应的正在运行的中断,运行完毕由硬件自动清零
           uint32_t RESERVED4[56];                                   
      __IO uint8_t  IP[240];                   //  中断优先级控制寄存器组:这是非常重要的一个寄存器,总共有240个8位的寄存器组成,每8位代表一个中断,而每八位只用了其高八位,我所用的103系列只用了0-//67即可,详细的会在下面给出
           uint32_t RESERVED5[644];                                  
      __O  uint32_t STIR;                      // 软件触发中断寄存器组
    }  NVIC_Type;      

    中断优先级控制寄存器组,说这个之前得明白一个概念,优先级概念,STM系列的中断优先级分为两级,抢占优先级和响应优先级。而又得知道两者的区别,其中区别如下:

    1、高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
    2、抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断
    3、抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
    4、如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
    抢占优先级和响应优先级中,数字越小说明级别越大。举个例子,假定设置中断优先级组为2,然后设置中断1(RTC中断)的抢占优先级为2,响应优先级为1。  中断6(外部中断0)的抢占优先级为3,响应优先级为0。中断4(外部中断1)的抢占优先级为2,响应优先级为0。则中断优先级为中断4>中断1>中断6.
    在详细弄懂了上述所有知识后,就可以设置自己我们自己的中断,具体步骤如下:
    系统运行后先设置中断优先级分组。调用函数:

    voidNVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);/

     整个系统执行过程中,只设置一次中断分组。

    ②针对每个中断,设置对应的抢占优先级和响应优先级:

    voidNVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

    ③ 如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。

    第二部分:串口通信基本原理
    这个复习得从通信的背景开始阐述,处理器与外部的通信分为两种方式并行和串行方式:并行的优点是速度快缺点为占用IO口较多,串行则速度慢优点为占用的IO口较少。串行通信按照传输方向又有三种方式,单工,半双工,全双工如图二所示:

    图二

    看完图应该就很清晰了,再解释一下
    1、单工:数据传输只支持数据在一个方向上传输
    2、半双工:允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;
    3、全双工:允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。 
    按照是否带时钟分为同步和异步,其中STM32的串口通信接口,UART:通用异步收发器USART:通用同步异步收发器
    我所用的板子中的大概电路如图三:

    图三

    STM32串口异步通信主要的参数定义为:起始位,数据位,硬件流位,停止位,波特率设置。举个例子如图四:

    图四


    第三部分:
    下面进行第三部分,实例演示,在这之前得明白所用的三个寄存器
    1、USART_SR状态寄存器
    2、USART_DR数据寄存器
    3、USART_BRR波特率寄存器
    而我们在运用库函数编写串口代码过程一般会运用到下面的几个库函数:

    voidUSART_Init();//串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能

    voidUSART_Cmd();//使能串口

    voidUSART_ITConfig();//使能相关中断

    voidUSART_SendData();//发送数据到串口,DR

    uint16_tUSART_ReceiveData();//接受数据,从DR读取接受到的数据

    FlagStatusUSART_GetFlagStatus();//获取状态标志位

    voidUSART_ClearFlag();//清除状态标志位

    ITStatusUSART_GetITStatus();//获取中断状态标志位

    voidUSART_ClearITPendingBit();//清除中断状态标志位

    其中提出一点波特率的计算方式如图5所示:

    图五

    最后就可以进行实例操作:总结步骤如下:
    ①串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();
    ②串口复位:USART_DeInit(); 这一步不是必须的
    ③GPIO端口模式设置:GPIO_Init(); 模式设置为GPIO_Mode_AF_PP
    ④串口参数初始化:USART_Init();
    ⑤开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)

          NVIC_Init();

          USART_ITConfig();

    ⑥使能串口:USART_Cmd();

    ⑦编写中断处理函数:USARTx_IRQHandler();

    ⑧串口数据收发:

    void USART_SendData();//发送数据到串口,DR

    uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据

    ⑨串口传输状态获取:

    FlagStatusUSART_GetFlagStatus(USART_TypeDef* USARTx,uint16_t USART_FLAG);

    void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

    编写得主函数代码如下:

    #include "stm32f10x.h"
    //串口通信实验1
    void my_usart_Init(void)
    {
    GPIO_InitTypeDef GPIO_InitStructA;
    USART_InitTypeDef USART_InitStructA;
    NVIC_InitTypeDef NVIC_InitStructA;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);//enable GPIOA和USART1 的时钟
    //初始化GPIOA,查表可知串口1发送模式下是全双工,GPIO口设置为推挽复用模式
    GPIO_InitStructA.GPIO_Mode=GPIO_Mode_AF_PP;
    GPIO_InitStructA.GPIO_Pin=GPIO_Pin_9;
    GPIO_InitStructA.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructA); 
    //初始化GPIOA,查表可知串口1接受模式下是全双工,GPIO口设置为浮空输入模式
    GPIO_InitStructA.GPIO_Mode=GPIO_Mode_IN_FLOATING;
    GPIO_InitStructA.GPIO_Pin=GPIO_Pin_10;
    GPIO_InitStructA.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructA); 
    //初始化串口参数
    USART_InitStructA.USART_BaudRate=115200;
    USART_InitStructA.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
    USART_InitStructA.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;
    USART_InitStructA.USART_Parity=USART_Parity_No;
    USART_InitStructA.USART_StopBits=USART_StopBits_1;
    USART_InitStructA.USART_WordLength=USART_WordLength_8b;
    USART_Init(USART1,&USART_InitStructA);

    //开启接受中断且初始化NVIC
    NVIC_InitStructA.NVIC_IRQChannel=USART1_IRQn;
    NVIC_InitStructA.NVIC_IRQChannelCmd=ENABLE;
    NVIC_InitStructA.NVIC_IRQChannelPreemptionPriority=1;
    NVIC_InitStructA.NVIC_IRQChannelSubPriority=1;
    NVIC_Init(&NVIC_InitStructA);

    USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//设置串口状态
    USART_Cmd(USART1, ENABLE);                    //使能串口1 
    }
    void USART1_IRQHandler(void)
    {
    u8 res;
    if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE))//由于有可能开启了很多中断故需要判断是否是我们所要的中断函数
    {
    res=USART_ReceiveData(USART1);//接受来自串口一的数据
    USART_SendData(USART1,res);//
    }
    }
     int main(void)
     {
       NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    my_usart_Init();
    while(1);
    //运行以后就一直在主函数等待,当电脑给单片机发送数据时就跳到中断
     
     }
    由于我没有JLINK在线调试,故用USB直接下载到单片机进行调试,最后用串口助手可以得到所设想达到的输出:结果如图六


    图六


    其中白色的发送是先经过PC先发送给单片机,如然后单片机又发送给PC由串口助手在打印得出;



    展开全文
  • 串口中断接收通信实验 熟悉MCU异步串行通信(SCI)工作原理。 掌握SCI通信编程方法。
  • (一)项目任务ü 任务1:用定时方式控制LEDn 子任务1.1 两种定期器认识n 子任务1.2 掌握定期器定时原理和计算方法n 子任务1.3 采用定时器方法实现点亮LED功能ü 任务2:采用按键扫描控制LEDn 子任务2.1 C语言--...

    (一)项目任务

    ü 任务1:用定时方式控制LED

    n 子任务1.1 两种定期器的认识

    n 子任务1.2 掌握定期器的定时原理和计算方法

    n 子任务1.3 采用定时器方法实现点亮LED功能

    ü 任务2:采用按键扫描控制LED

    n 子任务2.1 C语言---swtich语句认识

    n 子任务2.2 中断系统的认识

    n 子任务2.3 采用定时和中断方式驱动按键

    (二)相关知识点

    2.1、定时器的基本概念

    标准的51单片机有T0和T1两个定时器,52单片机多一个T2定时器,其余跟51单片机一样,有细心的读者会发现项目中用的是52单片机,名称却为51单片机,通常情况下把51和52单片机统称为51单片机。

    举个例子说明定时器工作的基本原理,假设一个水瓶的容量为65536ml,现在以1ml/s的速度往水瓶里滴水,那么需要65536s才能将水瓶滴满,再滴一滴水就会溢出。从中得到的启发是只要速度一定,可以根据滴的次数可以知道时间。假设现在需要定时30000s,只需往空瓶子里滴30000滴水即可,但是这样的话需要一直盯着水滴数。其实还有更好的办法,可以预先往水瓶里滴35536ml的水,再往水瓶里滴水,只要发现水瓶里的水溢出表示30000s的时间到了,定时器的工作方式亦是如此,使用定时器前首先了解相关寄存器。

    33b2dc77945c3f433c86e6ff53a52998.png

    章项目都以配置定时器T0为例,首先看下TCON寄存器各个位的含义。

    5a1712f4d056116a28879da34709209c.png

    TF1:定时器/计数器T1溢出标志。T1被允许计数以后,从初值开始加1计数。当最高位产生溢出时由硬件置“1”,向CPU请求中断,一直保持到CPU响应中断时,才由硬件清“0”(TF1也可由程序查询清“0”)。

    TR1:定时器T1的运行控制位。该位由软件置位和清零。TR1=1时就允许T1开始计数,TR1=0时禁止T1计数。

    TF0:同理与TF1,TF1是针对定时器/计数器T1,TF0是针对定时器/计数器T0。

    TR0:同理与TR1,TR1是针对定时器T1,TF0是针对定时器T0。

    5ae9cc90e620ddaa3f9c7bc0f86071cd.png
    表3 寄存器TMOD各位功能描述(不可位寻址)

    0d13d263b676ad8db14f149dd610e20a.png

    常用的模式有模式1和模式2,模式2常用在串口通信中,本项目中以模式1为主。注意寄存器TCON可以寻址,寄存器TMOD不可位寻址。寻址的意思是可以单独操作寄存器中的某一位,例如寄存器TCON中的第四位TR0可以取出来单独赋值,进行TR0 = 1或者TR0 = 0操作,但寄存器TMOD不可单独操作某个位,只能进行整体赋值,例如TMOD = 0x01。

    定时器内容较为丰富,建议大家阅读官方数据手册。本节部分内容摘自该手册定时器部分,本节只列出了定时器需要用到的寄存器,仅供参考。

    采用查询法配置定时器的步骤:

    1、通过配置定时器TMOD确定定时模式;

    2、将计算得到的初值装载到TH0和TL0;

    3、通过配置TR0启动定时器0;

    4、判断标志位TF0,如TF0为1表示溢出,可以通过软件方式清零再重新进行监测。

    2.2、定时器的定时计算

    时钟周期定义为时钟脉冲的倒数(时钟周期就是单片机外接晶振的倒数,例如12M的晶振,它的时间周期就是1/12000000),是计算机中最基本的、最小的时间单位。

    机器周期是指完成一个基本操作所需要的时间。机器周期主要针对汇编语言而言,在汇编语言下程序的每一条语句执行所使用的时间都是机器周期的整数倍,而且语句占用的时间是可以计算出来,而C语言一条语句的时间是确定的。51单片机系列在其标准架构下一个机器周期是12个时钟周期,也就是12/11059200秒。现在有不少增强型的51单片机,其速度都比较快,有的1个机器周期等于4个时钟周期,有的1个机器周期就等于1个时钟周期,也就是说大体上其速度可以达到标准51架构的3倍或12倍。因为项目中使用的是标准的51单片机,机器周期是12个时钟周期。

    再讲解下定时器和计数器这两个概念。定时器和计数器是单片机内部的同一个模块,通过配置SFR(特殊功能寄存器)可以实现两种不同的功能,大多数情况下是使用定时器功能,因此也主要来讲定时器功能,计数器功能大家自己了解下即可

    配套的单片机学习版采用的12M的无源晶振,那么时钟周期为1/12000000,机器周期为12/12000000,假设定时20ms,那么需要的机器周期为0.02/(12/12000000)=20000个,根据2.1小结讲解的需要在定时器预装65536-20000=45536个值,因为采用的定时模式1,将45536化为十六进制装载到寄存器TH0和TL0中,45536 / 256=177,45536 % 256=244,将177转化为十六进制0XB1装载到TH0,将244转化为十六进制0XF4装载到TL0。

    同理可以计算得16位寄存器最大能定时65536*(12/12000000)≈65ms,一般来说定时的时间尽量取在0~65ms中间值,如本例中定时器配置的定时20ms,如需定时1s,只要定时循环50次即可。读者可以计算下定时50ms需要多大的装载初值。

    注意:当采用11.0592M晶振时,采用类似的计算方式!

    2.3、C语言-----“|”,“&”

    对不可寻址的寄存器需要同时处理8个位,例如采用定时器0的模式1(M1=0,M0=1),配置寄存器TMOD=0x01,改配置虽然正确配置了第0、1位,同时也改变了第2到第7位,因此希望通过某种操作方式只改变第0、1位。在实际操作过程中采用TMOD=TMOD&0XFC,TMOD= TMOD|0X01,看似更为复杂,其实逻辑更为严谨。

    运算符“|”表示“按位或”,例如“1|1”为1,“1|0”为0,“0|1”为1,“0|0”为0,可见1“或”任何数都为1,0“或”0才为0。

    运算符“&”表示“按位与”,例如“1&1”为1,“1&0”为0,“0&1”为0,“0&0”为0,可见0“与”任何数都为0,1“与”1才为1。

    2.4、C语言-----swtich语句

    在项目三中采用if else语句用于条件的判断,如果条件较多的情况一般采用swtich语句,如下所示。在switch语句中有四个关键字,分别为switch、case、default和break。当switch后的表达式满足某个case后的常量表达式时,运行该case以后的语句块。注意每个case后面都需要break,表示跳出swtich语句。default表示当表达式没有匹配的case时,运行default后的语句块。

    switch(表达式)
    {
    case常量表达式1: 执行语句;break;
    case常量表达式2: 执行语句;break;
    case常量表达式3: 执行语句;break;
    case常量表达式4: 执行语句;break;
    default: 执行语句;break;
    }

    2.5、中断系统

    CPU正在运行时,外界发生了紧急事件请求,CPU暂停当前工作,处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。向CPU请求中断的源称为中断源。中断系统一般允许多个中断源,当几个中断源同时向CPU请求中断,有个优先响应的问题,在本项目中只涉及到定时器0和定时器1的中断,详细请参看芯片手册中P156。

    通过一个例子帮助读者理解中断含义,比如小明在家看电视,突然听到有敲门声,按下电视遥控暂停键去开门,开完门后继续看电视,这就是中断也是最常用的中断形式。还是小明在家看电视,突然听到有敲门声,按下电视遥控的暂停键再去开门,刚走到门口突然听到厨房里水开的声音,急忙跑到厨房把电源关了,再去开门,开完门后继续看电视,这是嵌套中断,在 “开门”这个中断源里又有“关电源”这个中断源,小明会根据事重缓急优先处理各个事件,单片机也是如此。

    bd37651de0e9276005ee417e6bc819f9.png
    图1 中断优先查询次序

    b1ad206b04e50b1251eea196b45c444e.png
    图2 C语言优先中断查询函数

    采用中断系统需编写中断函数,如3.2小节采用定时器0,中断函数为“void InterruptTimer0() interrupt 1”,它的格式为“void 变量名() interrupt 1”,变量名后的括号里 “void”可写可不写,其余部分要按照规范写,如图2所示一般将中断函数放在程序的末尾,函数名不需要进行声明!

    表5 中断允许寄存器(可位寻址)

    c543f3e33fb3752fd1f5da914d55b76e.png

    如表5所示,介绍常用的控制位。

    EA:CPU的总中断允许控制位,EA=1,使能总中断,EA=0,关闭总中断。

    ET0:T0的溢出中断允许位。ET0=1,允许T0中断;ET0=0禁止T0中断。

    ES:串行口1中断允许位。ES=1,允许串行口1中断;ES=0,禁止串行口1中断。

    IE中断允许寄存器在本项目中只要用到了第一、七位,定时器常配合中断使用,因此要使能EA和ET0位,同时也介绍了第四位ES,因为在串口中断中也会用到。

    2.6、全局变量和局部变量

    全局变量:在所有函数外部定义的变量称为全局变量,它的作用域贯穿整个程序。全局变量定义必须在所有函数之外。在项目3第3.2小结程序中定义的table1[15],变量定义在主函数和子函数之外。在同一源文件中,允许全局变量和局部变量同名,在局部变量作用域内,同名的全局变量不起作用。

    局部变量:函数内部的变量称为局部变量,它的作用域仅限于函数内部,该函数结束后里面的变量也无效了。不同函数里面局部变量可以同名,分配不同的存放单元,互不相干,对于初学者编者建议尽量使用不同变量名。

    由于定义全局变量会永久占用单片机内存单元,局部变量只有在具有该变量的函数使用时才会占用内存,使用完该函数后会自动释放内存,因此编者建议尽量使用局部变量。

    在本项目3.2小节Key_Scan()函数中有“static unsigned char cnt1=0xFF”语句,把“cnt1”叫做静态局部变量,若没有“static”这个关键字,“unsigned char cnt1=0xFF”语句中的“cnt1”叫做局部变量。举例说明,变量cnt1在Key_Scan()函数作用后变成0xAA,那么Key_Scan()被调用完成后释放内存空间,再下次调用Key_Scan()时,变量cnt1又恢复到初值0xFF,但是我们不希望变成初值,而是希望执行上一次函数执行结束后的值0xAA,那么在不使用全局变量的情况下可以通过添加“static”关键字实现。

    (三)设计实施

    3.1、查询法定时

    #include <reg52.h>
    sbit LED=P0^4;
    void main()
    {	
    	unsigned char cnt=0;
    	TMOD &= 0XFC; // TMOD= TMOD&0XFC 可以简写成TMOD &= 0XFC
    	TMOD |= 0X01;	// TMOD= TMOD|0X01 可以简写成TMOD |= 0X01
            TH0 = 0XB1 ; //定时20ms
    	TL0 = 0XF4;
    	TR0 = 1;  //启动定时器,TR为Time Run
    	 while(1)
    	 {
    		  if(1 == TF0)  //检测标志位是否为1,TF为Time Flag
    			{
    				TF0 = 0;//标志位要及时复位,方便下次计时
    				TH0 = 0XB1;//重新加载初值
    			        TL0 = 0xF4;
    				cnt++;
    				if(cnt >= 50)
    				{
    					cnt=0;
    					LED=~LED;
    				}		
    			} 
    	 }
    }
    

    ① 在项目二中采用delay函数延时的方式来闪烁LED,虽然跟本小节的实验现象一致,但是有根本性区别。delay函数是在延时过程中占用了大量CPU资源,因此不能进行其他函数运行,如果模块较多会造成信号堵塞,影响其他功能运行。定时器是由独立的模块运行,不占有CPU资源,可以让单片机做更多的事,因此在以后项目中尽量采用定时器定时。

    ② 定时器模式1在发生溢出后会自动清空初值,因此在每次计数时要重新加载初值;

    ③ “1==TF0”也可以写成“TF0==1”,因为该语句的含义是判断TF0是否为1,如果写成

    “TF0==1”方式,有可能写成“TF0=1”,这样的话程序不会报错,但是语句是有问题的;如果写成“1 ==TF0”,那么如果漏了一个等号写成“1 =TF0”,程序会直接报错,这种方式方便读者查错。

    ④ 如果采用变量进行计数,如小节中的cnt,一定要及时清零;

    ⑤ TMOD、TH0、TR0等一些定时器相关的变量都在头文件中进行过定义,因此不需要再重新定义;

    3.2、按键中断法

    #include <reg52.h>
    sbit LED1 = P0^4;
    sbit LED2 = P0^5;
    sbit KEY1 = P3^4;
    sbit KEY2 = P3^5;
    unsigned char KeyNum=0;   
    unsigned char KeyLock1=0; //按键1的标志位
    unsigned char KeyLock2=0; //按键2的标志位
    void KEY_Scan();
    void KEY_Action();
    void main()
    {	
    	EA = 1;//打开总中断
    	TMOD &= 0XFC;
    	TMOD |= 0X01;
    	TH0 = (65536-2000)/256;//定时2ms
    	TL0 = (65536-2000)%256;
    	ET0 = 1;
    	TR0 = 1;
    	 while(1)
    	 {
    		  KEY_Action();
    	 }
    }
    void KEY_Scan()
    {
    	static unsigned char cnt1=0xFF;
    	static unsigned char cnt2=0xFF;
      	cnt1 =(cnt1<<1)|KEY1;//a语句
    	cnt2 =(cnt2<<1)|KEY2;
    	if(cnt1 != 0x00) // b语句
    	{
    		KeyLock1=0;//c语句
    	}
    	else if(KeyLock1==0)// 检测到按键按下状态且第一次按下
    	{
    	  	KeyNum = 1;
    		KeyLock1=1;// 防止重复触发
    	}
    	if(cnt2 != 0x00)
    	{
    		KeyLock2=0;
    	}
    	else if(KeyLock2==0)
    	{
    	  	KeyNum = 2;
    		KeyLock2=1;
    	}
    }
    void KEY_Action()
    {
    	 switch(KeyNum)
    	 {
    	    case 1:LED1 =!LED1; KeyNum = 0;break;
    	    case 2:LED2 =!LED2; KeyNum = 0;break;
    	   	default:break;          
    	  }
    }
    void InterruptTimer0() interrupt 1
    {
        TH0 = (65536-2000)/256;
    	TL0 = (65536-2000)%256;
    	KEY_Scan();
    }
    

    ① 按键操作采用定时中断的方式,有效避免了delay函数占用CPU资源,在以后的程序中推荐使用这种方式;

    ② 按键定时中断的原理是将按键部分分为两部分,一部分是按键扫描KEY_Scan()在中断函数中每隔2ms进行扫描,另一部分是按键函数KEY_Action()在while函数中不停运行等待指令执行,当KEY_Scan()函数检测到按键按下,那么就执行KEY_Action()函数内容;

    ③ KEY_Scan()函数每隔2ms检测变量cnt1是否为0x00,0x00表示按键按下,若检测到非按下状态,标志位KeyLock1为0。若检测到按键按下状态且第一次按下,则KeyNum = 1,同时将KeyLock1赋值为1,防止重复触发。注意else if语句一定要结合if理解。

    ④ KEY_Scan()中的“cnt1 =(cnt1<<1)|KEY1”语句用来检测按键是否按下,cnt1初值为0b1111 1111,假设按键已稳定按下,则KEY1为零,经过第一个2ms后cnt1为(11111111<<1)|0,即1111 1110,经过第2个2ms后cnt1为(11111110<<1)|0,即1111 1100…..那么经过8个2ms后此时cnt1为0x00,此状态法有效解决按键抖动问题;

    ⑤ cnt2同理于cnt1;

    ⑥ KEY_Action()函数接收到KeyNum的值进行相应的动作。

    (四)小结

    1、熟练掌握定时器、中断的使用方法;

    2、掌握按键中断扫描的方法;

    3、掌握swtich使用方法。

    可以在B站观看相关视频!!

    哔哩哔哩 ( ゜- ゜)つロ 乾杯~ Bilibili

    展开全文
  • 说明:本次基于ZYNQ-7000系列的串口中断控制器设计主要实现通过PC机串口助手软件发送指令实现控制PS端LED的功能,整体设计不复杂,旨在于理解ZYNQ系列的串口控制器的原理以及用法,主要参考资料来自UG585官方文档。...
    说明:本次基于ZYNQ-7000系列的串口中断控制器设计主要实现通过PC机串口助手软件发送指令实现控制PS端LED的功能,整体设计不复杂,旨在于理解ZYNQ系列的串口控制器的原理以及用法,主要参考资料来自UG585官方文档。
    相关理论

    在ZYNQ7000系列器件中,UART控制器是一个全双工异步收发控制器,ZYNQ内部包含两个UART控制器,UART0和UART1。每一个UART控制器支持可编程的波特率发生器、64字节的接收FIFO和发送FIFO、产生中断、RXD和TXD信号的环回模式设置以及可配置的数据位长度、停止位和校验方式等。
    在这里插入图片描述
    由上图可知,UART控制器和IO端口由参考时钟(UART REF_CLK)驱动,同时控制器也需要连接APB总线时钟(CPU_1x clock),UART REF_CLK和CPU_1x clock都是来自于PS时钟子系统。UART控制器的配置以及状态的获取由控制(Control)和状态寄存器(Status Registers)完成。另外,UART控制器不仅可以连接至MIO,也可以映射到EMIO,从而使用PL的端口来实现串口通信的功能。当UART控制器连接到MIO时,只有Tx(发送)和Rx(接收)两个引脚;而当连接EMIO时,除Tx和Rx引脚外,可选的还有CTSN、DSDN、DSRN等引脚,这些引脚用于串口的流控制,即调制解调器的数据通讯中。UART控制器采用独立的接收和发送数据路径,每个路径包含一个64字节的FIFO,控制器对发送和接收FIFO中的数据进行串并转换操作。FIFO的中断标志支持轮询处理或中断驱动处理两种方式。另外,控制器中还有一个模式开关,支持 RXD 和 TXD 信号的各种环回配置。
    在这里插入图片描述
    UART控制器的寄存器通过APB从机接口和PS AXI总线互联,控制器的寄存器用于对UART控制器进行配置和获取状态。波特率发生器(Baud Rate Generator)为UART控制器的接收端和发送端提供位周期时钟;中断控制器(GIC)为串口的收发提供了中断服务的功能。APB总线接口通过向TxFIFO寄存器写值,将数据加载到TxFIFO存储器中。当数据加载至TxFIFO后,TxFIFO的空标志变成无效的状态,直到最后一个数据从TxFIFO中移出,加载至传输移位寄存器,TxFIFO恢复空的标志位。同时TxFIFO使用TFULL(满中断状态)用于表示当前TxFIFO已经写满,并且会阻止数据继续写入。如果此时继续执行写操作,那么会触发溢出,数据不会加载到TxFIFO中。RxFIFO存储器接收来自接收移位寄存器的数据,当接收完数据后,RxFIFO空标志信号同样变成无效的状态,直到所有的数据通过APB总线发送出去。RxFIFO的满标志状态用于表示RxFIFO已经写满,并且会阻止更多的数据写入。

    在这里插入图片描述
    对于串口的启动顺序,我们根据ug585指导手册可以总结如下:

    1. 复位控制器(在 PS 系统复位时);
    2. 配置IO引脚信号。RxD和TxD可以连接至MIO或者EMIO,只有EMIO可以使用串口的流控制。
    3. 配置UART参考时钟;
    4. 配置控制器功能(UART 控制器初始化);
    5. 配置中断,通过中断来管理RxFIFO和TxFIFO;
    6. 配置串口流控制(可选);
    7. 管理发送和接收的数据,可以采用轮询或中断驱动处理两种方式。

    本次我们在设计中采用了串口的中断方式控制,通过PC端发送一个八位的数字,开发板接收数据并进行比较判别,然后选择点亮或熄灭PS端的LED灯,用这个设计来进一步理解串口控制器的工作原理。
    在这里插入图片描述
    在串口初始化函数中,我们初始化串口设备,并让串口设备进行自检,然后在设置串口的工作模式和相关参数,例如波特率、停止位、校验位等,另外我们还要设置中断的触发等级,它可选由1-63,即当RxFIFO或者TxFIFO中写入对应的字节数,便会触发中断。
    在这里插入图片描述
    在串口的中断系统初始化中,我们将串口中断服务函数连接到串口的中断控制器上,并使能中断控制器,设置串口的中断触发方式为RxFIFO触发。
    经过编译与调试,我们采用单步仿真的方法确认了代码的逻辑没有问题,然后下载到开发板中进行测试,我们SDK软件下方的SDK Terminal窗口中连接上串口,下载验证成功。

    PS:代码工程见资源附录
    展开全文
  • 今天说一下STM32单片机接收不定长度字节...IDLE就是串口收到一帧数据后,发生的中断。什么是一帧数据呢?比如说给单片机一次发来1个字节,或者一次发来8个字节,这些一次发来数据,就称为一帧数据,也可以叫做...

    今天说一下STM32单片机的接收不定长度字节数据的方法。由于STM32单片机带IDLE中断,所以利用这个中断,可以接收不定长字节的数据,由于STM32属于ARM单片机,所以这篇文章的方法也适合其他的ARM单片机。

    IDLE中断什么时候发生?

    IDLE就是串口收到一帧数据后,发生的中断。什么是一帧数据呢?比如说给单片机一次发来1个字节,或者一次发来8个字节,这些一次发来的数据,就称为一帧数据,也可以叫做一包数据。

    如何判断一帧数据结束,就是我们今天讨论的问题。因为很多项目中都要用到这个,因为只有接收到一帧数据以后,你才可以判断这次收了几个字节和每个字节的内容是否符合协议要求。

    看了前面IDLE中断的定义,你就会明白了,一帧数据结束后,就会产生IDLE中断。这个中断真是太TMD有用了。省去了好多判断的麻烦。

    如何配置好IDLE中断?

    下面我们就配置好串口IDLE中断吧。

    这是串口CR1寄存器,其中,对bit4写1开启IDLE中断,对bit5写1开启接收数据中断。(注意:不同系列的STM32,对应的寄存器位可能不同)

    (RXNE中断和IDLE中断的区别?

    当接收到1个字节,就会产生RXNE中断,当接收到一帧数据,就会产生IDLE中断。比如给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。)

    这是状态寄存器,当串口接收到数据时,bit5就会自动变成1,当接收完一帧数据后,bit4就会变成1.

    需要注意的是,在中断函数里面,需要把对应的位清0,否则会影响下一次数据的接收。比如RXNE接收数据中断,只要把接收到的一个字节读出来,就会清除这个中断。

    IDLE中断,如何是F0系列的单片机,需要用ICR寄存器来清除,如果是F1系列的单片机,清除方法是“先读SR寄存器,再读DR寄存器”。(我怎么知道?手册上写的)

    下面以STM32F103为例给出源程序。

    我们先来看程序中的主要部分。

    串口初始化函数片段

    %E7%A9%BA%E9%97%B2%E4%B8%AD%E6%96%AD.png (17.99 KB, 下载次数: 45)

    下载附件 保存到相册

    2015-10-29 23:11 上传

    如果你原来的串口初始化函数具有打开串口接收中断的话,实际上就是在初始化函数中多了一条打开空闲中断的语句。

    串口中断函数

    串口中断函数里面,最重要的两条语句,就是上图中圈出来的两条语句。第一条语句用来判断是否接收到1个字节,第二条语句用来判断是否接收到1帧数据。(是不是感觉超级方便?妈妈再也不用担心我如何判断是否接收完1帧数据了。)

    主函数

    我写的这个主函数,是用来验证接收的正确性的。RxCounter表示的是这一帧数据有几个字节,接收完一帧数据,会在中断函数里面把ReceiveState置1,然后,通过串口把接收到的数据发送回串口。这样,既验证了接收了多少字节的正确性,又验证了接收到的数据是否正确。

     

    上图是结果验证。

    /**

    ******************************************************************************

    * @file    串口接收不定长字节数据

    * @author  瑞生

    * @version V1.0

    * @date    2015.10.23

    * @brief   Main program body

    ******************************************************************************

    ******************************************************************************

    */

    /* Includes ------------------------------------------------------------------*/

    #include "stm32f10x.h"

    #include "uart.h"

    volatile uint8_t aRxBuffer[100]={0x00};

    volatile uint8_t RxCounter=0;

    volatile uint8_t ReceiveState=0;

    /**

    * @brief  Main program.

    * @param  None

    * @retval None

    */

    int main(void)

    {

    uint8_t i=0;

    USART1_Init();

    while (1)

    {

    if(ReceiveState==1)//如果接收到1帧数据

    {

    ReceiveState=0;

    i=0;

    while(RxCounter--)// 把接收到数据发送回串口

    {

    USART_SendData(USART1, aRxBuffer[i++]);

    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);

    }

    RxCounter=0;

    }

    }

    }

    本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

    展开全文
  • 在时钟设置语句中或在main.c初始化语句中加入: NVIC_SetVectorTable(0x20000000,0x0); 原理是让中断向量表映射到SRAM中。
  • 2.怎么进行触屏开发2.1 关键词定义和工作原理页面:一个串口触摸屏一般可以制作多个页面,一个页面对应着一幅画面,每个页面需要编号。控件:串口触摸屏上大部分显示图形和文字都可以当作控...
  • 本文开发环境: MCU型号:STM32F051R8T6 IDE环境: MDK 5.25 代码生成工具:STM32CubeMx 5.2.0 ...串口接收中断的配置 串口接收DMA线的配置 示例程序及起运行流程 附件:代码工程(MDK) 文章...
  • 制定串口通信协议并定时发送一帧数据,常见处理方法是利用空闲中断,但STM32 HAL库好像没有专用空闲中断,自己实现起来比较麻烦,这里利用非空中断可以实现同样功能,缺点是效率比较低,具体实现过程如下 ...
  • 这一节实现利用中断实现串口中断功能,关于串口的原理我就不再讲述了,如果不明白,就请查看我的另一篇博客  http://blog.csdn.net/mybelief321/article/details/8931064  下面我还是贴出自己的实验代码,...
  • STC单片机不断电下载程序.rar(723.45 KB, 下载次数: 1)原理:先测试出STC-ISP下载软件下发串口命令,在程序中开启串口中断,中断接收得到下载命令就执行一条单片机复位命令,程序就自动下载进去了,整个...
  • 前言 讲解韦东山JZ2440开发板的串口驱动原理,对韦东山在维基教程串口使用内容的一些补充,串口使用点击这里进入。这里主要讲的是串口驱动的编程思路,如何...(1)看JZ2440开发板的原理图,可知UART0的引脚是GP...
  • UART0串口编程(二) 本节针对是ARM2200环境下编写串口程序,其中设计轮循方式,中断方式,以及在UC/OS-II操作系统下的串口...串口编程硬件原理 1. 串口特性: 1>16字节接收FIFO和16字节发送FIFO 2>接收FIF
  • 串口作为MCU重要外部接口,同时也是软件开发重要调试手段,其重要性不言而喻。STM32的串口资源相当丰富,功能也相当强劲。ALIENTEK战舰STM32开发板所使用STM32F103ZET6最多可提供5路串口,有分数波特率发生...
  • 原文来自 ... 今天说一下STM32单片机接收不定长度字节数据方法。由于STM32单片机带IDLE中断,所以利用这个中断,可以接收不定长字节数据,由于STM...IDLE就是串口收到一帧数据后,发生的中断。什么是一帧数据呢...
  • 串口空闲中断+DMA.zip

    2019-05-25 13:18:56
    接收不定长度数据是串口空闲中断的重要使用方法,在裁判系统、OpenMV、 Manifold数据解析的使用中帮助巨大。 1、FSMC为灵活的静态存储控制器,利用控制SRAM的原理控制TFTLCD,方便快捷 2、LCD初始化程序中有厂商...
  • STM32串口IDLE中断

    2020-07-29 00:45:47
    后面我发现有个很好东西,串口IDLE中断,能自动响应你从电脑(别的串口)接收到不定长数据。而不是一直干等着。 先简单说说原理吧,大概是,你stm32接收数据时,并不会马上把数据马上处理掉,而是写到你定义缓冲区里...
  • 时间在2021年1月26日,寒假放假在家好好学一学 开发板:初雪100出头那块 NRF52840 EVAL KIT 下载工具:JINLK V11(最好是...参考资料: NRF52840-Eval-Kit-Schematic.pdf(原理图) nRF5_SDK_17.0.2_d674dde(官方例程.
  • 图上是串口的结构图。 SBUF是数据缓冲寄存器,发送和接收用是一个地址,但是不用担心冲突,读只能从接收缓冲区,写只能在发送缓冲区里。 寄存器SCON(SM0 SM1 SM2 REN TB8 RB8 RI) SM0和SM1: 工作方式选择(0...
  • 文章目录导读原理实现过程在串口初始化中使能上述中断标记位改写串口中断函数写回调函数结束语 导读 STM32用CubeMx生成HAL库效率很低,利用HAL自带的串口中断在接收大量数据时很容易出现数据丢失。一般来说,...
  • 阐述PC 机串行通信和中断编程的原理,介绍一种在中断方式下实现PC 机与单片机进行串口通信的方法,给出用于PC 机的基本程序。
  • 串口的时序和工作原理就不写了,主要写如何应用 串口的设置除了GPIO外,需要设置参数有波特率、数据位、停止位,校验方式; GPIO输出配置AF_PP复用推挽输出 GPIO出入配置IN_FLAOTING浮空输入 USART配置为115200...
  • 中断原理讲解例程流程详解库函数分析详解对应寄存器介绍对应函数介绍对应注释详解本篇文章提供两种方法:一种是 :IDLE 接收空闲中断+DMA一种是: IDLE 接收空闲中断+RXNE接收数据中断都可完成串口数据收发知识点...
  • 51单片机串口多机通信的原理与编程实现

    千次阅读 多人点赞 2020-06-11 12:15:44
    51单片机串口多机通信 ...(Master host)主机串口初始化,串口发送,中断接收代码 (Slave host )从机串口初始化,串口发送,中断接收代码 主从机地址标识 主从机数据帧约束 接线和注意事项 .
  • 通过中断方式接收数据,可以有效提高单片机的效率。本程序采用中断方式接收数据,并通过数码管显示出来,很简单,仅仅是对初学者知道中断的基本操作,带原理
  • 串口调试小节之二 串口通讯原理

    千次阅读 2015-06-08 10:49:14
    我们以接收方为例,详细讲解串口通讯简单原理,一个串口数据接收情况基本如下:  主要分了三层: 1、  硬件层:负责将比特位转换成字节型数据,并且将数据传输通讯状态记录下来,产生中断让驱动读取,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 454
精华内容 181
关键字:

串口中断的原理