|____1.4外部中断/看门狗/定时器/PWM/RTC/待机唤醒/ADC/DAC/IIC/SPI/485/CAN

外部中断

  • [1. 外部中断](#1. 外部中断)
  • [2. 看门狗](#2. 看门狗)
  • [3. 定时器](#3. 定时器)
  • [4. 定时器产生 PWM](#4. 定时器产生 PWM)
  • [5. RTC实时时钟](#5. RTC实时时钟)
  • [6. 待机唤醒](#6. 待机唤醒)
  • [7. ADC](#7. ADC)
  • [8. DAC](#8. DAC)
  • [9. IIC](#9. IIC)
  • [10. SPI](#10. SPI)
  • [11. 485](#11. 485)
  • [12. CAN](#12. CAN)

1. 外部中断

c 复制代码
// 1. 初始化 IO 口为输入
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//使能 PORTA,PORTC 时钟
GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_5;		//PC5
GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_IPU; 	//设置成上拉输入
GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化 GPIOC5

// 2. 开启 IO 口复用时钟
RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO, ENABLE );	//外部中断,需要使能 AFIO 时钟

// 3. 将 GPIO 端口与中断线映射起来
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource5);	// 将中断线 5 与 PC5 映射起来

// 4. 初始化线上中断,设置触发条件等
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line	= EXTI_Line5;			// 中断线的标号(取值范围 EXTI_Line0 ~ EXTI_Line15)
EXTI_InitStructure.EXTI_Mode 	= EXTI_Mode_Interrupt;	// 中断模式(中断 EXTI_Mode_Interrupt 、事件 EXTI_Mode_Event)
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;	// 触发方式(下降沿触发 EXTI_Trigger_Falling、上升沿触发 EXTI_Trigger_Rising、任意电平(上升沿和下降沿)触发 EXTI_Trigger_Rising_Falling)
EXTI_InitStructure.EXTI_LineCmd = ENABLE;				// 使能中断线
EXTI_Init(&EXTI_InitStructure); //设置中断线 4 上的中断为下降沿触发

// 5. 配置中断分组(NVIC),并使能中断
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel 					= EXTI2_IRQn; //使能按键外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 0x02; //抢占优先级 2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority 		= 0x02; //子优先级 2
NVIC_InitStructure.NVIC_IRQChannelCmd 				= ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure); //中断优先级分组初始化

// 6. 编写中断服务函数
EXPORT EXTI0_IRQHandler 	//中断线 0 的中断函数
EXPORT EXTI1_IRQHandler 	//中断线 1 的中断函数
EXPORT EXTI2_IRQHandler 	//中断线 2 的中断函数
EXPORT EXTI3_IRQHandler 	//中断线 3 的中断函数
EXPORT EXTI4_IRQHandler 	//中断线 4 的中断函数
EXPORT EXTI9_5_IRQHandler 	//中断线 5-9 的中断函数
EXPORT EXTI15_10_IRQHandler	//中断线 10-15 的中断函数
c 复制代码
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);	// 判断某个中断线上的中断是否发生(标志位是否置位)
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);//清除某个中断线上的中断标志位

2. 看门狗

c 复制代码
// 1. 取消寄存器写保护(向 IWDG_KR 写入 0X5555)
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);	//使能对寄存器 IWDG_PR 和 IWDG_RLR 的写操作

// 2. 设置独立看门狗的预分频系数和重装载值
void IWDG_SetPrescaler(uint8_t IWDG_Prescaler); //设置 IWDG 预分频值
void IWDG_SetReload(uint16_t Reload); 			//设置 IWDG 重装载值

// 3. 重载计数值喂狗(向 IWDG_KR 写入 0XAAAA)
IWDG_ReloadCounter(); //按照 IWDG 重装载寄存器的值重装载 IWDG 计数器

// 4. 启动看门狗(向 IWDG_KR 写入 0XCCCC)
IWDG_Enable(); //使能 IWDG

// 5. 喂独立看门狗
IWDG->KR = 0XAAAA;
c 复制代码
// 1. 使能 WWDG 时钟(独立看门狗有独立的 40Khz 时钟,而窗口看门狗使用的是 PCLK1 的时钟)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); // WWDG 时钟使能

// 2. 设置窗口值和分频数
void WWDG_SetWindowValue(uint8_t WindowValue);	// 设置窗口值
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);// 设置分频数

// 3. 开启 WWDG 中断并分组
WWDG_EnableIT(); //开启窗口看门狗中断

// 4. 中断优先级配置
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel 					 = WWDG_IRQn; //WWDG 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占 2 子优先级 3 组 2
NVIC_InitStructure.NVIC_IRQChannelSubPriority 		 = 3; //抢占 2,子优先级 3,组 2
NVIC_Init(&NVIC_InitStructure);//NVIC 初始化

// 5. 设置计数器初始值并使能看门狗
void WWDG_Enable(uint8_t Counter);

// 6. 编写中断服务函数(喂狗)
void WWDG_IRQHandler(void)
{
	WWDG_Enable(0x7F);
	WWDG_ClearFlag(); //清除提前唤醒中断标志位
}

3. 定时器

c 复制代码
// 1. 时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // TIM3 时钟使能

// 2. 初始化定时器参数,设置自动重装值,分频系数,计数方式等
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period 		= 5000;					// 设置自动重载计数周期值
TIM_TimeBaseStructure.TIM_Prescaler 	= 7199; 				// 设置分频系数
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 		// 设置时钟分频因子
TIM_TimeBaseStructure.TIM_CounterMode 	= TIM_CounterMode_Up;	// 设置计数方式(向上计数 TIM_CounterMode_Up、向下计数 TIM_CounterMode_Down、中央对其计数)
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

// 3. 设置定时器允许更新中断
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);//参数1:选择定时器号;参数2:指明使能的定时器中断的类型(更新中断 TIM_IT_Update、触发中断 TIM_IT_Trigger、输入捕获中断等等);参数3:失能还是使能

// 4. 设置中断优先级
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel 					 = TIM3_IRQn; 	//TIM3 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 			//先占优先级 0 级
NVIC_InitStructure.NVIC_IRQChannelSubPriority 		 = 3; 			//从优先级 3 级
NVIC_InitStructure.NVIC_IRQChannelCmd 				 = ENABLE; 		//IRQ 通道被使能
NVIC_Init(&NVIC_InitStructure); //初始化 NVIC 寄存器

// 5. 开启定时器
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)

// 6. 编写中断服务函数
void TIM3_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查 TIM3 更新中断发生与否
	{
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除 TIM3 更新中断标志
	}
}
c 复制代码
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t)			// 读取中断状态寄存器的值判断中断类型
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)	// 清除中断标志位

4. 定时器产生 PWM

PWM 脉冲波形输出

c 复制代码
// 1. 开启定时器时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); 	// 使能 tim1 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);	// 使能 GPIO 外设时钟使能

// 2. 配置 GPIO 为复用推挽输出,输出定时器产生的 PWM 脉冲波形
GPIO_InitTypeDef 	GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_8; 		// TIM_CH1
GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed 	= GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// 3. 设置定时器的 自动重装载寄存器 ARR 和 预分频器寄存器 PSC(设置定时器的计数周期和溢出时间,即设置定时器多长时间产生一次更新事件)
TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period 		= 899; 					//设置自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler 	= 0; 					//设置预分频值(不分频)
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 		//设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode 	= TIM_CounterMode_Up; 	//向上计数模式
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据指定的参数初始化 TIMx 的

// 4. 设置定时器的 PWM 模式及通道方向, 使能定时器的 CH1 输出
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode 		= TIM_OCMode_PWM2; 			//脉宽调制模式2 
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 	//比较输出使能 
TIM_OCInitStructure.TIM_Pulse 		= 0; 						//设置待装入捕获比较寄存器的脉冲值 
TIM_OCInitStructure.TIM_OCPolarity 	= TIM_OCPolarity_High; 		//输出极性高 
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //初始化 TIM1 OC1

// 5. 设置 MOE 输出,使能 PWM 输出(高级定时器使能)
TIM_CtrlPWMOutputs(TIM1,ENABLE);// MOE 主输出使能

// 6. 使能定时器
TIM_Cmd(TIM1, ENABLE); //使能 TIM1


void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);// 修改 TIM1_CCR1 来控制占空比 
TIM_InternalClockConfig(TIM2)								// 将 TIM2 的时钟源设置为内部时钟
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable)			// 使能 TIM2 的通道 2 输出比较预装载功能(在没有预装载的情况下,当你修改比较寄存器(CCR2)的值时,新值会立即生效,可能导致当前正在输出的 PWM 波形突然发生不完整的跳变,产生毛刺或不期望的窄脉冲)
TIM_UpdateDisableConfig(TIM2, DISABLE)						// 允许 TIM2 产生更新事件

PWM 脉冲波形捕获

c 复制代码
// 1. 开启定时器时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能TIM2时钟 
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能 GPIOA 时钟

// 2. 配置 GPIO 为复用推挽输出,输出定时器产生的 PWM 脉冲波形
GPIO_InitTypeDef 	GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_0; 	
GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_IPD; // 下拉输入模式
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_0);  				 //PA0 下拉

// 3. 设置定时器的 自动重装载寄存器 ARR 和 预分频器寄存器 PSC(设置定时器的计数周期和溢出时间,即设置定时器多长时间产生一次更新事件)
TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period 		= 899; 					//设置自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler 	= 0; 					//设置预分频值(不分频)
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 		//设置时钟分割
TIM_TimeBaseStructure.TIM_CounterMode 	= TIM_CounterMode_Up; 	//向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据指定的参数初始化 TIMx 的

// 4. 设置定时器的输入捕获参数
TIM_ICInitTypeDef  	TIM2_ICInitStructure;
TIM2_ICInitStructure.TIM_Channel 	= TIM_Channel_1; 			//选择输入端 IC1映射到TI1上 
TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; 	//上升沿捕获 
TIM2_ICInitStructure.TIM_ICSelection= TIM_ICSelection_DirectTI; //映射到TI1上 
TIM2_ICInitStructure.TIM_ICPrescaler= TIM_ICPSC_DIV1;  			//配置输入分频,不分频  
TIM2_ICInitStructure.TIM_ICFilter 	= 0x00;						//IC1F=0000 配置输入滤波器 不滤波 
TIM_ICInit(TIM2, &TIM2_ICInitStructure); 

// 5. 使能捕获和更新中断
TIM_ITConfig(TIM2,TIM_IT_Update|TIM_IT_CC1,ENABLE);	// 允许更新中断 CC1IE 捕获中断

// 6. 使能定时器
TIM_Cmd(TIM2, ENABLE); //使能 TIM2

// 7. 设置中断分组
NVIC_InitTypeDef NVIC_InitStructure; 
NVIC_InitStructure.NVIC_IRQChannel 					= TIM2_IRQn; //TIM2中断 
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 2;  		 //先占优先级2级 
NVIC_InitStructure.NVIC_IRQChannelSubPriority 		= 0;  		 //从优先级0级 
NVIC_InitStructure.NVIC_IRQChannelCmd 				= ENABLE; 	 //IRQ通道被使能 
NVIC_Init(&NVIC_InitStructure);  //初始化外设NVIC寄存器 

// 8. 编写中断服务函数 
void TIM2_IRQHandler(void) 
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) 	//判断是否为更新中断 
	{	}
	if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)		//判断是否发生捕获事件 
	{	
		TIM_OC1PolarityConfig(TIM2,XXX)	// 设置通道捕获极性(上升/下降沿捕获)
	}
	TIM_ClearITPendingBit(TIM2, TIM_IT_CC1|TIM_IT_Update);	//清除中断和捕获标志位 
}

5. RTC实时时钟

c 复制代码
// 1. 使能电源时钟和备份区域时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	// 使能 PWR 和 BKP 外设时钟 

// 2. 取消备份区写保护
PWR_BackupAccessCmd(ENABLE); //使能 RTC 和后备寄存器访问 

// 3. 复位备份区域,开启外部低速振荡器
BKP_DeInit(); //复位备份区域 

// 4. 开启外部低速振荡器
RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE) 

// 5. 选择RTC时钟,并使能
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);  //选择 LSE 作为 RTC 时钟    

// 6. 使能 RTC 时钟
RCC_RTCCLKCmd(ENABLE); //使能RTC时钟

// 7. 设置RTC的分频,以及配置RTC时钟
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_WaitForSynchro();  //等待RTC寄存器同步  
RTC_ITConfig(RTC_IT_SEC, ENABLE);  //使能RTC秒中断

RTC_WaitForLastTask();  	 // 等待最近一次对RTC寄存器的写操作完成
RTC_EnterConfigMode();  	 // 允许配置 
RTC_SetPrescaler(32767);	 // 设置RTC预分频的值

RTC_WaitForLastTask();  	 // 等待最近一次对RTC寄存器的写操作完成
RTC_Set(2009,12,2,10,0,55);  //设置时间

RTC_ExitConfigMode();//退出配置模式,更新配置

// 8. 向指定的后备寄存器中写入用户程序数据,用于标记是否是第一次配置时钟
BKP_WriteBackupRegister(BKP_DR1, 0X5050);	// 向指定的后备寄存器中写入用户程序数据 0x5050
uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR);// 获取指定的后备寄存器中写入用户程序数据

// 9. 设置 RTC 中断分组
NVIC_InitTypeDef NVIC_InitStructure; 
NVIC_InitStructure.NVIC_IRQChannel 					= XXX; 		 //RTC 中断 
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 2;  		 //先占优先级2级 
NVIC_InitStructure.NVIC_IRQChannelSubPriority 		= 0;  		 //从优先级0级 
NVIC_InitStructure.NVIC_IRQChannelCmd 				= ENABLE; 	 //IRQ通道被使能 
NVIC_Init(&NVIC_InitStructure);  //初始化外设NVIC寄存器 

// 10. 编写中断服务函数
void RTC_IRQHandler(void) 
{
	if (RTC_GetITStatus(RTC_IT_SEC) != RESET)	// 秒钟中断 
	{	}
	if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断 
	{ 
		RTC_ClearITPendingBit(RTC_IT_ALR);  //清闹钟中断        
	}         
	RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);  //清闹钟中断 
	RTC_WaitForLastTask();        
}


RTC_SetCounter(seccount);	// 设置RTC计数器的值
RTC_GetCounter();			// 获取RTC计数器的值

6. 待机唤醒

c 复制代码
// 1. 使能电源时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); //使能PWR外设时钟 

// 2. 设置WK_UP引脚作为唤醒源
PWR_WakeUpPinCmd(ENABLE);  //使能唤醒管脚功能 

// 3. 设置SLEEPDEEP位,设置PDDS位,执行WFI指令,进入待机模式
PWR_EnterSTANDBYMode();   //进入待命(STANDBY)模式

// 4. 编写中断服务函数
void Sys_Enter_Standby(void) 
{     
	RCC_APB2PeriphResetCmd(0X01FC,DISABLE); //复位所有 IO 口 
	Sys_Standby(); 
} 

7. ADC

ADC 不带 MDA

c 复制代码
// 1. 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1 , ENABLE );    //使能 ADC1 通道时钟 

// 2. 设置模拟通道输入引脚
GPIO_InitTypeDef 	GPIO_InitStructure; 
GPIO_InitStructure.GPIO_Pin	 = GPIO_Pin_1; 
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;	// 模拟输入 
GPIO_Init(GPIOA, &GPIO_InitStructure);			// 初始化 GPIOA.1 

// 3. 设置 ADC 分频因子
RCC_ADCCLKConfig(RCC_PCLK2_Div6);	// 设置 ADC 分频因子 6 ,72M/6=12,ADC 最大时间不能超过14M

// 4. 复位 ADC
ADC_DeInit(ADC1); // 复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值 

// 5. 初始化 ADC 参数,设置 ADC 的工作模式以及规则序列的相关信息
ADC_InitTypeDef 	ADC_InitStructure; 
ADC_InitStructure.ADC_Mode 				 = ADC_Mode_Independent; //ADC 工作模式:独立模式 
ADC_InitStructure.ADC_ScanConvMode 		 = DISABLE; //AD 单通道模式 
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //AD 单次转换模式 
ADC_InitStructure.ADC_ExternalTrigConv 	 = ADC_ExternalTrigConv_None;  //转换由软件而不是外部触发启动 (由硬件触发 ADC_ExternalTrigConv_T2_CC2)
ADC_InitStructure.ADC_DataAlign 		 = ADC_DataAlign_Right; //ADC 数据右对齐 
ADC_InitStructure.ADC_NbrOfChannel 		 = 1; //顺序进行规则转换的ADC通道的数目1 
ADC_Init(ADC1, &ADC_InitStructure); //根据指定的参数初始化外设ADCx  

// 6. 使能 ADC 并校准
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1 

// 7. 校准 ADC
ADC_ResetCalibration(ADC1); 				//开启复位校准  
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1);  				//开始指定ADC1的校准状态
while(ADC_GetCalibrationStatus(ADC1));  	//等待校 AD 准结束 

// 8. 读取ADC值
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); 	//通道1 规则采样顺序值为1,采样时间为239.5周期
ADC_SoftwareStartConvCmd(ADC1, ENABLE);								//使能指定的 ADC1 的软件转换启动功能
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));						//等待转换结束
ADC_GetConversionValue(ADC1);										//返回最近一次ADC1规则组的转换结果

ADC 带 MDA

c 复制代码
// 1. 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1 , ENABLE );    //使能 ADC1 通道时钟 
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能 DMA 时钟

// 2. 设置模拟通道输入引脚
GPIO_InitTypeDef 	GPIO_InitStructure; 
GPIO_InitStructure.GPIO_Pin	 = GPIO_Pin_1; 
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;	// 模拟输入 
GPIO_Init(GPIOA, &GPIO_InitStructure);			// 初始化 GPIOA.1 

// 3. 设置 ADC 分频因子
RCC_ADCCLKConfig(RCC_PCLK2_Div6);	// 设置 ADC 分频因子 6 ,72M/6=12,ADC 最大时间不能超过14M

// 4. 复位 ADC
ADC_DeInit(ADC1); // 复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值 

// 5. 初始化 ADC 参数,设置 ADC 的工作模式以及规则序列的相关信息
ADC_InitTypeDef 	ADC_InitStructure; 
ADC_InitStructure.ADC_Mode 				 = ADC_Mode_Independent; //ADC 工作模式:独立模式 
ADC_InitStructure.ADC_ScanConvMode 		 = DISABLE; //AD 单通道模式 
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //AD 单次转换模式 
ADC_InitStructure.ADC_ExternalTrigConv 	 = ADC_ExternalTrigConv_None;  //转换由软件而不是外部触发启动 (由硬件触发 ADC_ExternalTrigConv_T2_CC2)
ADC_InitStructure.ADC_DataAlign 		 = ADC_DataAlign_Right; //ADC 数据右对齐 
ADC_InitStructure.ADC_NbrOfChannel 		 = 1; //顺序进行规则转换的ADC通道的数目1 
ADC_Init(ADC1, &ADC_InitStructure); //根据指定的参数初始化外设ADCx  

// 6. 使能 ADC 并校准
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1 

// 7. 初始化 DMA 通道参数
DMA_InitTypeDef DMA_InitStructure; 
DMA_InitStructure.DMA_PeripheralBaseAddr 	= &ADC1->DR;  					//DMA 外设 ADC 基地址 
DMA_InitStructure.DMA_MemoryBaseAddr 		= cmar;  						//DMA 内存基地址 
DMA_InitStructure.DMA_DIR 					= DMA_DIR_PeripheralSRC;  		//从外设读取发送到内存(或从内存读取发送到外设 DMA_DIR_PeripheralDST)
DMA_InitStructure.DMA_BufferSize 			= XX;  							//DMA 通道的 DMA 缓存的大小 
DMA_InitStructure.DMA_PeripheralInc 		= DMA_PeripheralInc_Disable;	//外设地址不变 
DMA_InitStructure.DMA_MemoryInc 			= DMA_MemoryInc_Enable;  		//内存地址递增 
DMA_InitStructure.DMA_PeripheralDataSize 	= DMA_PeripheralDataSize_Byte; 	//8 位 (DMA_MemoryDataSize_HalfWord 16位)
DMA_InitStructure.DMA_MemoryDataSize 		= DMA_MemoryDataSize_Byte; 		// 8 位 (DMA_PeripheralDataSize_HalfWord 16位)
DMA_InitStructure.DMA_Mode 					= DMA_Mode_Normal;  			//工作在正常缓存模式 (循环模式 DMA_Mode_Circular)
DMA_InitStructure.DMA_Priority 				= DMA_Priority_Medium; 			//DMA 通道 x 拥有中优先级 (高优先级 DMA_Priority_High) 
DMA_InitStructure.DMA_M2M 					= DMA_M2M_Disable;  			//非内存到内存传输 
DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据指定的参数初始化 


// 8. 使能 DMA 通道  的"传输完成"中断
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);

// 9. 设置 RTC 中断分组
NVIC_InitTypeDef NVIC_InitStructure; 
NVIC_InitStructure.NVIC_IRQChannel 					= XXX; 		 //RTC 中断 
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 2;  		 //先占优先级2级 
NVIC_InitStructure.NVIC_IRQChannelSubPriority 		= 0;  		 //从优先级0级 
NVIC_InitStructure.NVIC_IRQChannelCmd 				= ENABLE; 	 //IRQ通道被使能 
NVIC_Init(&NVIC_InitStructure);  //初始化外设NVIC寄存器 

// 10. 配置 ADC 规则组的通道顺序和采样时间
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, i+1, ADC_SampleTime_239Cycles5);

// 11. 使能串口DMA发送
ADC_DMACmd(ADC1, ENABLE);

// 12. 使能 DMA 通道,启动传输
ADC_Cmd(ADC1, ENABLE);

// 13. 校准 ADC
ADC_ResetCalibration(ADC1); 				//开启复位校准  
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1);  				//开始指定ADC1的校准状态
while(ADC_GetCalibrationStatus(ADC1));  	//等待校 AD 准结束 

// 14. 编写中断服务函数
void DMA1_Channel1_IRQHandler(void)
{
	if (DMA_GetITStatus(DMA1_IT_TC1)!=RESET)	// 查询DMA传输通道的状态
	{
		DMA_ClearITPendingBit(DMA1_IT_TC1);	// 清除中断线上的中断标志位
	}
}

8. DAC

c 复制代码
// 1. 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );   // 使能 PORTA
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE );   	// 使能 DAC 通道时钟

// 2. 设置模拟通道输入引脚
GPIO_InitTypeDef 	GPIO_InitStructure; 
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;         // 端口配置 
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;     // 模拟输入 
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
GPIO_Init(GPIOA, &GPIO_InitStructure);                      
GPIO_SetBits(GPIOA,GPIO_Pin_4) ;  // 输出高

// 3. 初始化DAC,设置DAC的工作模式
DAC_InitTypeDef 	DAC_InitType; 
DAC_InitType.DAC_Trigger						= DAC_Trigger_None; // 不使用触发功能 TEN1=0 
DAC_InitType.DAC_WaveGeneration					= DAC_WaveGeneration_None;// 不使用波形发生 
DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude	= DAC_LFSRUnmask_Bit0; 
DAC_InitType.DAC_OutputBuffer					= DAC_OutputBuffer_Disable ; //DAC1 输出缓存关闭  
DAC_Init(DAC_Channel_1,&DAC_InitType);  //初始化DAC通道1 

// 4. 使能DAC转换通道
DAC_Cmd(DAC_Channel_1, ENABLE);  //使能 DAC1 

// 5. 设置/获取DAC的输出值
DAC_SetChannel1Data(DAC_Align_12b_R, 0); // 12位右对齐,设置DAC初始值
DAC_GetDataOutputValue(DAC_Channel_1); 	 // 获取DAC值

9. IIC

c 复制代码
#define IIC_SCL  PCout(12)    // SCL -> PC12
#define IIC_SDA  PCout(11)    // SDA -> PC11
#define SDA_IN()  {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=8<<12;}
#define SDA_OUT() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=3<<12;}

1. 初始化 IIC

c 复制代码
void IIC_Init(void) 
{           
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC, ENABLE );  // 使能 PORTC 时钟  
	GPIO_InitTypeDef 	GPIO_InitStructure;   
	GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_12|GPIO_Pin_11; 
	GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_Out_PP ;   //开漏输出(GPIO_Mode_Out_PP 推挽输出,需要设置 SDA_OUT()和 SDA_IN()) 
	GPIO_InitStructure.GPIO_Speed 	= GPIO_Speed_50MHz; 
	GPIO_Init(GPIOC, &GPIO_InitStructure); 

	iic_stop();     // 停止总线上所有设备
} 

2. IIC 开始信号

c 复制代码
void IIC_Start(void)
{
	// SDA_OUT(); // 推挽输出,则需要设置,用来
	IIC_SDA = 1;     
	IIC_SCL = 1;
	delay_us(2); 
	IIC_SDA = 0;     
	delay_us(2); 
	IIC_SCL = 0;    // //钳住I2C总线,准备发送或接收数据 
}

3. IIC 停止信号

c 复制代码
void IIC_Stop(void)
{
	// SDA_OUT(); // 推挽输出,则需要设置
	IIC_SDA = 0;    
	IIC_SCL = 0;
	delay_us(2); 
	IIC_SCL = 1;
	delay_us(2); 
	IIC_SDA = 1;    
	delay_us(2); 
}

4. IIC 发送ACK

c 复制代码
void IIC_Ack(void)
{
	// SDA_OUT(); // 推挽输出,则需要设置
	IIC_SDA = 0;    
	IIC_SCL = 0;
	delay_us(2); 
	IIC_SCL = 1;
	delay_us(2); 
	IIC_SCL = 0;    
}

5. IIC 发送NACK

c 复制代码
void IIC_NAck(void)
{
	// SDA_OUT(); // 推挽输出,则需要设置
	IIC_SDA = 1;    
	IIC_SCL = 0;
	delay_us(2); 
	IIC_SCL = 1;
	delay_us(2); 
	IIC_SCL = 0;    
}

6. IIC 等待应答信号到来(接收 ACK)

c 复制代码
#define READ_SDA PCin(11)  	  // 输入SDA 

u8 IIC_Wait_Ack(void)
{
   u8 ucErrTime = 0;
  // SDA_IN();      //SDA设置为输入,推挽输出,则需要设置  
   IIC_SDA = 1;     //释放SDA总线
   delay_us(2);	   
   IIC_SCL = 1;     //产生第9个时钟脉冲
   delay_us(2);	 
   while(READ_SDA) //等待从机拉低SDA表示ACK
   {
       ucErrTime++;
       if(ucErrTime>250)
       {
           IIC_Stop();
           return 1;
      }
   }
   IIC_SCL = 0;     //拉低SCL,完成ACK周期
   return 0;  
}

7. IIC 发送单个字节

c 复制代码
void IIC_Send_Byte(u8 Val)
{                        
	u8 i;   
	// SDA_OUT(); 	  // 推挽输出,则需要设置  
   IIC_SCL = 0;//拉低时钟开始数据传输
   for(i=0;i<8;i++)
   {              
		IIC_SDA = (Val>>(7-i))&0x01;
		delay_us(2);   //对TEA5767这三个延时都是必须的
		IIC_SCL = 1;
		delay_us(2); 
		IIC_SCL = 0;	
		delay_us(2);
   }	 
} 	  

8. IIC 读取单个字节

c 复制代码
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
 	for(i=0;i<8;i++ )
	{
		IIC_SCL = 0; 
		delay_us(2);
		IIC_SCL = 1;
		receive<<=1;
    	if(READ_SDA)
    		receive++;   
		delay_us(1); 
   }					 
   if (!ack)
       IIC_NAck();//发送nACK
   else
       IIC_Ack(); //发送ACK   
   return receive;
}

10. SPI

c 复制代码
// 1. 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );	// PORTA 时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI1, ENABLE );	// SPI1 时钟使能

// 2. 配置相关引脚的复用功能
GPIO_InitTypeDef 	GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_AF_PP; // PA5,6,7 复用推挽输出
GPIO_InitStructure.GPIO_Speed 	= GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);//PA5-SPI_SCK   PA6-SPI_MISO  PA7-SPI_MOSI  PA4-NRF_CE
GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7); // 初始化

// 3. 初始化 SPI,设置 SPI 工作模式
SPI_InitTypeDef 	SPI_InitStructure;
SPI_InitStructure.SPI_Direction 		= SPI_Direction_2Lines_FullDuplex; // 双线双向全双工
SPI_InitStructure.SPI_Mode 				= SPI_Mode_Master; 	// 主 SPI
SPI_InitStructure.SPI_DataSize 			= SPI_DataSize_8b; 	// SPI 发送接收 8 位帧结构
SPI_InitStructure.SPI_CPOL 				= SPI_CPOL_High;	// 串行同步时钟的空闲状态为高电平
SPI_InitStructure.SPI_CPHA 				= SPI_CPHA_2Edge;	// 第二个跳变沿数据被采样
SPI_InitStructure.SPI_NSS 				= SPI_NSS_Soft; 	// NSS 信号由软件控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; // 预分频 256
SPI_InitStructure.SPI_FirstBit 			= SPI_FirstBit_MSB; // 数据传输从 MSB 位开始
SPI_InitStructure.SPI_CRCPolynomial 	= 7; 				// CRC 值计算的多项式
SPI_Init(SPI1, &SPI_InitStructure); //根据指定的参数初始化外设 SPIx 寄存器

// 4. 使能 SPI
SPI_Cmd(SPI1, ENABLE); //使能 SPI 外设

// 5. SPI 传输数据
void 	 SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;

// 6. 读写一个字节
u8 SPI1_ReadWriteByte(u8 TxData)
{		
	u8 retry=0;				 	
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
	{
		retry++;
		if(retry>200)
			return 0;
	}			  
	SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个数据
	retry=0;

	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//检查指定的SPI标志位设置与否:接受缓存非空标志位
	{
		retry++;
		if(retry>200)
			return 0;
	}	  						    
	return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据					    
}

SPI 的 DMA 方式

c 复制代码
// 1. 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );	// PORTA 时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI1, ENABLE );	// SPI1 时钟使能
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);		// 使能DMA1时钟

// 2. 配置相关引脚的复用功能
GPIO_InitTypeDef 	GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_AF_PP; // 复用推挽输出(PA5 [ SCK ]  PA7 [ MOSI ])
GPIO_InitStructure.GPIO_Speed 	= GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_IN_FLOATING; // 浮空输入(PA6 [ MISO ])
GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_Out_PP; // 推挽输出(PA4 [ NSS ])
GPIO_InitStructure.GPIO_Speed 	= GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_SetBits(GPIOA, GPIO_Pin_4); // 片选默认高电平(未选中)

// 3. 配置 SPI 为主机模式
SPI_InitTypeDef 	SPI_InitStructure;
SPI_InitStructure.SPI_Direction 		= SPI_Direction_2Lines_FullDuplex;  // 全双工
SPI_InitStructure.SPI_Mode 				= SPI_Mode_Master;                  // 主机模式
SPI_InitStructure.SPI_DataSize 			= SPI_DataSize_8b;                  // 8位数据
SPI_InitStructure.SPI_CPOL 				= SPI_CPOL_Low;                     // 时钟极性低
SPI_InitStructure.SPI_CPHA 				= SPI_CPHA_1Edge;                   // 第一个边沿采样
SPI_InitStructure.SPI_NSS 				= SPI_NSS_Soft;                   	// 软件管理NSS
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; 		// 72MHz/16=4.5MHz
SPI_InitStructure.SPI_FirstBit 			= SPI_FirstBit_MSB;                 // MSB先行
SPI_InitStructure.SPI_CRCPolynomial 	= 7;                           		// CRC多项式
SPI_Init(SPI1, &SPI_InitStructure);

// 5. 使能 SPI
SPI_Cmd(SPI1, ENABLE);

// 6. 配置DMA中断优先级
NVIC_InitTypeDef 	NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel 					 = DMA1_Channel2_IRQn;	// 配置DMA1通道2中断(SPI1 TX)
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority 		 = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd 				 = ENABLE;
NVIC_Init(&NVIC_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel 					 = DMA1_Channel3_IRQn;	// 配置DMA1通道3中断(SPI1 RX)
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority 		 = 1;
NVIC_Init(&NVIC_InitStructure);

// 7. 配置DMA通道
DMA_DeInit(DMA1_Channel2);
DMA_InitStructure.DMA_PeripheralBaseAddr 	= (uint32_t)&SPI1->DR;     	 	// 外设地址
DMA_InitStructure.DMA_MemoryBaseAddr 		= (uint32_t)0;             	 	// 内存地址(稍后设置)
DMA_InitStructure.DMA_DIR 					= DMA_DIR_PeripheralDST;    	// 内存到外设
DMA_InitStructure.DMA_BufferSize 			= 0;                        	// 传输长度(稍后设置)
DMA_InitStructure.DMA_PeripheralInc 		= DMA_PeripheralInc_Disable;	// 外设地址不变
DMA_InitStructure.DMA_MemoryInc 			= DMA_MemoryInc_Enable;     	// 内存地址递增
DMA_InitStructure.DMA_PeripheralDataSize 	= DMA_PeripheralDataSize_Byte; 	// 8位
DMA_InitStructure.DMA_MemoryDataSize 		= DMA_MemoryDataSize_Byte;      // 8位
DMA_InitStructure.DMA_Mode 					= DMA_Mode_Normal;              // 正常模式(非循环)
DMA_InitStructure.DMA_Priority 				= DMA_Priority_Medium;          // 中等优先级
DMA_InitStructure.DMA_M2M 					= DMA_M2M_Disable;              // 非内存到内存
DMA_Init(DMA1_Channel2, &DMA_InitStructure);

DMA_DeInit(DMA1_Channel3);
DMA_InitStructure.DMA_PeripheralBaseAddr 	= (uint32_t)&SPI1->DR;      	// 外设地址
DMA_InitStructure.DMA_MemoryBaseAddr 		= (uint32_t)0;                 // 内存地址(稍后设置)
DMA_InitStructure.DMA_DIR 					= DMA_DIR_PeripheralSRC;       // 外设到内存
DMA_InitStructure.DMA_BufferSize 			= 0;                           // 传输长度(稍后设置)
DMA_InitStructure.DMA_PeripheralInc 		= DMA_PeripheralInc_Disable;   // 外设地址不变
DMA_InitStructure.DMA_MemoryInc 			= DMA_MemoryInc_Enable;        // 内存地址递增
DMA_InitStructure.DMA_PeripheralDataSize 	= DMA_PeripheralDataSize_Byte; // 8位
DMA_InitStructure.DMA_MemoryDataSize 		= DMA_MemoryDataSize_Byte;     // 8位
DMA_InitStructure.DMA_Mode					= DMA_Mode_Normal;             // 正常模式
DMA_InitStructure.DMA_Priority 				= DMA_Priority_Medium;         // 中等优先级
DMA_InitStructure.DMA_M2M 					= DMA_M2M_Disable;             // 非内存到内存
DMA_Init(DMA1_Channel3, &DMA_InitStructure);

// 8. 使能中断
DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE); // 使能传输完成中断
DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE);	// 使能传输完成中断

// 9. 编写中断服务函数
void DMA1_Channel2_IRQHandler(void)
{
   if(DMA_GetITStatus(DMA1_IT_TC2))
   {
       DMA_ClearITPendingBit(DMA1_IT_TC2);
       
       /* 关闭SPI的TX DMA请求 */
       SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, DISABLE);
   }
}
void DMA1_Channel3_IRQHandler(void)
{
   if(DMA_GetITStatus(DMA1_IT_TC3))
   {
       DMA_ClearITPendingBit(DMA1_IT_TC3);
       
       /* 关闭SPI的RX DMA请求 */
       SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, DISABLE);
          
       GPIO_SetBits(GPIOA, GPIO_Pin_4);	 // 拉高片选
   }
}

// 10. 启动一次 SPI DMA 传输
void SPI_DMA_StartTransfer(uint8_t *txBuf, uint8_t *rxBuf, uint16_t size)
{
   /* 清除DMA标志位 */
   DMA_ClearFlag(DMA1_FLAG_TC2);
   DMA_ClearFlag(DMA1_FLAG_TC3);
   
   /* 重新配置DMA的内存地址和传输长度 */
   DMA_Cmd(DMA1_Channel2, DISABLE);  // 先关闭通道才能修改配置
   DMA1_Channel2->CMAR = (uint32_t)txBuf;  // 内存地址
   DMA1_Channel2->CNDTR = size;            // 传输长度
   DMA_Cmd(DMA1_Channel2, ENABLE);
   
   DMA_Cmd(DMA1_Channel3, DISABLE);
   DMA1_Channel3->CMAR = (uint32_t)rxBuf;  // 内存地址
   DMA1_Channel3->CNDTR = size;            // 传输长度
   DMA_Cmd(DMA1_Channel3, ENABLE);
   
   GPIO_ResetBits(GPIOA, GPIO_Pin_4);	// 拉低片选
   
   /* 使能SPI的DMA请求 */
   SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
   SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);
}

11. 485

c 复制代码
// 1. 使能时钟
CC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); 	// 使能PA时钟 
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);	// 使能USART2时钟 
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);		// 使能DMA传输

// 2. 配置相关引脚
GPIO_InitTypeDef 	GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin		= GPIO_Pin_9; 	// GPIOA9
GPIO_InitStructure.GPIO_Mode	= GPIO_Mode_AF_PP;			// 复用推挽输出
GPIO_InitStructure.GPIO_Speed 	= GPIO_Speed_50MHz; 		// 速度100MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化PA9

GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_10; 				// GPIOA10 
GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_IN_FLOATING;	// 浮空输入
GPIO_Init(GPIOG,&GPIO_InitStructure); //初始化PA10

//3. 中断配置
NVIC_InitStructure.NVIC_IRQChannel 					= USART1_IRQn; 
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;			//抢占优先级3 
NVIC_InitStructure.NVIC_IRQChannelSubPriority 		=3;  		//响应优先级3 
NVIC_InitStructure.NVIC_IRQChannelCmd 				= ENABLE;	//IRQ通道使能 
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器

//4. 串口初始化设置 
USART_InitTypeDef 	USART_InitStructure; 
USART_InitStructure.USART_BaudRate 				= bound;				//波特率设置 
USART_InitStructure.USART_WordLength 			= USART_WordLength_8b;	//字长为8位 
USART_InitStructure.USART_StopBits 				= USART_StopBits_1;		//一个停止位 
USART_InitStructure.USART_Parity 				= USART_Parity_No;		//无奇偶校验位 
USART_InitStructure.USART_HardwareFlowControl 	= USART_HardwareFlowControl_None;//无硬件数据流控制 
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;			//收发  
USART_Init(USART1, &USART_InitStructure); //初始化串口2

//5. 开启串口中断
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);	// 空闲总线中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);	// 接收数据寄存器非空中断
USART_ITConfig(USART1, USART_IT_ORE, ENABLE);	// 溢出错误中断

//6. 使能中断
USART_Cmd(USART1, ENABLE);  //使能串口1

//7. 清除发送完成标志位
USART_ClearFlag(USART1, USART_FLAG_TC);

//8. 编写中断函数(接收串口数据)
void USART1_IRQHandler(void) 
{  
	if (USART_GetITStatus(USART1, USART_IT_ORE)!=RESET){	// 溢出错误中断
		USART_ClearITPendingBit(USART1, USART_IT_ORE);
	}

	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)	// 接收到数据(接收数据寄存器非空中断)
	{    
  		res = USART_ReceiveData(USART1);//;读取接收到的数据USART2->DR 
  		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}   

	if (USART_GetITStatus(USART1, USART_IT_IDLE)!=RESET)	// 空闲总线中断(接收完成)
	{
		RecvData		= (u8)(USART_ReceiveData(USART1));
		USART_ClearITPendingBit(USART1, USART_IT_IDLE);
	}            
} 

// 9. 编写发送函数
void RS485_Send_Data(u8 *buf,u8 len) 
{ 
	u8 t; 
	for(t=0;t<len;t++)  //循环发送数据 
	{ 
 		while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); //等待发送结束 
    	USART_SendData(USART2,buf[t]); //发送数据 
	}   
	while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); //等待发送结束 
} 


10. 下列为采用 DMA 模式发送
//10.1 等待DMA可配置
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}

//10.2 配置 DMA
DMA_DeInit(DMA1_Channel4);
DMA_InitStructure.DMA_PeripheralBaseAddr 	= (uint32_t)&USART1->DR;		// DMA外设ADC基地址
DMA_InitStructure.DMA_MemoryBaseAddr 		= (uint32_t)rx_buf;				// DMA内存基地址
DMA_InitStructure.DMA_DIR 					= DMA_DIR_PeripheralDST;		// 数据传输方向,从内存读取发送到外设
DMA_InitStructure.DMA_BufferSize 			= RX_BUF_LEN;					// DMA通道的DMA缓存的大小
DMA_InitStructure.DMA_PeripheralInc 		= DMA_PeripheralInc_Disable;	// 外设地址寄存器不变
DMA_InitStructure.DMA_MemoryInc 			= DMA_MemoryInc_Enable;			// 内存地址寄存器递增
DMA_InitStructure.DMA_PeripheralDataSize 	= DMA_PeripheralDataSize_Byte;	// 数据宽度为8位
DMA_InitStructure.DMA_MemoryDataSize 		= DMA_MemoryDataSize_Byte;		// 数据宽度为8位
DMA_InitStructure.DMA_Mode 					= DMA_Mode_Normal;     			// 工作在正常缓存模式
DMA_InitStructure.DMA_Priority 				= DMA_Priority_Medium;			// 中优先级 
DMA_InitStructure.DMA_M2M 					= DMA_M2M_Disable;				// 非内存到内存传输
DMA_Init(DMA1_Channel5, &DMA_InitStructure);

//10.3 使能指定 DMA 通道的传输完成中断
DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE); // 开启传输完成中断

//10.4 使能串口的DMA发送
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);

//10.5 关闭 DMA 传输
DMA_Cmd(DMA1_Channel4, DISABLE);

//10.6 编写DMA中断函数
void DMA1_Channel4_IRQHandler(void)
{
	if(DMA_GetFlagStatus(DMA1_FLAG_TC4))
	{
		DMA_ClearITPendingBit(DMA1_IT_TC4);
		pro = DMA_GetCurrDataCounter(DMA1_Channel4);//得到当前还剩余多少个数据
		DMA_Cmd(DMA1_Channel4,DISABLE);	// 关闭 DMA 传输
		......
		DMA_Cmd(DMA1_Channel4,ENABLE);	// 开启 DMA 传输
	}
}

12. CAN

c 复制代码
//1. 使能时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);	// 使能PORTA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);	// 使能CAN1时钟

// 2. 配置相关引脚
GPIO_InitTypeDef 	GPIO_InitStructure; 
GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_11| GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_AF;		//复用功能
GPIO_InitStructure.GPIO_OType 	= GPIO_OType_PP;	//推挽输出
GPIO_InitStructure.GPIO_Speed 	= GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd 	= GPIO_PuPd_UP;		//上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化PA11,PA12

GPIO_PinAFConfig(GPIOA,GPIO_PinSource11,GPIO_AF_CAN1); //GPIOA11复用为CAN1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_CAN1); //GPIOA12复用为CAN1

// 3. 设置 CAN 单元工作模式及波特率等
CAN_InitStructure.CAN_TTCM		= DISABLE;			// 非时间触发通信模式   
CAN_InitStructure.CAN_ABOM		= DISABLE;			// 软件自动离线管理	  
CAN_InitStructure.CAN_AWUM		= DISABLE;			// 睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
CAN_InitStructure.CAN_NART		= ENABLE;			// 禁止报文自动传送 
CAN_InitStructure.CAN_RFLM		= DISABLE;			// 报文不锁定,新的覆盖旧的  
CAN_InitStructure.CAN_TXFP		= DISABLE;			// 优先级由报文标识符决定 
CAN_InitStructure.CAN_Mode		= CAN_Mode_LoopBack;// 模式设置1,回环模式 
CAN_InitStructure.CAN_SJW		= CAN_SJW_1tq;		// 重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1tq~CAN_SJW_4tq
CAN_InitStructure.CAN_BS1		= CAN_BS1_7tq; 		// 范围CAN_BS1_1tq ~CAN_BS1_16tq
CAN_InitStructure.CAN_BS2		= CAN_BS2_6tq;		// 范围CAN_BS2_1tq ~	CAN_BS2_8tq
CAN_InitStructure.CAN_Prescaler	= 6;  				// 分频系数(Fdiv)为brp+1	
CAN_Init(CAN1, &CAN_InitStructure);   // 初始化CAN1 

// 4. 设置过滤器
CAN_FilterInitStructure.CAN_FilterNumber		= 0;	  //过滤器0
CAN_FilterInitStructure.CAN_FilterMode			= CAN_FilterMode_IdMask; 
CAN_FilterInitStructure.CAN_FilterScale			= CAN_FilterScale_32bit; //32位 
CAN_FilterInitStructure.CAN_FilterIdHigh		= 0x0000;////32位ID
CAN_FilterInitStructure.CAN_FilterIdLow			= 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh	= 0x0000;//32位MASK
CAN_FilterInitStructure.CAN_FilterMaskIdLow		= 0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment= CAN_Filter_FIFO0;//过滤器0关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation	= ENABLE; //激活过滤器0
CAN_FilterInit(&CAN_FilterInitStructure);//滤波器初始化

// 5. 开启串口中断
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);	// FIFO0消息挂号中断允许

//6. 设置中断优先级
NVIC_InitStructure.NVIC_IRQChannel 					 = CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	// 主优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority 		 = 0;	// 次优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd 				 = ENABLE;
NVIC_Init(&NVIC_InitStructure);

//7. 编写中断函数
void CAN1_RX0_IRQHandler(void)
{
 	CanRxMsg RxMessage;
	int i=0;
	CAN_Receive(CAN1, 0, &RxMessage);
	for(i=0;i<8;i++)
		printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data[i]);
}

//8. can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)	
u8 CAN1_Send_Msg(u8* msg,u8 len)
{	
	u8 mbox;
	u16 i=0;
	CanTxMsg TxMessage;
	TxMessage.StdId=0x12;	 // 标准标识符为0
	TxMessage.ExtId=0x12;	 // 设置扩展标示符(29位)
	TxMessage.IDE=0;		 // 使用扩展标识符
	TxMessage.RTR=0;		 // 消息类型为数据帧,一帧8位
	TxMessage.DLC=len;		 // 发送两帧信息
	for(i=0;i<len;i++)
		TxMessage.Data[i]=msg[i];// 第一帧信息          
	
	mbox= CAN_Transmit(CAN1, &TxMessage);   

	i=0;
	while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))	i++;	//等待发送结束
	if(i>=0XFFF)
		return 1;
	return 0;		
}

//9. can口接收数据查询
u8 CAN1_Receive_Msg(u8 *buf)
{		   		   
	u32 i;
	CanRxMsg RxMessage;
	if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)
		return 0;		//没有接收到数据,直接退出 

	CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//读取数据	

	for(i=0;i<RxMessage.DLC;i++)
		buf[i]=RxMessage.Data[i];  
	return RxMessage.DLC;	
}
相关推荐
sramdram16 小时前
基于32位微控制器的FOC汽车水泵方案
单片机·嵌入式硬件·汽车·微控制器·32位微控制器
开发笔记-阿牛19 小时前
解析一款有意思的产品||CK6159A语音芯片设计的儿童专注力训练机-舒尔特玩具
stm32·单片机·嵌入式硬件·音频
残影飞雪20 小时前
ESP32-C5下载烧录固件失败报错的原因
单片机·嵌入式硬件
搞全栈小苏21 小时前
SIMetrix_simplis8.4安装教程——附安装包
stm32·单片机·嵌入式硬件·电力电子
qq_573967821 天前
STM32控制器
stm32·单片机·嵌入式硬件
某林2121 天前
履带小车底盘stm32F103RCT6开发
人工智能·stm32·单片机·嵌入式硬件·架构·人机交互·ros2
sramdram1 天前
低功耗MCU微控制器在动态血糖仪(CGM)中的应用方案
单片机·嵌入式硬件·mcu·mcu微控制器
Dlrb12111 天前
ARM--IMX6ULL的SDK学习,BSP开发工程管理,以及链接脚本与Makefile的编写
单片机·嵌入式硬件·arm·sdk·链接脚本·i.mx6ull·cortex-a7
李永奉1 天前
杰理可视化SDK开发-配置ANC主动降噪功能模块教程
单片机·嵌入式硬件·物联网