外部中断
- [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 的中断函数
cITStatus 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 更新中断标志 } }
cITStatus 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
cvoid 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 开始信号
cvoid 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 停止信号
cvoid 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
cvoid 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
cvoid 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 发送单个字节
cvoid 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 读取单个字节
cu8 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; }







