申明 杭州艾研信息技术有限公司保留随时对其产品进行修正 改进和完善的权利, 同时也保留在不作任何通告的情况下, 终止其任何一款产品的供应的权利 用户在下订单前应及时获取相关信息的最新版本, 并验证这些信息是当前的和完整的 可通过如下方式获取最新信息 技术资料和技术支持 : 技术支持电话 :0571-

Save this PDF as:
 WORD  PNG  TXT  JPG

Size: px
Start display at page:

Download "申明 杭州艾研信息技术有限公司保留随时对其产品进行修正 改进和完善的权利, 同时也保留在不作任何通告的情况下, 终止其任何一款产品的供应的权利 用户在下订单前应及时获取相关信息的最新版本, 并验证这些信息是当前的和完整的 可通过如下方式获取最新信息 技术资料和技术支持 : 技术支持电话 :0571-"

Transcription

1 第 4 章电阻测量模块 杭州艾研信息技术有限公司 2014 年 11 月 - 1 -

2 申明 杭州艾研信息技术有限公司保留随时对其产品进行修正 改进和完善的权利, 同时也保留在不作任何通告的情况下, 终止其任何一款产品的供应的权利 用户在下订单前应及时获取相关信息的最新版本, 并验证这些信息是当前的和完整的 可通过如下方式获取最新信息 技术资料和技术支持 : 技术支持电话 : 技术支持邮箱 产品 & 资料下载中心 : 互动论坛 : 公司地址 : 浙江省杭州市西湖区留和路 16 号新峰商务楼 B402 更多资讯请添加艾研信息官方微信 ( 搜索公众号 : 艾研 ) 或扫一 扫下方二维码 :

3 第 4 章电阻测量模块 精确电阻测量并非易事, 电阻测量模块提供了两种高精度测电阻的应用范例 : 电桥法测电阻和恒流源法测电阻 电桥法是将待测电阻代替原来已经平衡的电桥的一个桥臂, 采用基准源为电桥供电, 通过测量因电桥桥臂失衡产生的电压来获得待测电阻的值 ; 恒流源法是设计一个在一定负载范围内能提供精确恒定电流的恒流源, 待测电阻作为恒流源负载, 测量待测电阻两端的电压即可获得电阻阻值 为了提供性能比较, 模块同时搭配两种不同的放大采集电路 : 一路采用仪用运算放大器 INA333 提供高共模抑制比的信号放大功能, 再由 TIVA Cortex M4 自带的 12 位 ADC 进行数据采集 ; 另一路直接采用 型 ADC ADS1100 进行采集, 并通过 I 2 C 将数据传给 TIVA Cortex M4 通过在模块上的跳线配置可以实现两种不同的前端测试电路和两种放大采集电路的交叉搭配, 测试并比较不同配置方式的性能指标, 可获得对不同测量方式和不同电路特性的认识 1

4 2

5 3

6 4

7 5

8 6

9 软件流程图 1 程序启动 2 液晶初始化 3 ADC 采样初始化 4 I2C 初始化 5 液晶显示字符 5 程序主循环 读取电压 ADC 显示码值 通过 I2C 读取 ADS1100 数据显示原始数据 系统延时 结束 是 程序终止 否 ADC 采样外设端口的电平根据和参考电平的比较, 将数字量化输出 PB4 采集电平转化的 ADC 采样值 ADS1100 芯片通过 I2C 通信协议与 TIVA LaunchPad 进行通信和数据交换 外设端口 PA6 PA7 设置为 I2C 的时钟和数据信号线, 实现与 TIVA 的通信 LCD 显示诸如 INA333 Measure:xx V, ADS1100 Measure: xx V 等信息, 便于实验过程中观察实验数据变化 周期性的通过 ADC 采样获取电平的 ADC 采样值, 并在 LCD 直接显示出 ADC 的转化码值 1 2 电阻测量模块流程图 USB 线连接 TIVA LaunchPad 和计算机, 使用 CCS 软件烧写程序 完成烧写后,TIVA LaunchPad 上电后自行运行程序 液晶 (LCD) 初始化详细内容参见第三章 LCD 模块相关内容 周期性的通过 I2C 通信采样获取 ADS1100 采样码值并通过 LCD 直接显示 为了防止 LCD 显示内容过快的重复刷新, 通过主动系统延时将主循环控制在较低的刷新频率上 ( 如 5Hz) 当 TIVA LaunchPad 掉电后, 程序会跳出主循环, 程序终止 7

10 关键代码分析 根据软件流程图的分析可知 获取两种不同方式的电压数字量, 一个需要初始化 Tiva LaunchPad 的 ADC 功能并实时采样 ; 一个需要初始化 I2C 通信并将接收到的数据合成为有效数据 ADC 的初始化和数据采样 1 ADC 初始化 /******************************************************************** * 初始化 ADC 获取滚轮电压值, 用于电桥电路测量电阻 * // // M4 PB4 <--ADC0 模数转换信号源 // ********************************************************************/ #define ADC_BASE ADC0_BASE // 使用 ADC0 #define SequenceNum 3 // 使用序列 3 void Init_ADC_Detect(){ // 使能 ADC0 外设 ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0); 8

11 } // 使能 Port B 外设端口 ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); // 选择 PB4 作为模数装换 ADC 的管脚 ROM_GPIOPinTypeADC(GPIO_PORTB_BASE, GPIO_PIN_4); // 配置采样序列的触发源和优先级 ROM_ADCSequenceConfigure(ADC_BASE, SequenceNum, ADC_TRIGGER_PROCESSOR, 0); // 配置采样序列发生器的步进 ROM_ADCSequenceStepConfigure(ADC_BASE, SequenceNum, 0, ADC_CTL_CH10 ADC_CTL_IE ADC_CTL_END); // 使能一个采样序列 ROM_ADCSequenceEnable(ADC_BASE, SequenceNum); // 清除采样序列中断源 ROM_ADCIntClear(ADC_BASE, SequenceNum); 2 ADC 数据采样在程序主循环中以一定更新频率的不断采样 ADC 外设端口的电压值 while(1) { // 对 while 做 125ms 的延时, 每秒刷新频率为 8Hz 9

12 ROM_SysCtlDelay(SysCtlClockGet() / 3 / 30); } //ADC 测电阻 ADCProcessorTrigger(ADC_BASE, SequenceNum); // 等待完成 AD 转换 while(!adcintstatus(adc_base, SequenceNum, false)) { } // 清楚 ADC 中断标志位 ADCIntClear(ADC_BASE, SequenceNum); // 读取 ADC 值 ADCSequenceDataGet(ADC_BASE, SequenceNum, pui32adc0value); // 根据参考电平 3.3V 将获取的数字量转化为实际电压值 sample_bridge_average = (pui32adc0value[0] * 3300) / 4096;... 通过 I 2 C 控制 ADS1100, 采集电压值 1 I 2 C 通信初始化 /******************************************************************** 10

13 * 初始化 AI2C 获取 ADS1100 上的 ADC 电压数据, 用于恒流源测量电阻 * // // M4 PA6 <--SCL I2C 协议时钟信号 // PA7 <--SDA I2C 协议数据信号 // ********************************************************************/ void Init_I2C_Comm() { // 使能 I2C1 外设 SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C1); // 使能 PortA 外设端口 SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); // 配置 PA6 PA7 为上拉端口 GPIOPadConfigSet(GPIO_PORTA_BASE, GPIO_PIN_6 GPIO_PIN_7, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU); // PA6 配置为 I2C 协议时钟信号 PA7 配置为 I2C 协议数据信号 GPIOPinConfigure(GPIO_PA6_I2C1SCL); 11

14 GPIOPinConfigure(GPIO_PA7_I2C1SDA); GPIOPinTypeI2C(GPIO_PORTA_BASE, GPIO_PIN_6 GPIO_PIN_7); GPIOPinTypeI2CSCL(GPIO_PORTA_BASE, GPIO_PIN_6); // 初始化 I2C 主机模块 设置总线速度和使能主机模块 I2CMasterInitExpClk(I2C1_BASE, SysCtlClockGet(), false); } // 使能 I2C 主机模块 I2CMasterEnable(I2C1_BASE); 2 获取 ADS1100 通过 I2C 协议传输到 Tiva 的 ADC 数据 /******************************************************************** * 获取 ADS1100 上采集到的 ADC 数据 * 通信协议 : 1 设置读取的 I2C 从机地址 (ADS1100); * 2 获取 16Bit ADC 电压数据中的高 8Bit; * 3 获取 16Bit ADC 电压数据中的低 8Bit; * 4 得到 ADS1100 的配置信息 ********************************************************************/ 12

15 uint32_t I2C_ADC_OpReg_MSB_i; // 保存通过 I2C 读取 ADS1100 的 16 位 AD 的高字节 uint32_t I2C_ADC_OpReg_LSB_i; // 保存通过 I2C 读取 ADS1100 的 16 位 AD 的高字节 uint32_t I2C_ADC_ConfigReg_i; #define DELAY_6MS (SysCtlClockGet() / 3) / void CatchI2C() { // 恒流源测电阻 I2CMasterSlaveAddrSet(I2C1_BASE, SLAVE_ADDRESS, true); //############################################### I2CMasterControl(I2C1_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START); while(i2cmasterbusy(i2c1_base)); I2C_ADC_OpReg_MSB_i = I2CMasterDataGet(I2C1_BASE); //############################################### I2CMasterControl(I2C1_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT); while(i2cmasterbusy(i2c1_base)); I2C_ADC_OpReg_LSB_i = I2CMasterDataGet(I2C1_BASE); //############################################### I2CMasterControl(I2C1_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH); while(i2cmasterbusy(i2c1_base)); I2C_ADC_ConfigReg_i = I2CMasterDataGet(I2C1_BASE); 13

16 } 3 将 ADC 数据转化为实时电压数据 // 将高 8 位数据放置高 8 位上 temp1 = (I2C_ADC_OpReg_MSB_i & 0x000000FF) << 8 ; // 将低 8 位数据放置低 8 位上 temp2 = (I2C_ADC_OpReg_LSB_i & 0x000000FF); // 合并高低位数据为完整的采样数据 temp3 = (int16_t)(temp1 + temp2); 4 得到的 temp3 就是最终 ADS1100 获得的 16 位电压数据采样值 记录下后可用于后面的数据测试和分析 14

17 15

18 16

19 17

20 第 5 章程控增益放大模块 杭州艾研信息技术有限公司 2014 年 11 月 - 1 -

21 申明 杭州艾研信息技术有限公司保留随时对其产品进行修正 改进和完善的权利, 同时也保留在不作任何通告的情况下, 终止其任何一款产品的供应的权利 用户在下订单前应及时获取相关信息的最新版本, 并验证这些信息是当前的和完整的 可通过如下方式获取最新信息 技术资料和技术支持 : 技术支持电话 : 技术支持邮箱 产品 & 资料下载中心 : 互动论坛 : 公司地址 : 浙江省杭州市西湖区留和路 16 号新峰商务楼 B402 更多资讯请添加艾研信息官方微信 ( 搜索公众号 : 艾研 ) 或扫一 扫下方二维码 :

22 第 5 章程控增益放大模块 - 1 -

23 - 2 -

24 3

25 1 程序成功烧写后,TIVA LaunchPad 上电后可启动程序运行 程序主循环 6 2 LCD 初始化包括 LCD 端口使能 SSI 通信协议配置 LCD 配置初始化 LCD 清屏 4 个步骤 每个步骤详情请见本书第三章 程序启动 1 读取滚轮 ADC 显示码值并发送至 DAC ADC 采样初始化有两个部分 : 滚轮电阻采样初始化 (PE0) 和峰值检测模块电压采样初始化 (PD2) ADC 采样初始化步骤 :1 使能 ADC 模块外设 2 配置相关 GPIO 为 ADC 功能 3 ADC 采样序列配置 4 ADC 采样序列步进配置 5 使能采样序列并清除中断标志 LCD 初始化 ADC 采样初始化 SSI 初始化 读取电压 ADC 采样值并进行峰值检测 系统延时 7 8 否 4 SSI 初始化负责完成与 DAC8802 之间通信的所有信号线的配置 包括 PF2 PF3 PF1 PB2 PB3 PC4, 其中 PF2 PF3 PF1 配置成 SSI 通信端口, 其余这使能为端口输出 SSI 初始化步骤 :1 使能 SSI 外设模块 2 配置相关 GPIO 复用功能为 SSI 模块功能并为 SSI 模块通信使用 3 SSI 通信模式 时钟频率设置和数据位设置 4 使能 SSI LCD 显示字符 5 5 结束 是 程序终止 5 在 LCD 上显示 Peak detection voltage:xx V, 等信息, 便于实验过程中观察实验数据变化 6 在程序主循环中, 读取滚轮 ADC 采样值,TIVA 的 AD 是 12-Bit, 而 DAC8802 的 DA 是 14-Bit, 需要通过相应的转换得到 DAC8802 对应的码值并将码值发送至 DAC8802, 同时将码值显示到 LCD 上 读取 ADC 采样值的程序部分详见本书第三章 7 读取电压 (PD2)ADC 采样值, 通过相应的转换公式转换成实际电压值并进行峰值检测, 将检测到的峰值显示在 LCD 上, 便于观察 此处读取 ADC 采样值的程序部分可参考本书第三章 8 为防止 LCD 过快重复刷新, 系统进行延时将主循环控制在一个合理的执行速度内 在程序设计过程中主要涉及到 LCD 显示,ADC 采样以及 SSI 通信, 其中 LCD 显示和 ADC 采样功能设置可参考本书第三章相关内容 Xxx SPI(SSI) 通信 4

26 DAC8802 采用 SPI( 相当于 Tiva M4 的 SSI 协议 ) 通信协议进行数据传输 DAC8802 是 14-bit 的 DAC, 而串行数据锁存在 DAC8802 的串 行输入寄存器 (serial input register) 中, 该寄存器为 16-bit 即两个字节长度 (2-bit 的地址 :A1-A0, 以及 14 的 DA 数据 :D13-D0) 寄存器数据格 式如下 : 表 xx SPI 寄存器数格式 Bit B15 B14 B13 B12 B11 B10 B9 B8 B7 B6 B5 B4 B3 B2 B1 B0(LSB) Data A1 A0 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 DAC8802 内部有两个 DAC, 分别为 DAC A 和 DAC B, 其中 DAC A 为放大功能,DAC B 为衰减功能, 程序控制 DAC8802 的放大和衰减功 能就是选择使能其中一个 DAC 模块 该两个模块的选择通过串行输入寄存器中地址位 :A1 和 A0 进行选择 表 xx 地址位设置 A1 A0 使能 DAC 模块 0 0 None 0 1 DAC A 1 0 DAC B 1 1 DAC A 和 DAC B 程序上的实现可以采用 #define 宏定义, 然后在发送数据时将定义好的地址值加上待发送数据即可 宏定义代码如下 : #define DAC_A 0x4000 #define DAC_B 0x8000 #define DAC_AB 0xC000 //DAC A //DAC B //DAC A 和 DAC B SPI(SSI) 通信配置函数 DAC8802 通过 CS( 低电平有效 ),SDI,SCK 三线控制数据的传输, 其对应 Tiva M4 中的 SSIFss,SSITx,SSIClk 线, 相应配置代码如下 : /******************************************************************** SSI 模块使能, 并且设置相关端口初始状态 5

27 none none * * * PF2(SSI1Clk) -->SPICLK 时钟信号端 * TIVA PF3(SSI1Fss) -->SYNC 帧信号端 * PF1(SSI1Tx) -->SDIN SSI 数据发送端 (LM4F120->DAC8802) * PB2(GPIO) -->LDAC * PB3(GPIO) -->RS * PC4(GPIO) -->MSB * * ********************************************************************/ void ssi_en() { // 使能外设 SSI1 模块 ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI1); // 使能 SSI1 使用的外设 GPIOF ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); //SSI1 端口功能使能 //PF2 复用功能配置为 SSI1CLK, 时钟线 ROM_GPIOPinConfigure(GPIO_PF2_SSI1CLK); //PF3 复用功能配置为 SSI1FSS, 片选线 ROM_GPIOPinConfigure(GPIO_PF3_SSI1FSS); //PF1 复用功能配置为 SSI1TX, 数据发送线 6

28 ROM_GPIOPinConfigure(GPIO_PF1_SSI1TX); //LDAC 置高 ROM_GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_2); ROM_GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_2, GPIO_PIN_2); //RS 置高 ROM_GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_3); ROM_GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_3, GPIO_PIN_3); //MSB 置高 ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC); ROM_GPIOPinTypeGPIOOutput(GPIO_PORTC_BASE, GPIO_PIN_4); ROM_GPIOPinWrite(GPIO_PORTC_BASE, GPIO_PIN_4, GPIO_PIN_ 配置 PF1,PF2,PF3 供外设 SSI1 使用 ROM_GPIOPinTypeSSI(GPIO_PORTF_BASE, GPIO_PIN_1 GPIO_PIN_2 GPIO_PIN_3); // 端口模式 :1M,16 位数据 ROM_SSIConfigSetExpClk(SSI1_BASE, ROM_SysCtlClockGet(), SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, , 16); } // 使能 SSISSIEnable(SSI1_BASE); 程序还涉及到 DAC8802 另外三根线的配置 :LDAC 置高,RS 置高,MSB 置高 其中 LDAC 和 RS 都是低电平有效 LDAC 控制 DAC8802 的输出,RS 和 MSB 信号线连接至 DAC8802 内部的上电复位模块, 复位时若 MSB=0, 则所有寄存器值为 0x0000, 若 MSB=1, 则所有寄存器值为 0x2000 DAC8802 的时序图如下 : 7

29 图 xx 时序图 根据时序图可以完成 DAC8802 跟 Tiva M4 之间的数据传输 在 SPI(SSI) 配置程序中, 时序图中的 SDI,CLK 以及 CS 线都配置成了 Tiva M4 中的 SSI 功能, 在数据传输过程中这三根信号线上的电平变化都由 Tiva M4 的 SSI 模块自行控制 而 LDAC 线配置成普通的 GPIO 功能, 则改线上的 电平变化需要自行控制 SPI(SSI) 传输程序代码如下 : /************************************************************** 向 dac8802 发送数据 unsigned long val, 取值范围 0~ , 参数不正确 ; * 1, 传输成功 ; **************************************************************/ unsigned char ssi_send_2_dac8802(unsigned long val) {val > 16384) return 0; ROM_SSIDataPut(SSI1_BASE, DAC_AB + val); // 发数据 + while(rom_ssibusy(ssi1_base)); // 等待发送完成 // 数据发送结束时,LDAC 线需要一个电平的跳变 (H->L->H) ROM_GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_2, 0); delay(); ROM_GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_2, GPIO_PIN_2); 8

30 delay(); return 1; } 程序传输中使用 ROM_SSIDataPut 就可完成数据发送, 发送的数据格式地址 + 数据即程序中的 DAC_AB+val, 表示的是使能 DAC A 和 DAC B 同时使用两个 DAC 模块,val 即为需要发送的数据 此时同时完成放大和衰减, 如果只要单独使用放大或者衰减, 则发送数据时自需要变成 DAC_A+val 或 DAC_B+val 在数据发送完成后需要完成一个 LDAC 信号线的电平转变, 完成 DAC8802 模块的输出 9

31 10

32 11

33 12

34 13

35 14

36 第 9 章宽带压控增益模块 杭州艾研信息技术有限公司 2014 年 11 月 - 1 -

37 申明 杭州艾研信息技术有限公司保留随时对其产品进行修正 改进和完善的权利, 同时也保留在不作任何通告的情况下, 终止其任何一款产品的供应的权利 用户在下订单前应及时获取相关信息的最新版本, 并验证这些信息是当前的和完整的 可通过如下方式获取最新信息 技术资料和技术支持 : 技术支持电话 : 技术支持邮箱 产品 & 资料下载中心 : 互动论坛 : 公司地址 : 浙江省杭州市西湖区留和路 16 号新峰商务楼 B402 更多资讯请添加艾研信息官方微信 ( 搜索公众号 : 艾研 ) 或扫一 扫下方二维码 :

38 2

39 实验程序使用按键 S3 选择当前程序工作在实验 B, 实验 C 还是实验 D, 这三个实验程序上唯一的区别是 : 在程序刚运行时三个实验的 DAC7311 的初始值不同, 其实部分都是一致的 ; 使用按键 S1 和 S2 完成 VC 端电压值的调节, 同时改变 DAC7311 的工作值, 在液晶上同步显示当前 VC 端电压值 ; 通过 SSI 传输协议改变 DAC7311 的工作值 ; 程序成功烧写后,TIVA LaunchPad 上电后可启动程序运行 LCD 初始化包括 LCD 端口使能 SSI 通信协议配置 LCD 配置初始化 LCD 清屏 4 个步骤 每个步骤详情请见本书第三章 按键初始化, 配置 S1(PC7),S2(PD6),S3(PD7) 输入模式 注意 :PD7 口默认锁定功能为 NMI, 使用 GPIO 功能时需要解锁定 SSI 初始化, 完成与 DAC7311 之间通信的所有信号线的配置,PF2 配置成时钟信号线,PF3 配置成帧信号端,PF1 配置成数据发送线 SSI 初始化步骤 :1 使能 SSI 外设模块 2 配置相关 GPIO 复用功能为 SSI 模块功能并为 SSI 模块通信使用 3 SSI 通信模式 时钟频率设置和数据位设置 4 使能 SSI 在 LCD 上初始显示 ssi:xxxx, 表示 DAC7311 当前码值,vc:xxxxmV, 表示电路 vc 端的当前电压值, 便于实验过程中观察实验数据变化 6 按键扫描, 按下 S1 对 DAC7311 当前码值增加一个步进值, 按下 S2 对 DAC7311 当前码值减少一个步进值, 按下 S3 进行三种工作模式 (LAB1,LAB2,LAB3) 之间的切换, 并显示在 LCD 上 程序启动 LCD 初始化 按键初始化 SSI 通信初始化 LCD 显示字符 程序主循环 按键扫描获取按键值进行工作模式选择或者码值增减 发送码值至 DAC7311, 计算输出电压 系统延时 结束 是 程序终止 7 通过 SSI 发送码值至 DAC7311, 并通过相应的电压转换公式计算电路 vc 端的电压值, 显示在 LCD 上, 电压值范围 (-3.3V 3.3V) 8 为防止 LCD 过快重复刷新, 系统进行延时将主循环控制在一个合理的执行速度内 否 图 xx 程序流程图 LCD 显示部分程序可参考本书第三章 按键功能实验程序需要使用 LCD 开发板上的所有按键 :S1 S2 S3 程序使用按键扫描完成端口状态的读取, 在按键的初始化配置中需要注意的是 S3 的配置, 因为 S3 连接在端口 PD7, 而 PD7 口已经被锁定为 NMI(non-maskable interrupt, 不可屏蔽中断 ) 功能, 所以在使用该端口时需要先解除锁定, 使其能够配置成 GPIO 功能 解除锁定代码如下 : // 解锁 HWREG(GPIO_PORTD_BASE+GPIO_O_LOCK) = GPIO_LOCK_KEY; HWREG(GPIO_PORTD_BASE+GPIO_O_CR) = (1<<7); HWREG(GPIO_PORTD_BASE+GPIO_O_DEN) &=(~(1<<7)); HWREG(GPIO_PORTD_BASE+GPIO_O_PDR) &= (~(1<<7)); 3

40 HWREG(GPIO_PORTD_BASE+GPIO_O_PUR) &= (~(1<<7)); HWREG(GPIO_PORTD_BASE+GPIO_O_AFSEL) &=(~(1<<7)); 完成解锁后 PD7 口就可以跟 PC7,PD6 一样配置初始化使用 按键使用端口的初始化程序代码如下 : 对端口 C D 进行按键初始化 none none * * * PC7 <--Button1 * TIVA PD6 <--Button2 * PD7 <--Button3 * * 注 :PD7 口默认锁定功能为 NMI, 使用其 GPIO 功能时需要解锁定再配置成 GPIO 功能 ***********************************************************/ void Init_Key() { // // 解锁 HWREG(GPIO_PORTD_BASE+GPIO_O_LOCK) = GPIO_LOCK_KEY; HWREG(GPIO_PORTD_BASE+GPIO_O_CR) = (1<<7); HWREG(GPIO_PORTD_BASE+GPIO_O_DEN) &=(~(1<<7)); HWREG(GPIO_PORTD_BASE+GPIO_O_PDR) &= (~(1<<7)); HWREG(GPIO_PORTD_BASE+GPIO_O_PUR) &= (~(1<<7)); HWREG(GPIO_PORTD_BASE+GPIO_O_AFSEL) &=(~(1<<7)); // // 初始化外设 GPIO ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC); ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD); // 设置 PD 为 2MA, 若上拉输出 ROM_GPIOPadConfigSet(GPIO_PORTC_BASE, GPIO_PIN_7, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU); ROM_GPIOPadConfigSet(GPIO_PORTD_BASE, GPIO_PIN_6, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU); 4

41 } ROM_GPIOPadConfigSet(GPIO_PORTD_BASE, GPIO_PIN_7, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU); // 设置 GPIO 输入模式 ROM_GPIODirModeSet(GPIO_PORTC_BASE, GPIO_PIN_7, GPIO_DIR_MODE_IN); ROM_GPIODirModeSet(GPIO_PORTD_BASE, GPIO_PIN_6, GPIO_DIR_MODE_IN); ROM_GPIODirModeSet(GPIO_PORTD_BASE, GPIO_PIN_7, GPIO_DIR_MODE_IN); 按键扫描程序直接使用 ROM_GPIOPinRead() 进行读取按键所在端口的状态值, 三个 按键对应不同的功能 :S3 选择实验项目,S1 增大 DAC7311 工作值使 VC 端电压增大,S2 减小 DAC7311 工作值使 VC 端电压减小 程序扫描三个端口即 PC7:S1 按键端口 ;PD6: S2 按键端口 ;PD7:S3 按键端口 按键扫描程序代码如下 : /****************************************************************** ** 按键扫描函数 none 0x00 没有键按下 * 0x01 按下 PC7,S1 * 0x02 按下 PD6,S2 * 0x03 按下 PD7,S3 * * * PC7 <--Button1 * TIVA PD6 <--Button2 * PD7 <--Button3 * * ******************************************************************* */ unsigned char scan_key(void) { if (ROM_GPIOPinRead(GPIO_PORTC_BASE, GPIO_PIN_7) == 0x00) 5

42 { } // 延时约 10ms, 消除按键抖动 ROM_SysCtlDelay(10*(ROM_SysCtlClockGet() / 3000)); KEY 抬起 while (ROM_GPIOPinRead(GPIO_PORTC_BASE, GPIO_PIN_7) == 0x00); // 延时约 10ms, 消除松键抖动 ROM_SysCtlDelay(10*(ROM_SysCtlClockGet() / 3000)); return 0x01; }ROM_GPIOPinRead(GPIO_PORTD_BASE, GPIO_PIN_6) == 0x00) { 约 10ms, 消除按键抖动 ROM_SysCtlDelay(10*(ROM_SysCtlClockGet() / 3000)); // 等待 KEY 抬起 while (ROM_GPIOPinRead(GPIO_PORTD_BASE, GPIO_PIN_6) == 0x00); // 延时约 10ms, 消除松键抖动 ROM_SysCtlDelay(10*(ROM_SysCtlClockGet() / 3000)); return 0x02; } if (ROM_GPIOPinRead(GPIO_PORTD_BASE, GPIO_PIN_7) == 0x00) { // 延时约 10ms, 消除按键抖动 ROM_SysCtlDelay(10*(ROM_SysCtlClockGet() / 3000)); // 等待 KEY 抬起 while (ROM_GPIOPinRead(GPIO_PORTD_BASE, GPIO_PIN_7) == 0x00); // 延时约 10ms, 消除松键抖动 ROM_SysCtlDelay(10*(ROM_SysCtlClockGet() / 3000)); return 0x03; } return 0; 按键扫描函数的返回值表示实验中按下了不同按键, 根据该值就可以完成工作模式转 换或者是 VC 端电压的增减 按键响应程序代码如下 : key_val = scan_key(); if(key_val) { switch(key_val) { // 按下 S1( 按键 1), 增加 // 键扫描 6

43 } } case 0x01: //... break; // 按下 S2( 按键 2), 减小 case 0x02: //... break; // 按下 S3( 按键 3), 工作模式的切换 ( 实验之间的切换 ) // 切换工作模式主要改变的是 dac7311 工作的默认码值 (VC 端 ) // 开始工作的电压 case 0x03: switch(key3_presscount) { // 工作模式 1: 带宽压控增益放大与衰减 case 1: //... break; // 工作模式 2: 正反馈 RC 震荡器 case 2: //... break; // 工作模式 3: 自稳幅闭环振荡器 case 3: //... break; default: break; } Key3_PressCount++; if(key3_presscount > 3) { Key3_PressCount = 1; } default: break; } 7

44 SSI 通信功能 DAC7311 与 Tiva M4 之间通过 SSI(SPI) 通信完成 DAC7311 是 12-bit 的 DAC, 其 内部有一个 16-bit 的寄存器 寄存器格式如下 : Bit B15 B14 B13 B12 B11 B10 B9 B8 B7 B6 B5 B4 B3 B2 B1 B0 Data PD1 PD2 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 X X 寄存器低两位为无效位,B2~B13 为数据位, 高两位 B14(PD2),B15(PD1) 为控制选择 位表示 DAC7311 不同的工作模式 : 一种正常工作模式, 三种 power-down 工作模式 在实 验程序只使用正常工作模式, 这两位都配置为 0 DAC7311 由 SYNC,SCLK,DIN 三线控制 SYNC 为信号选择线, 相当于 Tiva M4 的 SSIFss 信号线 SCLK 为时钟信号线, 相当于 Tiva M4 的 SSIClk 信号线 DIN 为数据 线, 相当于 Tiva M4 的 SSITx 信号线 SSI(SPI) 通信程序设置如下 : /****************************************************************** ** SSI 初始化设置 none none * * * * PF2(SSI1Clk) -->SPICLK 时钟信号端 * TIVA PF3(SSI1Fss) -->SYNC 帧信号端 * PF1(SSI1Tx) -->SDIN SSI 数据发送端 (LM4F120->DAC7811) * * ******************************************************************* */ void ssi_en() { // 使能 SSI1 外设 ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI1); // 使能 SSI1 使用的 GPIOF 外设 ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); // 配置 PF2 复用功能为 SSI1CLK, 时钟线 8

45 ROM_GPIOPinConfigure(GPIO_PF2_SSI1CLK); // 配置 PF3 复用功能为 SSI1FSS, 片选线 ROM_GPIOPinConfigure(GPIO_PF3_SSI1FSS); // 配置 PF1 复用功能为 SSI1TX, 数据发送线 ROM_GPIOPinConfigure(GPIO_PF1_SSI1TX); } // 配置 PF1,PF2,PF3 供 SSI1 使用 ROM_GPIOPinTypeSSI(GPIO_PORTF_BASE, GPIO_PIN_1 GPIO_PIN_2 GPIO_PIN_3); // 端口模式 :1M,16 位数据 ROM_SSIConfigSetExpClk(SSI1_BASE, ROM_SysCtlClockGet(), SSI_FRF_TI, SSI_MODE_MASTER, , 16); ROM_SSIEnable(SSI1_BASE); DAC7311 寄存器的低两位无效, 所以在 SSI 通信程序实现中需要将发送的数据左移两位, 然后再通过 SSI 进行传输 程序代码实现如下 : /****************************************************************** ******** 发送数据到 dac7311 val 取值范围 0~ 参数不正确 ; * 1 传输成功 ; *************************************************************/ unsigned char ssi_send_2_dac7311(unsigned long val) { if(val > 4095) return 0; // 判断参数正确与否 val = val << 2; // 左移两位,DAC7311 内部寄存器低两位无效 ROM_SSIDataPut(SSI1_BASE, val); // 发送数据 while(rom_ssibusy(ssi1_base)); // 等待发送完成 return 1; } 实验程序需要在 LCD 上显示当前 VC 端的电压值,VC 端的电压可以通过 DAC7311 的输出电压换算得到 DAC7311 的输出电压计算公式 : 9

46 式中 out V V D 2 out DD n V 是 DAC7311 的输出电压, V DD 是 DAC7311 的输入电压, D 是 Tiva M4 通过 SSI 传输给 DAC7311 的工作值, D 0, 4095, n 是 DAC7311 的 DA 位数, 该值为 12 电路中 V 3.3V, 故 DAC7311 的输出电压为 0 ~ 3.3V, 该电压通过 LMP7701 构 DD 成的双极性输出电路将电压值转换为 3.3 V ~ 3.3V, 即为 VC 端的电压 在程序设计中不涉及负值的处理, 故可以将 3.3 V ~ 3.3V 在程序中提升为 0 ~ 6.6V 程序中可以通过如 下方式计算出 VC 端的电压 故 VC 端电压为 : D V 2 VDD V 0,6.6V n V 0 V 3.3 VC V V 6.6 程序中扩大 1000 倍计算, 显示成 mv 档 程序代码如下 : // 计算 VC 端的电压值 VC_Value = (dac7311_val*3300*2) / 4096; // 根据 3300( 中间值, 进行正负显示处理 ) int show_val; if(vc_value > 3300) { } else { } show_val = VC_Value ; show_val = VC_Value; 程序代码中的 VC_Value 即为计算公式中的 V,show_val 计算得出的都是正值, 而根据程序数据跟 VC 端真实电压的对应关系可知当 VC_Value<3300( V 3.3 ) 时 VC 端电压为负值,LCD 上显示负值 ;VC_Value>3300( V 3.3) 时 VC 端电压为正值 LCD 上显 示正值 程序代码如下 : for(i = 0; i < 4; ++i) { if(vc_value < 3300) 10

47 { } // 在 LCD 上显示负号 LCD_Draw_Char('-', LINE_TWO, 50); } else { // 在 LCD 上清除负号 LCD_Draw_Char(' ', LINE_TWO, 50); } // 在 LCD 上显示 VC 端电压值 LCD_Draw_Char('0' + data1[i], LINE_TWO, * i); 不同的实验 VC 端以及 DAC7311 具有各自的初始默认值,VC 端的电压是通过 DAC7311 工作值计算获得 默认值在按键响应程序中设置 A 宽带压控增益放大和衰减该实验初始默认值 VC 端电压需要为负压, 保持在 -1.13V 根据计算公式换算, 在程序实现中保持 DAC7311 的工作值为 1350, 计算得出的 VC 端的电压为 V B 正反馈 RC 振荡器该实验初始默认值为 0 即可, 此时 DAC7311 的工作值为 2048 C 自稳幅闭环振荡器该实验初始默认值 VC 端电压需要为正压, 保持在 1.17V 根据计算公式换算, 在程序实现中保持 DAC7311 的工作值为 2775, 计算得出 VC 端电压为 1.171V 11

48 12

49 13

50 14

51 15

52 16

53 17

54 18

55 第 10 章频率与相位跟踪模块 杭州艾研信息技术有限公司 2014 年 11 月 - 1 -

56 申明 杭州艾研信息技术有限公司保留随时对其产品进行修正 改进和完善的权利, 同时也保留在不作任何通告的情况下, 终止其任何一款产品的供应的权利 用户在下订单前应及时获取相关信息的最新版本, 并验证这些信息是当前的和完整的 可通过如下方式获取最新信息 技术资料和技术支持 : 技术支持电话 : 技术支持邮箱 产品 & 资料下载中心 : 互动论坛 : 公司地址 : 浙江省杭州市西湖区留和路 16 号新峰商务楼 B402 更多资讯请添加艾研信息官方微信 ( 搜索公众号 : 艾研 ) 或扫一 扫下方二维码 :

57 2

58 3

59 4

60 5

61 6

62 软件流程图 程序启动 程序主循环 初始化正弦表液晶初始化 1 计算频率计算相位差 主程序中断响应中断点定时器 Timer0A 中断中断返回中断响应 定时器 Timer0 初始化定时器 Timer1 初始化定时器 Timer2 初始化 5 PWM 初始化 显示输入信号的跟踪频率值 系统延时 结束 是 程序终止 否 中断点定时器 Timer1A 中断中断返回中断响应中断点定时器 Timer1B 中断中断返回中断响应中断点定时器 Timer2A 中断中断返回主程序 图 x 频率相位模块流程图 图 x 频率相位模块中断响应 1 创建 2K 大小数组存放标准的正弦表, 用于后面生成一个 SPWM 波形时校对其占空比值 正弦表的最大值即为 SPWM 波形的最大周期 程序创建一个信号跟踪采样频率为 10KHz 的定时器中断 每次响应中断会根据最新的频率和相位跟踪数据测算出 SPWM 的占空比值, 并调节 PWM 输出 5 Timer1 的 TimerA 开启捕获模式 TimerB 启动周期模式 两者结合配置共同承担检测计算信号频率的任务 Timer2A 的 TimerA 开启捕获模式来计算信号相位偏移 PWM 初始负责生成一个 10KHz 的 PWM 载波信号 通过响应 Timer0A 中断后调节该 PWM 波形的占空比来形成一个 SPWM 波 后续电路通过运放等电路合成跟踪信号 软件流程图如上图所示, 频率相位的程序相对而言较为复杂 1 生成一个由 2048 点脉宽信息组成的正弦表, 脉宽信息与正弦波幅值成一次函数关系 该表用于调整输出 PWM 信号的占空比值 2 定时器 Timer0 产生一个 10K 频率的定时周期响应, 每次响应都会调整 PWM 的输出占空比值 3 定时器 Timer1 中的 Timer1A 和 Timer1B 同时启动, 用于测量输入频率 其中 Timer1A 采用捕获模式响应输入信号的硬件中断 Timer2B 设定为溢出计时的工作模式 根据不同的输入频率计算输入信号的周期后换算得到信号频率 ; 如下图所示, 输入信号上升沿触发可以得到 T1,T2 两个中断点的定时器值 根据下图的构思可以得到输入信号的周期 (Tp): Tp = T2 T1 + To * M 7

63 Tp: 输入信号的周期 输入信号 : 时间轴 : To:Timer1B 计时溢出时间 t T1:Timer1A 捕获中断点时间 M: 统计两次中断中间的 Timer1B 溢出次数 信号频率测算示意图 T2:Timer1A 再次捕获中断点时间 最后根据系统频率信息就能够换算得到输入信号的精确频率信息 关键代码分析 1 初始化 Timer0 定时器, 生成 SPWM /************************************************************ 初始化 Timer0 定时器中断, 以 10K 的频率响应定时中断, * 通过调节 PWM 信号的占空比输出一组 SPWM 信号 null null ************************************************************/ void Init_Timer0_SPWM() { // Enable the peripherals used by this example. ROM_SysCtlPeripheralEnable (SYSCTL_PERIPH_TIMER0); // Enable processor interrupts. ROM_IntMasterEnable (); // Configure the two 32-bit periodic timers. ROM_TimerConfigure (TIMER0_BASE, TIMER_CFG_PERIODIC); 8

64 // 8K Hz 响应 ROM_TimerLoadSet (TIMER0_BASE, TIMER_A, ROM_SysCtlClockGet() / SAMPLE_FREQUENCY); // Setup the interrupts for the timer timeouts. ROM_IntEnable (INT_TIMER0A); ROM_TimerIntEnable (TIMER0_BASE, TIMER_TIMA_TIMEOUT); // Enable the timers. ROM_TimerEnable (TIMER0_BASE, TIMER_A); } 2 初始化 Timer1 定时器, 测算输入信号频率 /************************************************************ 初始化 Timer1 定时器中的 Timer1A 和 Timer1B 中断, 用于测算输入信号频率 * Timer1A 工作在捕获模式 ; * Timer1B 工作在计数溢出模式 ; null null ************************************************************/ void Init_Timer1_Frequency() { // 启用 Timer1 模块 ROM_SysCtlPeripheralEnable (SYSCTL_PERIPH_TIMER1); // 启用 GPIO_M 作为脉冲捕捉脚 ROM_SysCtlPeripheralEnable (SYSCTL_PERIPH_GPIOB); // 配置 GPIO 脚为使用 Timer4 捕捉模式 ROM_GPIOPinConfigure (GPIO_PB4_T1CCP0); ROM_GPIOPinTypeTimer (GPIO_PORTB_BASE, GPIO_PIN_4); // 为管脚配置弱上拉模式 ROM_GPIOPadConfigSet (GPIO_PORTB_BASE, GPIO_PIN_4, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU); 9

65 // 配置使用 Timer4 的 TimerA 模块为边沿触发减计数模式 ROM_TimerConfigure (TIMER1_BASE, TIMER_CFG_SPLIT_PAIR TIMER_CFG_A_CAP_TIME TIMER_CFG_B_PERIODIC); // 使用下降沿触发 ROM_TimerControlEvent (TIMER1_BASE, TIMER_BOTH, TIMER_EVENT_NEG_EDGE); // 设置计数范围为 0x8FFF~0X8FFA ROM_TimerLoadSet (TIMER1_BASE, TIMER_A, 0xFFFF); ROM_TimerLoadSet (TIMER1_BASE, TIMER_B, 0xFFFF); // 注册中断处理函数以响应触发事件 TimerIntRegister(TIMER1_BASE, TIMER_A, Int_Timer1A_Handler); TimerIntRegister(TIMER1_BASE, TIMER_B, Int_Timer1B_Handler); // 系统总中断开 ROM_IntMasterEnable (); // 时钟中断允许, 中断事件为 Capture 模式中边沿触发, 计数到达预设值 ROM_TimerIntEnable (TIMER1_BASE, TIMER_CAPA_EVENT TIMER_TIMB_TIMEOUT); // NVIC 中允许定时器 A 模块中断 ROM_IntEnable (INT_TIMER1A INT_TIMER1B); } // 启动捕捉模块 ROM_TimerEnable (TIMER1_BASE, TIMER_BOTH); 3 初始化 Timer2 定时器, 计算输入输出信号相位差 : /************************************************************ 初始化 Timer2 定时器中的 Timer2A 中断, 用于测算输入信号和输出信号的时间差 * Timer2A 工作在捕获模式 ; null null ************************************************************/ 10

66 // 初始化相位跟踪 void Init_Timer2_Phase() { // 启用 Timer4 模块 ROM_SysCtlPeripheralEnable (SYSCTL_PERIPH_TIMER2); // 启用 GPIO_M 作为脉冲捕捉脚 ROM_SysCtlPeripheralEnable (SYSCTL_PERIPH_GPIOF); // 配置 GPIO 脚为使用 Timer4 捕捉模式 ROM_GPIOPinConfigure (GPIO_PF4_T2CCP0); ROM_GPIOPinTypeTimer (GPIO_PORTF_BASE, GPIO_PIN_4); // 为管脚配置弱上拉模式 ROM_GPIOPadConfigSet (GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU); // 配置使用 Timer4 的 TimerA 模块为沿触发加计时模式 ROM_TimerConfigure (TIMER2_BASE, TIMER_CFG_SPLIT_PAIR TIMER_CFG_A_CAP_TIME); // 使用下降沿触发 ROM_TimerControlEvent (TIMER2_BASE, TIMER_A, TIMER_EVENT_NEG_EDGE); // 设置计数范围为 0~0x8FFF ROM_TimerLoadSet (TIMER2_BASE, TIMER_A, 0xFFFF); // 注册中断处理函数以响应触发事件 TimerIntRegister(TIMER2_BASE, TIMER_A, Int_Timer2A_Handler); // 系统总中断开 ROM_IntMasterEnable (); // 时钟中断允许, 中断事件为 Capture 模式中边沿触发 ROM_TimerIntEnable (TIMER2_BASE, TIMER_CAPA_EVENT); // NVIC 中允许定时器 A 模块中断 ROM_IntEnable (INT_TIMER2A); 11

67 } // 启动捕捉模块 ROM_TimerEnable (TIMER2_BASE, TIMER_A); 4 频率计算方法 : // 统计频率信号 void Int_Timer1A_Handler(void) { // 最后一次 Timer 的计数值 uint32_t CapTimer_Fre = 0; // 清除中断标志位 ROM_TimerIntClear (TIMER1_BASE, ROM_TimerIntStatus (TIMER1_BASE, false)); ROM_TimerEnable (TIMER1_BASE, TIMER_A); Sample_Index = 0; // 获取当前 Timer1 的计数值 CapTimer_Fre = TimerValueGet(TIMER1_BASE, TIMER_B); // 当两次中断之间的时间差超过了一个 Timer1B 的计数周期时, 需要补加一个 Timer1B 的计数值再相减 if (Fre_TimerOutCount >= 1) fre_temp_tick = CapTimer_Fre_Ori + ((Fre_TimerOutCount - 1) << 16 )//* TIMER_TOTAL_COUNTNUM) + (TIMER_TOTAL_COUNTNUM - CapTimer_Fre); else // 若两次中断的响应在一个周期内, 则直接用前一次的计数器值减去后一次的计数器值 fre_temp_tick = CapTimer_Fre_Ori - CapTimer_Fre; // 统计多个时间差值, 统计取平均值 fre[fre_frequece_index++] = (TIVA_MAIN_FREQUENCY / fre_temp_tick);//frequency_detect(fre_temp_tick, Fre_TimerOutCount); // 将计算所得频率放入频率数组中 if (Fre_Frequece_Index >= FREQUENCY_AVARAGE_NUM) 12

68 { // 取平均处理 Fre_Cur_Frequency = average_float(fre, FREQUENCY_AVARAGE_NUM, FREQUENCY_CUT_NUM); // 计算不同频率对应步进值 Phase_step_N = Fre_Cur_Frequency * SIN_TABLE_N / SAMPLE_FREQUENCY; // 统计值存放队列清空 Fre_Frequece_Index = 0; } // 中断处理结束, 将当前计数值转化为上次中断计数值用于下次计算差值 CapTimer_Fre_Ori = CapTimer_Fre; } Fre_TimerOutCount = 0; 5 在 Timer1B 的中断响应中, 统计出输入信号两次上升沿捕获中断之间 Timer1B 计数器的计数溢出次数 : // 统计 Timer1B 计数溢出的次数 void Int_Timer1B_Handler(void) { // 清除中断标志位 ROM_TimerIntClear (TIMER1_BASE, ROM_TimerIntStatus (TIMER1_BASE, true)); ROM_TimerEnable (TIMER1_BASE, TIMER_B); // 统计 Timer1B 计数溢出的次数 Fre_TimerOutCount++; } 13

69 14

70 1 理解原理图以后编写 Launchpad 代码, 代码可参考网上资源 然后烧写代码 2 在母板上 TIVA 液晶 MDAC 模块连接完成, 准备实验 3 跳帽的连接 : 如图和图 在频率相位模块上用跳帽将 J2 的 3 2 和 J3 的 1 2 连接 4 仪器连接: 示波器两个表笔分别连接到图所示的测试点 和图所示的测试点 注意示波器不要忘了图的接地 5 信号输入: 将信号发生器的表笔连接到图 J1 的 IN+, 输入频率 100Hz 的正弦波, 信号发生器同样要注意接地如图 6 打开 TIVA 开关, 可以观察到 LED 点亮, 在液晶上能看到信号发生器输入的正弦波的频率 同时在示波器上能看到两路信号 一路是 TIVA 经滤波迟滞比较以后的方波 另一路是信号发生器, 经过迟滞比较以后的正弦波 观察两路信号比较异同, 分析其产生的原理 频率相位跟踪实验 15

71 软件流程图及关键代码分析 本实验和 SPWM 波的生成与正弦波发生实验一致, 所以软件流程也是相同的 定时器 Timer2 采用和 Timer1A 同样的捕获模式设置采样输出信号的中断, 用于测量输入输出信号相位差 输入信号的中断还是在 Timer1A 中响应, 输出信号的中断在 Timer2A 上升沿触发时可以获取 Timer1A 定时器时间 通过以上一步同样的方式可以得到两个信号的相位差值 如下图所示 : 输入信号 : Td: 两组信号捕获中断的时间差 输出信号 : 时间轴 : t T1:Timer1A 捕获中断点时间 T2: 在 Timer2A 中段再次获取 Timer1A 的时间 相位跟踪示意图 16

72 关键代码分析 1 在 Timer2 中计算输入输出两路信号的相位差值 : // 统计相位跟踪差值 void Int_Timer2A_Handler(void) { // 清除中断标志位 ROM_TimerIntClear (TIMER2_BASE, ROM_TimerIntStatus (TIMER2_BASE, false)); // 因为减计数会自动停止, 所以需要重新启用计数模块 ROM_TimerEnable (TIMER2_BASE, TIMER_A); // 获取中断响应时的 Timer1A 计数值 uint32_t CapTimer1A = ROM_TimerValueGet (TIMER1_BASE, TIMER_B); // 计算两路信号计数差值 if (Fre_TimerOutCount >= 1) Phase_tick = CapTimer_Fre_Ori + ((Fre_TimerOutCount - 1) << 16) // * 0xFFFF + (TIMER_TOTAL_COUNTNUM - CapTimer1A); else Phase_tick = CapTimer_Fre_Ori - CapTimer1A; // 通过 PID 算法计算相位差, 将相位差放入相位差数组中 pha[pha_phase_index++] = Delay_time_calculate(Phase_tick, Fre_Cur_Frequency); // 取相位差平均值 if (Pha_Phase_Index >= PHASE_AVARAGE_NUM) { Delay_phase = average_float(pha, PHASE_AVARAGE_NUM, PHASE_CUT_NUM);// average_phase(); Pha_Phase_Index = 0; } 17

73 } 2 相位差的计算: 相位差在计算过程中会使用到 PID 算法调节 PID 算法, 按偏差的比例 (P) 积分 (I) 和微分 (D) 进行控制的 PID 控制器 ( 亦称 PID 调节器 ) 是应用最为广泛的一种自动控制器 它具有原理简单, 易于实现, 适用面广, 控制参数相互独立, 参数的选定比较简单等优点 ; 而且在理论上可以证明, 对于过程控制的典型对象 一阶滞后 + 纯滞后 与 二阶滞后 + 纯滞后 的控制对象,PID 控制器是一种最优控制 PID 调节规律是连续系统动态品质校正的一种有效方法, 它的参数整定方式简便, 结构改变灵活 (PI PD ) 在两路信号的相位跟踪同步过程控制中通过 PID 算法调节输出信号的相位值不断的趋近与输入信号的相位达到跟踪的效果 /****************************************************** ************** * 名称 : Delay_time_cal(void) * 功能 : 计算相位差 * 入口参数 : 无 * 出口参数 :temp_delay_phase ******************************************************* ************/ long int Delay_DETA = 0; float kp = 0.5, ki = 0.05, kd = 0.01, ui0 = 0.0, ui = 0, u = 0; float e = 0, e1 = 0; float Delay_time_calculate(int Phase_tick, float frequency) // 计算相位差 { volatile float Cal_delay_time = 0.0; volatile float temp_delay_phase = 0.0; Cal_delay_time = IQ_div_f_i(Phase_tick, TIVA_MAIN_FREQUENCY) ; 18

74 if (Cal_delay_time > 0.5 / frequency) Cal_delay_time -= 1.0 / frequency; if (Cal_delay_time < -0.5 / frequency) Cal_delay_time += 1.0 / frequency; temp_delay_phase = Cal_delay_time * frequency * SIN_TABLE_N; // 位置式 PID 调节器 e = temp_delay_phase; ui = ui0 + ki * e; u = kp * e + kd * (e - e1) + ui; ui0 = ui; e1 = e; temp_delay_phase = u; } return temp_delay_phase; 19