外部中断实验

代码构造均取自野火教程,只是加上了一些注释。

按键和 EXTI 宏定义:

//引脚定义
#define KEY1_INT_GPIO_PORT GPIOA
//GPIOA
#define KEY1_INT_GPIO_CLK RCC_AHB1Periph_GPIOA
//打开AHB1时钟
#define KEY1_INT_GPIO_PIN GPIO_Pin_0
//pin0
#define KEY1_INT_EXTI_PORTSOURCE EXTI_PortSourceGPIOA
//GPIOA中断
#define KEY1_INT_EXTI_PINSOURCE EXTI_PinSource0
//第0引脚中断
#define KEY1_INT_EXTI_LINE EXTI_Line0
//选择EXTI中断源(PA0)
#define KEY1_INT_EXTI_IRQ EXTI0_IRQn
//IRQ通道

#define KEY1_IRQHandler EXTI0_IRQHandler
//中断服务函数

//以下同上
#define KEY2_INT_GPIO_PORT GPIOC
#define KEY2_INT_GPIO_CLK RCC_AHB1Periph_GPIOC
#define KEY2_INT_GPIO_PIN GPIO_Pin_13
#define KEY2_INT_EXTI_PORTSOURCE EXTI_PortSourceGPIOC
#define KEY2_INT_EXTI_PINSOURCE EXTI_PinSource13
#define KEY2_INT_EXTI_LINE EXTI_Line13
#define KEY2_INT_EXTI_IRQ EXTI15_10_IRQn

#define KEY2_IRQHandler EXTI15_10_IRQHandler

嵌套向量中断控制器 NVIC 配置:

 /**
  * @brief  配置嵌套向量中断控制器NVIC
  * @param  无
  * @retval 无
  */
static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 配置NVIC为优先级组1 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  
  /* 配置中断源:按键1 */
  NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;
  /* 配置抢占优先级:1 */
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 配置子优先级:1 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断通道 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
  /* 配置中断源:按键2,其他使用上面相关配置 */  
  NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;
  NVIC_Init(&NVIC_InitStructure);
}


EXTI 中断配置:

 /**
  * @brief  配置 PA0 为线中断口,并设置中断优先级
  * @param  无
  * @retval 无
  */
void EXTI_Key_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure; 
EXTI_InitTypeDef EXTI_InitStructure;
  
/*开启按键GPIO口的时钟*/
RCC_AHB1PeriphClockCmd(KEY1_INT_GPIO_CLK|KEY2_INT_GPIO_CLK ,ENABLE);
  
  /* 使能 SYSCFG 时钟 ,使用GPIO外部中断时必须使能SYSCFG时钟*/
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
  
  /* 配置 NVIC */
  NVIC_Configuration();
  
/* 选择按键1的引脚 */ 
  GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN;
  /* 设置引脚为输入模式 */ 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;    
  /* 设置引脚不上拉也不下拉 */
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  /* 使用上面的结构体初始化按键 */
  GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure); 
/* 连接 EXTI 中断源 到key1引脚 */
  SYSCFG_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE,KEY1_INT_EXTI_PINSOURCE);
  /* 选择 EXTI 中断源 */
  EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;
  /* 中断模式 */
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  /* 下降沿触发 */
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;  
  /* 使能中断/事件线 */
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
  
  /* 选择按键2的引脚 */ 
  GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN;  
  /* 其他配置与上面相同 */
  GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure);      
/* 连接 EXTI 中断源 到key2 引脚 */
  SYSCFG_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE,KEY2_INT_EXTI_PINSOURCE);
  /* 选择 EXTI 中断源 */
  EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE;
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  /* 上升沿触发 */
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;  
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
}

EXTI 中断服务函数(一般会把中断函数放入stm32f4xx_it.c文件当中):

void KEY1_IRQHandler(void)
{
  //确保是否产生了EXTI Line中断
	if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) 
	{
		// LED1 取反		
		LED1_TOGGLE;
    //清除中断标志位
		EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);     
	}  
}

void KEY2_IRQHandler(void)
{
  //确保是否产生了EXTI Line中断
	if(EXTI_GetITStatus(KEY2_INT_EXTI_LINE) != RESET) 
	{
		// LED2 取反		
		LED2_TOGGLE;
    //清除中断标志位
		EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE);     
	}  
}

理解值和引用-第八章-C#从入门到放弃

Circle c;
c = new Circle(42);
Circle refc;
refc = c;  //c与refc指向同一地址

正确的复制类的方法(包括复制私有数据):

class Circle
{
    private int radius;
    ...
    public Circle Clone()
    {
        //创建新的Circle对象
        Circle clone = new Circle();
        //将私有的数据从this复制到clone
        clone.radius = this.radius;
        
        //返回包含克隆数据的新Circle对象
        return clone;
    }
}

null值:

Circle c = new Circle(42);
Circle copy = null;
...
if(copy == null)
{
    copy = c;
    ...
}

对于值类型,需要将变量生命为可空值类型:

int i = null; //非法
int? i = null; //合法

可空类型的属性:

int? i = null;
...
if(!i.HasValus)
{
    i = 99;
}
else
{
    Console.WriteLine(i.Value);
}

ref:

static void example(ref int a)
{
    a++;
}
static void Main()
{
    int arg = 42;
    example(ref arg);
    //arg=43
}

out:

static void example(out int a)
{
    a = 42;
}
static void Main()
{
    int arg;
    example(out arg);
    //arg=42
}

object:

Circle c;
c = new Circle(42);
object o;
o = c;

创建和管理类和对象 – 第七章 – C# 从入门到放弃

创建构造器的一个例子:

class Circle
{
    private int radius;
    
    public Circle()  //默认构造器
    {
        radius = 0;
    }
    public Circle(int initalRadius)  //重载的构造器
    {
        radius = initialRadius;
    }
    public double Area()
    {
        return Math.PI * radius * radius;
    }
}

使用多文件时的写法:

Circ1.cs内容:

partial class Circle
{
    public Circle()  //默认构造器
    {
        radius = 0;
    }
    public Circle(int initalRadius)  //重载的构造器
    {
        radius = initialRadius;
    }
}

Circ2.cs内容:

partial class Circle
{
    private int radius;
    public double Area()
    {
        return Math.PI * radius * radius;
    }
}

调用自己的字段时,使用this关键字:

class Point
{
    private int x, y;
    private static int objectCount = 0;
    public Point()
    {
        this.x = -1;
        this.y = -1;
        objectCount++;
    }
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
        objectCount++;
    }
    public double DistanceTo(Point other)
    {
        int xDiff = this.x - other.x;   //注意此处
        int yDiff = this.y - other.y;
        double distance = Math.Sqrt((xDiff * xDiff) + (yDiff * yDiff));
        return distance;
    }
    public static int ObjectCount() => objectCount;
    //ObjectCount方法返回objectCount的值
}

=======

静态方法/数据使用static标记

只能访问标记为static的其他方法和字段

静态类(不允许被new:

class Math
{
    ...
    public const double PI = 3.14159265358979;
}

匿名类:

var myxxxx = new {Name = "xx", Age = 123};
Console.WriteLine($"Name:{myxxxx.Name},Age:{myxxxx.Age}");


var myyyyy = new {Name = "yy", Age = 321};

myxxxx = myyyyy; //合法,因为具有相同类型

异常处理 – 第六章 – C# 从入门到放弃

基本语法:

try
{
    ...
}
catch(Exception ex) when (ex.GetTypr() != typeof(System.OutOfMenoryException))
{
    ....
}

=====

数据溢出异常的处理:

checked:

int number = int.MaxValue;
checked
{
    int willThrow = number++;
    Console.WriteLine("won't run to here");
}

unchecked:

int number = int.MaxValue;
unchecked
{
    int willThrow = number++;
    Console.WriteLine("will run to here");
}

also:

try
{
    int lhs = 9876543;
    int rhs = 9876543;
    int outcome = 0;
    outcome = checked(lhs * rhs);
}
catch(OverflowException oEx)
{
    result.Text = oex.Message;
}

=====

手动抛出异常:

try
{
    throw new ArgumentOutOfRangeException("error!");
}
catch(ArgumentOutOfRangeException msg)
{
    xxx.Text = msg.Message;
}

finally的用法:

try
{
    DataConnection.Open();
    DataCommand.ExecuteReader();
    ...
    return;
}
finally
{
    DataConnection.Close();
}
//无论是否抛出异常,也无论从什么地方 return 返回,finally 语句块总是会执行,这样你有机会调用 Close 来关闭数据库连接(即使未打开或打开失败,关闭操作永远是可以执行的),以便于释放已经产生的连接,释放资源。 顺便说明,return 是可以放在 try 语句块中的。但不管在什么时机返回,在返回前,finally 将会执行。
//小结
try
{
    // 执行的代码,其中可能有异常。一旦发现异常,则立即跳到 catch 执行。否则不会执行 catch 里面的内容
}
catch
{
    // 除非 try 里面执行代码发生了异常,否则这里的代码不会执行
}
finally
{
    // 不管什么情况都会执行,包括 try catch 里面用了 return , 可以理解为只要执行了 try 或者 catch,就一定会执行 finally
}