当前位置: 首页 > news >正文

哪个网站可以做一对一老师招标网中标公示

哪个网站可以做一对一老师,招标网中标公示,网站百度流量怎么做,超市网站设计本内容基于江协科技STM32视频学习之后整理而得。 文章目录 1. ADC模拟-数字转换器1.1 ADC模拟-数字转换器1.2 逐次逼近型ADC1.3 ADC框图1.4 ADC基本结构1.5 输入通道1.6 规则组的转换模式1.6.1 单次转换#xff0c;非扫描模式1.6.2 连续转换#xff0c;非扫描模式1.6.3 单次…本内容基于江协科技STM32视频学习之后整理而得。 文章目录 1. ADC模拟-数字转换器1.1 ADC模拟-数字转换器1.2 逐次逼近型ADC1.3 ADC框图1.4 ADC基本结构1.5 输入通道1.6 规则组的转换模式1.6.1 单次转换非扫描模式1.6.2 连续转换非扫描模式1.6.3 单次转换扫描模式1.6.4 连续转换扫描模式 1.7 触发控制1.8 数据对齐1.9 转换时间1.10 校准1.11 硬件电路 2. AD库函数及代码2.1 AD库函数2.2 7-1AD单通道代码2.2.1 硬件电路2.2.2 代码流程2.2.3 代码 2.3 7-2AD多通道代码2.3.1 硬件电路2.3.2 硬件运行结果2.3.3 代码流程2.3.4 代码 3. DMA直接存储器存取3.1 DMA3.2 存储器映像3.3 DMA框图3.4 DMA基本结构3.5 DMA请求触发3.6 数据宽度与对齐3.7 数据转运DMA3.8 ADC扫描模式DMA 4. DMA库函数及代码4.1 DMA库函数4.2 8-1DMA数据转运4.2.1 硬件电路4.2.2 代码流程4.2.3 代码 4.3 8-2DMAAD多通道4.3.1 硬件电路4.3.2 代码流程4.3.3 代码 1. ADC模拟-数字转换器 1.1 ADC模拟-数字转换器 ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量建立模拟电路到数字电路的桥梁DAC数字模拟转换器PWM是数字到模拟的转换使用PWM来控制LED的亮度、电机的速度这就是DAC的功能同时PWM只有完全导通和完全断开两种状态在这两种状态上都没有功率损耗所以在直流电机调速这种大功率的应用场景中使用PWM来等效模拟量是比DAC更好的选择并且PWM电路更加简单更加常用所以可以看出PWM还是挤占了DAC的很多应用空间目前DAC的应用主要是在波形生成这些领域比如信号发生器、音频解码芯片。12位逐次逼近型ADC1us转换时间。12位表示分辨率范围0-2^12-10~4095位数越高量化结果就越精细对应分辨率就越高。转换时间即转换频率转换需要时间1us表示AD从转换开始到产生结果需要花1us的时间对应的AD转换频率就是1MHz输入电压范围0-3.3V转换结果范围0~409518个输入通道可测量16个外部和2个内部信号源规则组和注入组两个转换单元模拟看门狗自动监测输入电压范围ADC一般可以用于测量光线强度、温度这些值如果光线高于某个阈值低于某个阈值或者温度高于某个阈值低于某个阈值时执行一些操作低于某个阈值、高于某个阈值的判断就可以用模拟看门狗来自动执行模拟看门狗可以监测指定的某些通道当AD值高于它设定的上阈值或低于下阈值时它就会申请中断就可以在中断函数里执行相应的操作这样就不用不断地手动读值再用if进行判断了。STM32103C8T6 ADC资源ADC1、ADC210个外部输入通道就是最多只能测量10个外部引脚的模拟信号 1.2 逐次逼近型ADC ADC0809独立8位逐次逼近型ADC芯片IN0~IN78路输入通道通过通道选择开关选中一路输入到比较器进行转换。地址锁存和译码想选中哪个通道就把通道号放在ADDA~ADDC上然后给一个锁存信号对应的通路开关就可以自动拨好。通路选择开关相当于一个可以通过模拟信号的数据选择器。比较器电压比较器可以判断两个输入信号电压的大小关系输出一个高低电平指示谁大谁小。其输入一个是通道选择开关输出的待测电压另一个是DAC的电压输出端。DAC是数模转换器给DAC一个数据就能输出对应的电压值内部是使用加权电阻网络实现的转换。现在有了一个外部通道输入的未知编码的电压和一个DAC输出的已知编码的电压它俩同时输入到电压比较器进行大小判断。如果DAC输出的电压比较大就调小DAC数据反之输出电压比较小就增大DAC数据。直到DAC输出的电压和外部通道输入的电压近似相等。这样DAC输入的数据就是外部电压的编码数据了这就是DAC的实现原理该电压调节过程是逐次逼近寄存器SAR完成为了最快找到未知电压的编码采用二分法0~255每次对半分128、64、32这些数据正好是二进制每一位的位权。该判断过程相当于对二进制从高位到低位依次判断是1还是0的过程。对于8位的ADC从高位到低位依次判断8次就能找到未知电压的编码。AD转换结束后DAC的输入数据就是未知电压的编码通过8位三态锁存缓冲器输出。EOC是End of Convert转换结束信号START是开始转换给一个输入脉冲开始转换CLOCK是ADC时钟因ADC内部是一步一步进行判断的因此需要时钟来推动这个过程。VREF和VREF-是DAC的参考电压该参考电压也决定了ADC的输入范围所以也是ADC参考电压。 1.3 ADC框图 对于普通的ADC多路开关一般都是只选中一个的就是选中某一个通道、开始转换、等待转换完成、取出结果。但是在这里可以选中多个而且在转换的时候还分成了两个组规则通道组和注入通道组其中规则组可以一次性最多选中16个通道注入组最多可以选中4个通道但是规则组只有一个数据寄存器而注入组有4个数据寄存器用规则组需要使用DMA配合转运数据。规则组和注入组的触发源主要来自定时器有定时器的各个通道还有TRGO定时器主模式的输出可以选择TIM3定一个1ms的时间并且把TIM3的更新事件选择为TRGO输出然后在ADC里选择开始触发信号为TIM3的TRGO这样TIM3的更新事件就能通过硬件自动触发ADC转换了。也可以选择外部中断引脚来触发转换。VREF和VREF-是ADC的参考电压VDDA和VSSA是ADC的供电引脚一般VREF要接VDDAVREF-要接VSSAADCCLK是ADC的时钟用于驱动内部逐次比较的时钟是来自ADC预分频器这个ADC预分频器是来源于RCC的。 1.4 ADC基本结构 规则组最多可以选中16个通道注入组最多可以选择4个通道转换结果存放在AD数据寄存器里规则组只有1个数据寄存器注入组有4个触发控制提供了开始转换START信号可以选择软件触发和硬件触发。硬件触发主要来自于定时器也可以选择外部中断的引脚。 来自于RCC的ADC时钟CLOCKADC逐次比较的过程就是由这个时钟推动的可以布置一个模拟看门狗用于检测转换结果的范围若超出设定的阈值就通过中断输出控制向NVIC申请中断规则组和注入组转换完成后会有个EOC信号会置一个标志位也可以通向NVIC。开关控制在库函数中就是ADC_Cmd函数用于给ADC上电的。 1.5 输入通道 1.6 规则组的转换模式 在ADC初始化的结构体里会有两个参数参1是选择单次转换还是连续转换参2是选择扫描模式还是非扫描模式。 1.6.1 单次转换非扫描模式 在非扫描模式下该菜单只有第一个序列1的位置有效。菜单同时选中一组的方式就退化为简单地选中一个的方式。在序列1可以指定要转换的通道之后就可以触发转换ADC就会对这个通道2进行模数转换。转换完成后结果存放在数据寄存器里同时给EOC标志位置1转换结束。判断转换结束后就可以在数据寄存器里读取结果。若想再启动一次转换就需要再触发一次转换结束置EOC标志位读结果。若想换一个通道转换则在转换之前把第一个位置的通道2改为其他通道然后再启动转换。流程触发转换–判断转换结束置EOC标志位–获取转换值 1.6.2 连续转换非扫描模式 它在一次转换结束后不会停止而是立刻开始下一轮的转换然后一直持续下去。因此只需最开始触发一次之后就可以一直转换。优点是开始转换之后不需要等待一段时间想要读AD值的时候直接从数据寄存器取就是了。 1.6.3 单次转换扫描模式 每触发一次转换结束后就会停下来下次转换就得再触发才能开始。初始化结构体中还会有个参数通道数目若为7就是在每次触发之后依次对前7个位置进行AD转换转换结果都放在数据寄存器里。为了防止数据被覆盖就需要用DMA及时将数据挪走。7个通道转换完成之后产生EOC信号转换结束。然后再触发下一次就又开始新一轮的转换。 1.6.4 连续转换扫描模式 一次转换完成后立刻开始下一次的转换。 在扫描模式的情况下还有一种模式间断模式在扫描的过程中每隔几个转换就暂停一次需要再次触发才能继续。 1.7 触发控制 类型外部引脚/来自片上定时器的内部信号具体是引脚还是定时器需要用AFIO重映射来确定。 软件控制位软件触发 触发信号的选择可以通过设置右边的寄存器来完成。也可以使用库函数实现。 1.8 数据对齐 ADC是12位的其转换结果就是一个12位的数据。但数据寄存器是16位的所以存在数据对齐的问题 1.9 转换时间 量化编码即逐次比较采样保持是因为量化编码是需要一小段时间的如果在这一小段时间里输入的电压不断变化则无法定位输入电压的位置所以在量化编码之前需要设置一个采样开关先打开采样开关收集一下外部的电压之后断开采样开关再进行AD转换这样在量化编码的期间电压始终保持不变这样才能精确地定位未知电压的位置。采样时间就是采样保持的时间采样时间越大越能避免一些毛刺信号的干扰。但转换时间也会相应延长。12.5个ADC周期是量化编码花费的时间因为是12位的ADC所以需要花费12个周期0.5个周期是做其他用的。ADC周期就是从RCC分频过来的ADCCLKADCCLK最大是14MHz。 1.10 校准 ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间在每个电容器上都会计算出一个误差修正码(数字值)这个码用于消除在随后的转换中每个电容器上产生的误差建议在每次上电后执行一次校准启动校准前 ADC必须处于关电状态超过至少两个ADC时钟周期 1.11 硬件电路 第一个电位器可调电压的电路就是接一个电位器当滑动端往上滑时电压增大往下滑时电压减小。第二个传感器输出电压的电路如光敏电阻、热敏电阻、红外接收管、麦克风等都可以等效为一个可变电阻。传感器阻值变小时下拉作用变强输出端电压就下降传感器阻值变大时下拉作用变弱输出端受上拉电阻的作用电压就会升高。第三个电压转换电路若想测一个0-5V的VIN电压但ADC只能接收0-3.3V的电压就可以搭一个转换电路上面阻值17K下面阻值33K总共50K根据分压公式中间的电压就是VIN/50K*33K得到的电压范围就是0~3.3V就可以进入ADC转换了。 2. AD库函数及代码 2.1 AD库函数 // 配置ADCCLK分频器 void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);// DeInit恢复缺省配置、Init初始化、StructInit结构体初始化 void ADC_DeInit(ADC_TypeDef* ADCx); void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct); void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);// 给ADC上电的即开关控制 void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);// 开启DMA输出信号 void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);// 中断输出控制用于控制某个中断能不能通往NVIC void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);// 复位校准、获取复位校准状态 void ADC_ResetCalibration(ADC_TypeDef* ADCx); FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);// 开始校准、获取开始校准状态 void ADC_StartCalibration(ADC_TypeDef* ADCx); FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);// ADC软件开始转换控制用于软件触发的函数 void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);// ADC获取软件开始转换状态 FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);// 判断转换是否结束。获取标志位状态参数给EOC的标志位判断EOC标志位是不是置1了 FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);// 配置间断模式函数1每隔几个通道间断一次函数2是不是启用间断模式 void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number); void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);// ADC规则组通道配置给序列的每个位置填写指定的通道 void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);// ADC 外部触发转换控制就是是否允许外部触发转换 void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);// ADC换取转换值。就是获取AD转换的数据寄存器读取转换结果使用该函数 uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);// ADC获取双模式转换值是ADC模式读取转换结果的函数 uint32_t ADC_GetDualModeConversionValue(void);// 对ADC注入组进行配置 void ADC_AutoInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState); void ADC_InjectedDiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState); void ADC_ExternalTrigInjectedConvConfig(ADC_TypeDef* ADCx, uint32_t ADC_ExternalTrigInjecConv); void ADC_ExternalTrigInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState); void ADC_SoftwareStartInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState); FlagStatus ADC_GetSoftwareStartInjectedConvCmdStatus(ADC_TypeDef* ADCx); void ADC_InjectedChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime); void ADC_InjectedSequencerLengthConfig(ADC_TypeDef* ADCx, uint8_t Length); void ADC_SetInjectedOffset(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel, uint16_t Offset); uint16_t ADC_GetInjectedConversionValue(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel);// 对模拟看门狗进行配置函数1是否启动看门狗函数2配置高低阈值函数3配置看门的通道 void ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx, uint32_t ADC_AnalogWatchdog); void ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold, uint16_t LowThreshold); void ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel);// ADC温度传感器、内部参考电压控制用于开启内部的两个通道 void ADC_TempSensorVrefintCmd(FunctionalState NewState);// 获取中断状态、清除中断挂起位 FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG); void ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG); ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT); void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT);2.2 7-1AD单通道代码 2.2.1 硬件电路 实现功能接一个电位器即滑动变阻器用该电位器产生一个0~3.3V连续变化的模拟电压信号接到STM32的PA0口上之后用STM32内部的ADC读取电压数据显示在屏幕上。屏幕上第一行显示的是AD转换后的原始数据第二行是经过处理后实际的电压值。往左拧AD值减小电压值也减小AD值最小为0对应的电压就是0V往右拧AD值变大对应电压值也变大。STM32的ADC是12位的所以AD结果最大值是40952^12 - 1对应的电压是3.3V。 2.2.2 代码流程 开启RCC时钟包括ADC和GPIO时钟ADCCLK的分频器配置GPIO配置为模拟输入模式配置多路开关把左边的通道接入到右边的规则组列表里配置ADC转换器采用库函数结构体包括AD转换器和AD数据寄存器开关控制调用ADC_Cmd函数开启ADC想要软件触发转换有函数可以触发 2.2.3 代码 AD.c代码 #include stm32f10x.h // Device headervoid AD_Init(void) {/*1. 开启RCC时钟包括ADC和GPIO时钟ADCCLK的分频器2. 配置GPIO配置为模拟输入模式3. 配置多路开关把左边的通道接入到右边的规则组列表里4. 配置ADC转换器采用库函数结构体包括AD转换器和AD数据寄存器5. 开关控制调用ADC_Cmd函数开启ADC6. 想要软件触发转换有函数可以触发*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 6分频12MHzGPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN;// 模拟输入GPIO_InitStructure.GPIO_Pin GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA, GPIO_InitStructure);// 采样时间是55.5个ADCCLK的周期// 在规则组序列1的位置写入通道0ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);ADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode ADC_Mode_Independent; // 独立模式ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; //数据对齐右对齐ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; // 触发源软件触发ADC_InitStructure.ADC_ContinuousConvMode DISABLE;// 单次转换ADC_InitStructure.ADC_ScanConvMode DISABLE; // 非扫描模式ADC_InitStructure.ADC_NbrOfChannel 1; // 1个通道ADC_Init(ADC1,ADC_InitStructure);ADC_Cmd(ADC1, ENABLE);// 复位校准ADC_ResetCalibration(ADC1);while (ADC_GetResetCalibrationStatus(ADC1) SET);// 开始校准、获取开始校准状态ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1) SET);}uint16_t AD_GetValue(void) {// 软件触发ADC_SoftwareStartConvCmd(ADC1,ENABLE);// 判断转换结束while (ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) RESET); // 55.5 12.5 68个周期ADCCLK 12MHz1/12M*68 5.6us等待5.6us// ADC获取转换值return ADC_GetConversionValue(ADC1); } main.c代码 #include stm32f10x.h // Device header #include Delay.h #include OLED.h #include AD.huint16_t ADValue; float Voltage;int main(void) {OLED_Init();AD_Init();OLED_ShowString(1, 1, ADValue:);OLED_ShowString(2, 1, Voltage:0.00V);while(1){ADValue AD_GetValue();Voltage (float)ADValue / 4095 * 3.3;OLED_ShowNum(1, 9, ADValue, 4);OLED_ShowNum(2, 9, Voltage, 1);// 显示整数部分OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2);Delay_ms(100);} }2.3 7-2AD多通道代码 2.3.1 硬件电路 接了三个传感器光敏电阻、热敏电阻、反射式红外传感器把它们的AO模拟电压输出端分别接在了A1、A2、A3引脚加上原来的电位器总共四个输出通道然后测出来的4个AD数据分别显示在屏幕上AD0电位器往左拧减小往右拧增大AD1光敏电阻遮挡时电阻变大下拉作用变弱输出电压变大AD值增大AD2热敏电阻用手热一下温度升高阻值变小输出电压变小AD值减小AD3反射式红外传感器手靠近有反光AD值减小。 2.3.2 硬件运行结果 2.3.3 代码流程 开启RCC时钟包括ADC和GPIO时钟ADCCLK的分频器配置GPIO配置为模拟输入模式配置多路开关把左边的通道接入到右边的规则组列表里配置ADC转换器采用库函数结构体包括AD转换器和AD数据寄存器开关控制调用ADC_Cmd函数开启ADC想要软件触发转换有函数可以触发 2.3.4 代码 AD.c代码 #include stm32f10x.h // Device headervoid AD_Init(void) {/*1. 开启RCC时钟包括ADC和GPIO时钟ADCCLK的分频器2. 配置GPIO配置为模拟输入模式3. 配置多路开关把左边的通道接入到右边的规则组列表里4. 配置ADC转换器采用库函数结构体包括AD转换器和AD数据寄存器5. 开关控制调用ADC_Cmd函数开启ADC6. 想要软件触发转换有函数可以触发*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 6分频GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN;// 模拟输入GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA, GPIO_InitStructure);ADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode ADC_Mode_Independent; // 独立模式ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; //数据对齐右对齐ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; // 触发源软件触发ADC_InitStructure.ADC_ContinuousConvMode DISABLE;// 单次转换ADC_InitStructure.ADC_ScanConvMode DISABLE; // 非扫描模式ADC_InitStructure.ADC_NbrOfChannel 1; // 1个通道ADC_Init(ADC1,ADC_InitStructure);ADC_Cmd(ADC1, ENABLE);// 复位校准ADC_ResetCalibration(ADC1);while (ADC_GetResetCalibrationStatus(ADC1) SET);// 开始校准、获取开始校准状态ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1) SET);}uint16_t AD_GetValue(uint8_t ADC_Channel) {/*调用该函数时只要先指定通道返回值就是我们指定通道的结果.通道为 0、1、2、3*/// 采样时间是55.5个ADCCLK的周期// 通道为参数指定的通道ADC_RegularChannelConfig(ADC1,ADC_Channel,1,ADC_SampleTime_55Cycles5);// 软件触发ADC_SoftwareStartConvCmd(ADC1,ENABLE);// 判断转换结束while (ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) RESET); // 等待5.6us// ADC换取转换值return ADC_GetConversionValue(ADC1); } main.c #include stm32f10x.h // Device header #include Delay.h #include OLED.h #include AD.huint16_t AD0, AD1, AD2, AD3; float Voltage;int main(void) {OLED_Init();AD_Init();OLED_ShowString(1, 1, AD0:);OLED_ShowString(2, 1, AD1:);OLED_ShowString(3, 1, AD2:);OLED_ShowString(4, 1, AD3:);while(1){AD0 AD_GetValue(ADC_Channel_0);AD1 AD_GetValue(ADC_Channel_1);AD2 AD_GetValue(ADC_Channel_2);AD3 AD_GetValue(ADC_Channel_3);OLED_ShowNum(1, 5, AD0, 4);OLED_ShowNum(2, 5, AD1, 4);OLED_ShowNum(3, 5, AD2, 4);OLED_ShowNum(4, 5, AD3, 4);Delay_ms(100);} }3. DMA直接存储器存取 3.1 DMA DMA可以提取外设和存储器或者存储器和存储器之间的高速数据传输无须CPU干预节省了CPU的资源。外设指外设的寄存器一般是外设的数据寄存器DR如ADC的数据寄存器、串口的数据寄存器存储器是指运行内存SRAM和程序存储器Flash是存储变量数组和程序代码的地方12个独立可配置的通道DMA17个通道DMA25个通道每个通道都支持软件触发和特定的硬件触发如果DMA进行的是存储器到存储器的数据转运比如想把Flash里的一批数据转运到SRAM里去就需要软件触发。使用软件触发之后DMA就会把这批数据以最快的速度全部转运完成。如果DMA进行的是外设到存储器的数据转运由于外设的数据转运需要一定的时机就需要硬件触发比如转运ADC的数据那就得ADC每个通道AD转换完成后硬件触发一次DMA之后DMA再转运触发一次转运一次。所以存储器到存储器的转运一般用软件触发外设到存储器的转运一般用硬件触发。采用特定的硬件触发意思是每个DMA的通道的硬件触发源是不一样的要使用某个外设的硬件触发源就得使用它连接的那个通道而不能任意选择通道。STM32F103C8T6 DMA资源DMA17个通道 3.2 存储器映像 类型起始地址存储器用途ROM0x0800 0000程序存储器Flash存储C语言编译后的程序代码和常量·0x1FFF F000系统存储器存储BootLoader用于串口下载0x1FFF F800选项字节存储一些独立于程序代码的配置参数RAM0x2000 0000运行内存SRAM存储运行过程中的临时变量0x4000 0000外设寄存器存储各个外设的配置参数0xE000 0000内核外设寄存器存储内核各个外设的配置参数 计算器的5大组成部分运算器、控制器、存储器、输入设备和输出设备运算器和控制器合在一起称为CPU 因此计算机的核心关键部分就是CPU和存储器存储器又包括存储器的内容、存储器的地址ROM是只读存储器是一种非易失性、掉电不丢失的存储器。RAM是随机存储器是一种易失性、掉电丢失的存储器。程序存储器Flash存储C语言编译后的程序代码即下载程序的位置。运行程序一般也是从主闪存里面开始运行的。系统存储器和选项字节实际存储介质也是Flash。选项字节存的主要是Flash的读保护、写保护。运行内存SRAM存储运行过程中的临时变量就是在程序中定义变量、数组、结构体的地方。 3.3 DMA框图 主要包括CPU和存储器 Flash主闪存是ROM只读存储器的一种。如果通过总线直接访问的话无论是CPU还是DMA都是只读的只能读取数据而不能写入。如果DMA的目的地址填了Flash的区域转运时就会出错。可以配置Flash接口控制器对Flash进行写入。SRAM是运行内存可以任意读写。数据寄存器可以正常读写。各种外设都可以看成是寄存器也是一种SRAM存储器。寄存器一种特殊的存储器。一方面CPU可以对寄存器进行读写就像读写运行内存一样。另一方面寄存器的每一位背后都连接了一根导线。这些导线可以用于控制外设电路的状态如置引脚的高低电平、导通和断开开关、切换数据选择器或者多位组合起来当做计数器、数据寄存器。因此寄存器是连接软件和硬件的桥梁。软件读写寄存器就相当于在控制硬件的执行。 因此外设就是寄存器寄存器就是存储器。因此DMA转运就可以归结为一类问题就是从某个地址取内容再放到另一个地址去。总线矩阵的左端是主动单元也就是拥有存储器的访问权右端是被动单元它们的存储器只能被左边的主动单元读写。主动单元里内核有DCode和系统总线可以访问右边的存储器。DCode总线是专门访问Flash的系统总线是访问其他东西的。另外由于DMA要转运数据所以DMA也必须要有访问的主动权。DMA1有一条DMA总线DMA2有一条DMA总线DMA1有7个通道DMA2有5个通道。各个通道可以分别设置它们转运数据的源地址和目的地址可以各自独立地工作。虽然多个通道可以独立转运数据但DMA总线只有一条所以所有的通道都只能分时复用一条DMA总线如果产生了冲突那就会由仲裁器根据通道的优先级来决定谁先用谁后用。在总线矩阵这边也会有个仲裁器如果DMA和CPU都要访问同一个目标则DMA就会暂停CPU的访问以防止冲突。但总线仲裁器仍会保证CPU得到一半的总线带宽使CPU也能正常的工作。AHB从设备即DMA自身的寄存器。DMA作为一个外设其也会有相应的配置寄存器连接在了总线右边的AHB总线上。因此DMA即是总线矩阵的主动单元可以读写各种存储器也是AHB总线上的被动单元。DMA请求即是触发的意思。其右边的触发源是各个外设。所以DMA请求就是DMA的硬件触发源如ADC转换完成、串口接收到数据。需要触发DMA转运数据的时候通过DMA请求线路向DMA发出硬件触发信号之后DMA就可以执行数据转运的工作。DMA的工作用于访问各个存储器的DMA总线内部的多个通道可以进行独立的数据转运仲裁器用于调度各个通道防止产生冲突AHB从设备用于配置DMA参数DMA请求用于硬件触发DMA的数据转运。 3.4 DMA基本结构 左边的外设寄存器与右边的寄存器FlashSRAM是数据转运的两大站点。DMA的数据转运向右的外设到存储器向左的存储器到外设可以通过方向参数控制。另一种转运方式存储器到存储器如Flash到SRAM或SRAM到SRAM。转运方法外设和存储器都包括三个参数起始地址决定了数据从哪里来到哪里去数据宽度指定一次转运要按多大的数据宽度来进行可以选择字节Byte、半字HalfWord和字Word。字节Byte是8位一次转运uint8_t半字HalfWord是16位一次转运uint16_t字Word是32位一次转运uint32_t。地址是否自增指定一次转运完成后下一次转运是不是要把地址移动到下一个位置去。 若要进行存储器到存储器的转运那就需要把其中一个存储器的地址放在外设的这个站点。传输计数器用来指定总共需要转运几次的。是自减计数器。自动重装器当传输计数器减到0之后是否要自动恢复到最初的值。比如最初传输计数器给5如果不使用自动重装器那转运5次后DMA就结束了。如果使用自动重装器那转运5次后计数器减到0后就会立即重装到初始值5。触发就是决定DMA需要在什么时机进行转运。触发源有硬件触发和软件触发具体选择由M2M存储器到存储器参数决定M2M为1选择软件触发以最快的速度连续不断地触发DMA争取早日把传输计数器清零完成这一轮的转换。软件触发和自动重装器不能同时使用。软件触发是想把传输计数器清零循环模式是清零后自动重装。软件触发适用于存储器到存储器的转运因为存储器到存储器的转运是软件启动、不需要时机并且想尽快完成的任务。 M2M为0就是硬件触发硬件触发源可以选择ADC、串口、定时器使用硬件触发的转运一般都是与外设有关的转运。转运需要一定的时机如ADC转换完成、串口收到数据、定时时间到等在硬件达到这些时机时传一个信号过来来触发DMA进行转运。开关控制EN位DMA_Cmd函数。当给DMA使能后DMA就准备就绪可以进行转运了。DMA转运条件1开关控制DMA_Cmd必须使能2传输计数器必须大于03触发源必须有触发信号。触发一次转运一次传输计数器自减一次。当传输计数器等于0且没有自动重装时这时无论是否触发DMA都不会再进行转运了。此时就需要DMA_Cmd给DISABLE关闭DMA。再为传输计数器写入一个大于0的数再DMA_Cmd给ENABLE开启DMA继续工作。注意写传输计数器时必须先关闭DMA再进行。 3.5 DMA请求触发 默认优先级是通道号越小优先级越高。也可以在程序中配置优先级。 3.6 数据宽度与对齐 如果数据宽度都一样就是正常的一个一个转运。当源端和目标都是8位时转运第一步在源端的0位置读数据B0在目标的0位置写数据B0就是把这个B0从左边挪到右边。之后的步骤就是把B1从左边挪到右边之后B2、B3。当源端是8位目标是16位其操作是在源端读B0在目标写00B0之后读B1写00B1。就是如果目标宽度比源端数据宽度大那就在目标数据前面多出来的空位补0。当目标数据宽度比源端数据宽度小时像从16位转运到8位时就是读B1B0只写入B0读B3B2只写入B2也就是把多出来的高位舍弃掉。总结如果把小的数据转到大的里面去高位就会补0如果把大的数据转到小的里面去高位就会舍弃掉如果数据宽度一样那就没事。 3.7 数据转运DMA 外设地址DataA数组的首地址存储器地址DataB数组的首地址起始地址DataA[0]和DataB[0]数据宽度8位地址是否自增是两边地址都要自增。方向参数外设站点 -存储器站点传输计数器7不需要自动重装触发软件触发 3.8 ADC扫描模式DMA 触发一次后7个通道依次进行AD转换转换结果都放到ADC_DR数据寄存器里面。需要做的是在每次转换完成后进行一个DMA数据转运并且目的地址进行自增。DMA配置1外设地址写入ADC_DR这个寄存器的地址存储器的地址可以在SRAM中定义一个数组ADValue然后把ADValue的地址当作存储器的地址2数据宽度因为ADC_DR和SRAM数组都是uint16_t因此数据宽度都是16位的半字传输3地址是否自增外设地址不自增存储器地址自增传输方向外设站点 - 存储器站点传输计数器7个。计数器是否自动重装可以看ADC的配置。若ADC是单次扫描那DMA的传输计数器可以不自动重装转换一轮就停止。若ADC是连续扫描那DMA就可以使用自动重装在ADC启动下一轮转换的时候DMA也启动下一轮的转运ADC和DMA同步工作。触发选择ADC_DR的值是在ADC单个通道转换完成后才会生效所以DMA转运的时机需要和ADC单个通道转换完成同步所以DMA的触发要选择ADC的硬件触发。ADC扫描模式在每个单独的通道转换完成后没有任何标志位也不会触发中断所以程序不好判断某个通道转换完成的时机是什么时候但应该会产生DMA请求去触发DMA转运。 4. DMA库函数及代码 4.1 DMA库函数 // ADC1_BASE是ADC1的基地址基地址就是起始地址即4001 2400 #define ADC1 ((ADC_TypeDef *) ADC1_BASE) #define ADC1_BASE (APB2PERIPH_BASE 0x2400) #define APB2PERIPH_BASE (PERIPH_BASE 0x10000) #define PERIPH_BASE ((uint32_t)0x40000000) /*! Peripheral base address in the alias region */typedef struct {__IO uint32_t SR;__IO uint32_t CR1;__IO uint32_t CR2;__IO uint32_t SMPR1;__IO uint32_t SMPR2;__IO uint32_t JOFR1;__IO uint32_t JOFR2;__IO uint32_t JOFR3;__IO uint32_t JOFR4;__IO uint32_t HTR;__IO uint32_t LTR;__IO uint32_t SQR1;__IO uint32_t SQR2;__IO uint32_t SQR3;__IO uint32_t JSQR;__IO uint32_t JDR1;__IO uint32_t JDR2;__IO uint32_t JDR3;__IO uint32_t JDR4;__IO uint32_t DR; } ADC_TypeDef;ADC1-DR // ADC1是结构体指针指向的是ADC1外设的起始地址 // 访问结构体成员相当于是加一个地址偏移。 // 起始地址 偏移就是指定的寄存器// 恢复缺省配置 void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx); // 初始化 void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct); // 结构体初始化 void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct); // 使能 void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState); // 中断输出使能 void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState); // DMA设置当前数据寄存器给传输计数器写数据的 void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber); // DMA获取当前数据寄存器返回传输计数器的值 uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx); // 获取标志位状态 FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG); // 清除标志位 void DMA_ClearFlag(uint32_t DMAy_FLAG); // 获取中断状态 ITStatus DMA_GetITStatus(uint32_t DMAy_IT); // 清除中断挂起位 void DMA_ClearITPendingBit(uint32_t DMAy_IT); 4.2 8-1DMA数据转运 4.2.1 硬件电路 使用DMA进行存储器到存储器的数据转运把一个数组源数组的数据转运到另一个数组目的数组中。采用软件触发。 在OLED中显示源数组DataA 、地址及数据目的数组DataB、地址及数据。 4.2.2 代码流程 RCC开启DMA的时钟AHB时钟调用DMA_Init初始化参数外设和存储器站点的起始地址、数据宽度、地址是否自增、方向、传输计数器、是否需要自动重装、选择触发源开关控制DMA_Cmd如果是硬件触发在对应的外设调用一下xxx_DMACmd开启一下触发信号的输出如果需要DMA的中断调用DMA_ITConfig开启中断输出再在NVIC里配置相应的中断通道写中断函数如果转运完成传输计数器清0了若再想给传输计数器赋值的话就DMA失能、写传输计数器、DMA使能。 4.2.3 代码 MyDMA.c代码 #include stm32f10x.h // Device headeruint16_t MyDMA_Size;void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint32_t Size) { /* 1. RCC开启DMA的时钟 2. 调用DMA_Init初始化参数外设和存储器站点的起始地址、数据宽度、地址是否自增、方向、传输计数器、是否需要自动重装、选择触发源 3. 开关控制DMA_Cmd 4. 如果是硬件触发在对应的外设调用一下xxx_DMACmd开启一下触发信号的输出 5. 如果需要DMA的中断调用DMA_ITConfig开启中断输出再在NVIC里配置相应的中断通道写中断函数 6. 如果转运完成传输计数器清0了若再想给传输计数器赋值的话就DMA失能、写传输计数器、DMA使能。 */// DMA是AHB总线的设备需要开启AHB时钟MyDMA_Size Size;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr AddrA; // 外设站点的起始地址 DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_Byte; // 外设站点的数据宽度DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Enable ; // 外设站点的是否自增DMA_InitStructure.DMA_MemoryBaseAddr AddrB; // 存储器站点的起始地址 DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_Byte; // 存储器站点的数据宽度DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; // 存储器站点的是否自增DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralSRC; // 传输方向DMA_InitStructure.DMA_BufferSize Size; // 缓存区大小即传输计数器DMA_InitStructure.DMA_Mode DMA_Mode_Normal; // 传输模式就是是否使用自动重装DMA_InitStructure.DMA_M2M DMA_M2M_Enable; // 选择是否是存储器到存储器即选择软件触发还是硬件触发DMA_InitStructure.DMA_Priority DMA_Priority_Medium; // 优先级DMA_Init(DMA1_Channel1, DMA_InitStructure);/*DMA转运有三个条件1. 传输计数器大于02. 触发源有触发信号3. DMA使能*/DMA_Cmd(DMA1_Channel1, DISABLE); }// DMA传输函数调用依次这个函数就再次启动一次DMA转运 void MyDMA_Transfer(void) {// 重新给传输计数器赋值先使DMA失能DMA_Cmd(DMA1_Channel1, DISABLE);DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size);DMA_Cmd(DMA1_Channel1, ENABLE);// 等待转运完成while (DMA_GetFlagStatus(DMA1_FLAG_TC1) RESET);DMA_ClearFlag(DMA1_FLAG_TC1); } main.c #include stm32f10x.h // Device header #include Delay.h #include OLED.h #include MyDMA.h/* uint8_t bb 0x66; // 存储在SRAM中 const uint8_t aa 0x66; 存储在Flash中const定义的变量是常量值不能更改 Flash是只读不能写入因此const与Flash对应 */uint8_t DataA[] {0x01, 0x02, 0x03, 0x04};// 源端数组 uint8_t DataB[] {0, 0, 0, 0};// 目标数组int main(void) {OLED_Init();// 把DataA数组的数据转运到DataB里MyDMA_Init((uint32_t)DataA, (uint32_t)DataB, 4);OLED_ShowString(1, 1, DataA);OLED_ShowString(3, 1, DataB);OLED_ShowHexNum(1, 8, (uint32_t)DataA, 8);// 显示DataA的地址OLED_ShowHexNum(3, 8, (uint32_t)DataB, 8);OLED_ShowHexNum(2, 1, DataA[0], 2);OLED_ShowHexNum(2, 4, DataA[1], 2);OLED_ShowHexNum(2, 7, DataA[2], 2);OLED_ShowHexNum(2, 10, DataA[3], 2);OLED_ShowHexNum(4, 1, DataB[0], 2);OLED_ShowHexNum(4, 4, DataB[1], 2);OLED_ShowHexNum(4, 7, DataB[2], 2);OLED_ShowHexNum(4, 10, DataB[3], 2);while(1){DataA[0] ;DataA[1] ;DataA[2] ;DataA[3] ;OLED_ShowHexNum(2, 1, DataA[0], 2);OLED_ShowHexNum(2, 4, DataA[1], 2);OLED_ShowHexNum(2, 7, DataA[2], 2);OLED_ShowHexNum(2, 10, DataA[3], 2);OLED_ShowHexNum(4, 1, DataB[0], 2);OLED_ShowHexNum(4, 4, DataB[1], 2);OLED_ShowHexNum(4, 7, DataB[2], 2);OLED_ShowHexNum(4, 10, DataB[3], 2);Delay_ms(1000);MyDMA_Transfer();OLED_ShowHexNum(2, 1, DataA[0], 2);OLED_ShowHexNum(2, 4, DataA[1], 2);OLED_ShowHexNum(2, 7, DataA[2], 2);OLED_ShowHexNum(2, 10, DataA[3], 2);OLED_ShowHexNum(4, 1, DataB[0], 2);OLED_ShowHexNum(4, 4, DataB[1], 2);OLED_ShowHexNum(4, 7, DataB[2], 2);OLED_ShowHexNum(4, 10, DataB[3], 2);Delay_ms(1000);} }4.3 8-2DMAAD多通道 4.3.1 硬件电路 使用ADC的扫描模式来实现多通道采集然后使用DMA来进行数据转运AD转换的数据就会自动到定义的数组里然后用OLED显示一下。 4.3.2 代码流程 开启RCC时钟包括ADC和GPIO时钟ADCCLK的分频器配置GPIO配置为模拟输入模式配置多路开关把左边的通道接入到右边的规则组列表里配置ADC转换器采用库函数结构体包括AD转换器和AD数据寄存器开关控制调用ADC_Cmd函数开启ADC想要软件触发转换有函数可以触发 4.3.3 代码 AD.c代码 #include stm32f10x.h // Device headeruint16_t AD_Value[4];void AD_Init(void) {/*1. 开启RCC时钟包括ADC和GPIO时钟ADCCLK的分频器2. 配置GPIO配置为模拟输入模式3. 配置多路开关把左边的通道接入到右边的规则组列表里4. 配置ADC转换器采用库函数结构体包括AD转换器和AD数据寄存器5. 开关控制调用ADC_Cmd函数开启ADC6. 想要软件触发转换有函数可以触发*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 6分频GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN;// 模拟输入GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA, GPIO_InitStructure);// 4个通道ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5);ADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode ADC_Mode_Independent; // 独立模式ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; //数据对齐右对齐ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; // 触发源软件触发ADC_InitStructure.ADC_ContinuousConvMode ENABLE;// 单次转换,循环ADC_InitStructure.ADC_ScanConvMode ENABLE; // 扫描模式ADC_InitStructure.ADC_NbrOfChannel 4; // 4个通道ADC_Init(ADC1,ADC_InitStructure);DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)ADC1-DR; // 外设站点的起始地址 ADCDMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_HalfWord; // 外设站点的数据宽度DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Disable ; // 外设站点的是否自增,否DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)AD_Value; // 存储器站点的起始地址 SRAMDMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_HalfWord; // 存储器站点的数据宽度DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; // 存储器站点的是否自增是DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralSRC; // 传输方向DMA_InitStructure.DMA_BufferSize 4; // 缓存区大小即传输计数器DMA_InitStructure.DMA_Mode DMA_Mode_Circular; // 传输模式就是是否使用自动重装DMA_InitStructure.DMA_M2M DMA_M2M_Disable; // 选择是否是存储器到存储器即选择硬件触发还是软件触发DMA_InitStructure.DMA_Priority DMA_Priority_Medium; // 优先级DMA_Init(DMA1_Channel1, DMA_InitStructure);DMA_Cmd(DMA1_Channel1, ENABLE);ADC_DMACmd(ADC1, ENABLE);// 开启DMA触发信号ADC_Cmd(ADC1, ENABLE);// 复位校准ADC_ResetCalibration(ADC1);while (ADC_GetResetCalibrationStatus(ADC1) SET);// 开始校准、获取开始校准状态ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1) SET);ADC_SoftwareStartConvCmd(ADC1,ENABLE); }/* void AD_GetValue(void) {调用该函数ADC开始转换DMA也同步进行转运AD转换结果依次放在这上面的AD_Value数组里DMA_Cmd(DMA1_Channel1, DISABLE);DMA_SetCurrDataCounter(DMA1_Channel1,4);DMA_Cmd(DMA1_Channel1, ENABLE);while (DMA_GetFlagStatus(DMA1_FLAG_TC1) RESET);DMA_ClearFlag(DMA1_FLAG_TC1); } */ main.c代码 #include stm32f10x.h // Device header #include Delay.h #include OLED.h #include AD.hint main(void) {OLED_Init();AD_Init();OLED_ShowString(1, 1, AD0:);OLED_ShowString(2, 1, AD1:);OLED_ShowString(3, 1, AD2:);OLED_ShowString(4, 1, AD3:);while(1){// AD_GetValue();OLED_ShowNum(1, 5, AD_Value[0], 4);OLED_ShowNum(2, 5, AD_Value[1], 4);OLED_ShowNum(3, 5, AD_Value[2], 4);OLED_ShowNum(4, 5, AD_Value[3], 4);Delay_ms(100);} }
http://www.hkea.cn/news/14464351/

相关文章:

  • wordpress 网站重置动漫网站设计源代码
  • 网站建设项目团队组织结构图设计定制型网站建设
  • 手机网站建设 移商动力怎么找广告商接广告
  • 织梦 电影网站 模板常州网站建设平台
  • 青岛专业做网站优化帮齐家网做的网站
  • 商城网站建设所必备的四大功能是哪些网站建设题库含答案
  • 电子商务网站设计与维护新手做网站推荐
  • 中国网站排行榜前100名app引导页模板html
  • 自做网站告白如何弄站外做deal的网站
  • 网站建设主要职责兼职制作网站开发
  • 餐饮酒店网站怎么做一家专门做动漫的网站
  • 网站开发小图标怎么设置宁波建设局网站郑建华
  • 电子元器件在哪个网站上做济源制作网站
  • 网站原型设计流程福州有网站开发的公司吗
  • app 设计网站建设电子商务网站的建设与规划
  • 宁波cms建站网站建设最好的公司排名
  • 优化网站推广设计网站推荐素材网站
  • 遂宁建设局网站首页温州在线制作网站
  • 北京网站建设流程帮做装修设计的网站
  • 做一个综合商城网站多少钱网站推广方案设计
  • 云南省网站建设公司韶关做网站公司
  • 服务器域名已有做网站wordpress 去除评论框
  • 中国建设银行官网站诚聘英才网站建设 生产
  • 重庆模板做网站管理咨询公司怎么找客户
  • .design 域名的网站汕头网站推广seo
  • 做网站 江门做互联网的网站
  • 注册网站建设公司主营项目类别游戏网站首页设计
  • 广州网站建设联系电话电脑做网站软件
  • 送菜网站制作大丰企业做网站多少钱
  • 江苏建设招标网站网站建设备案优化设