This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] TMS320F28335:查询有关 F28335的 IQmath.h

Guru**** 657930 points
Other Parts Discussed in Thread: C2000WARE, CONTROLSUITE, TMDSPREX28335
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/1201003/tms320f28335-query-about-iqmath-h-for-f28335

器件型号:TMS320F28335
主题中讨论的其他器件:C2000WAREcontrolSUITETMDSPREX28335

大家好、

我们的客户有几个关于 C2000的 IQMath 库的问题:

1.以下行的含义是什么: (_IQ) AdcRegs.ADCRESULT0
(_IQ)是否用于指定 Adcresult0寄存器的内容并将其转换为全局 IQ 格式?

2. 在其中一个示例代码中,ADC 数据的读取方式如下: _IQ16toIQ ((_IQ) AdcRegs.ADCRESULT0);
ADCResult0正从 IQ16转换为全局 IQ 格式。 在浮点样本代码中、ADC 结果右移4个位置、或从已包含右移4个位置数据的 ADCMirror 寄存器中选择结果。 为什么 ADCResult 寄存器内容未在上述行中右移4次? 您能解释一下以上行的工作吗?
但可以使用以下两个语法中的任何一个:
_IQ12toIQ ((_IQ) AdcMirror.ADCRESULT0);
_IQ12toIQ ((_IQ) AdcRegs.ADCRESULT0>>4);
三个语法是否彼此不同? 哪一种方法更好?

3.在同一示例代码中,也有以下两行:
SUB =_IQ (1.5);
 _IQmpy (AdcFsVoltage、_IQ16toIQ (_IQ) AdcRegs.ADCRESULT0)-(sub);
根据这两个指令、将从 ADCRESULT0的内容中减去1.5以获得双极输出(例如:交流电压或交流电流)。 我在这个假设中是否正确?
在浮点模式下、我使用了 Y = Mx + C 方法(并计算了 m 和 c 的值)将信号的实际值与 ADCRESULT0寄存器的结果相关联。 在本例中、是否仍需要使用 OFFSET =_IQ (1.5)? 或者、我可以使用我自己从校准过程中获得的偏移值吗?

4. 我正在做一个逆变器. 在执行整个控制并获得调制指数值后、我是否应该将调制指数从 IQ 格式转换为浮点、然后分配给6个 EPWM 寄存器中的任何一个的 CMPA 或 CMPB?

5、 我如何执行 IQ 格式的"if 语句"?

6. 我是在我的代码中定义 global-Q 值还是应该在 IQmath.h 头文件中更改它?

7. 在以下屏幕截图中的"指定浮点支持"选项中、我是否需要 FPU32库支持或 softlib? 或者我应该保留 FPU32库支持并在代码中包含 IQmath.h?



很抱歉列出了很多问题、但希望您能帮助我们。 提前感谢!


此致、

Jejomar



  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Jejomar:

    非常感谢您以我的名义发布这些问题。

    此致、

    Ankit

    研究学生

    VIT, Vellore, India

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好!

    您能否提供示例代码或告诉我它在 C2000Ware 中的位置、以便我可以了解 这些问题的背景?

    此致、

    Omer Amir

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Omer Amir:

    感谢您的帮助。 请允许我向您提供您所寻求的信息。

    首先

    第1行: (_IQ) AdcRegs.ADCRESULT0

    第2行:_IQ16toIQ ((_IQ) AdcRegs.ADCRESULT0);

    第3行:sub =_iq (1.5);(注意:第3行实际上写为#define sub _iq (1.5);)

    第4行:_IQmpy (AdcFsVoltage、_IQ16toIQ ((_IQ) AdcRegs.ADCRESULT0)-(sub);

    注1:第3行实际上写为#define sub _IQ(1.5);

    注2:行1是行2的子集、行1和行2是行4的子集

    以下链接上名为 Anmol D 的用户所张贴的代码中显示了行1、2、3和4:

    https://e2e.ti.com/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/705771/tms320f28335-issue-regarding-three_phase_srf_pll-parameters-in-tms320f28335

    这个人在2018年发布了他的疑问、他使用了 名为"SPLL_3ph_SRF_IQ.c"的内部 PLL 文件、该文件位于基于 IQ 数学的太阳能库的源文件夹中。 我不知道当时他使用的是哪个版本的 CCS、因此我无法确认。 控制套件中有太阳能库的 v1.0、v1.1和 v1.2版本。 路径如下所示:

    C:\ti\controlSUITE\libs\app_libs\solar\v1.2\IQ\source (对于 v1.2)

    v1.2中提供给"SPLL_3ph_SRF_IQ.c"的代码与上述 URL 上提供的代码不匹配。 用户可能根据其要求在原始代码中额外添加了几行、因此您可能不愿意查看消费者的代码。

    但是、我在 PDF 的第179页找到了类似的声明(这次偏移量为1.65)。 该 PDF 介绍过去可能发生的 Piccolo 技术讲座。 由于 E2E 政策、我不允许上传任何类型的文件(代码除外)。 我请 Jejomar 先生上传上述 PDF 文件第179页的截图。

    而另外一个类似的声明(然而没有任何偏移)被写在模块17的 PDF 版本的第26页上。 这次 PDF 是关于由 Frank Bormann 主持的 TMDSPREX28335研讨会。 我将把博尔曼先生提供的代码附在下面(请参阅第186行),我将请 Jejomar 先生张贴第二张屏幕截图和第二份 PDF 文件。

    //
    //      Lab17: TMS320F28335
    //      (c) Frank Bormann
    //
    //###########################################################################
    //
    // FILE:	Lab17.c
    // 
    // TITLE:	DSP28335ControlCARD; ePWM1A 2KHz output
    //			25% pulse width active high
    //			low - pass filtered by function "IQssfir" (IQ-Math function)
    //			solution file for Lab17
    //###########################################################################
    //  Ver | dd mmm yyyy | Who  | Description of changes
    // =====|=============|======|===============================================
    //  3.0 | 12 Oct 2009 | F.B. | Lab17 for F28335; 
    //  3.1 | 29 Nov 2009 | F.B  | Lab16 for F28335 @30MHz and PE revision 5
    //###########################################################################
    #include "DSP2833x_Device.h"
    #include "IQmathLib.h"
    
    #define AdcBufLen 50
    #define AdcFsVoltage	_IQ(3.0)		// ADC full scale voltage
    
    // external function prototypes
    extern void InitSysCtrl(void);
    extern void InitPieCtrl(void);
    extern void InitPieVectTable(void);
    extern void InitCpuTimers(void);
    extern void InitAdc(void);
    extern void ConfigCpuTimer(struct CPUTIMER_VARS *, float, float);
    extern  _iq IQssfir(_iq*, _iq*, Uint16);  
    
    
    // Prototype statements for functions found within this file.
    void Gpio_select(void);
    void Setup_ePWM1A(void);
    interrupt void cpu_timer0_isr(void);
    interrupt void adc_isr(void);
    void Setup_ADC(void);
    
    // global variables
    _iq AdcBuf[AdcBufLen];					// ADC results buffer
    _iq AdcBufFiltered[AdcBufLen];			// filtered ADC results buffer
    _iq xBuffer[5] = {0,0,0,0,0};			// filter sample buffer
    // filter coefficients
    _iq coeffs[5] = {_IQ(0.0357), _IQ(0.2411), _IQ(0.4465), _IQ(0.2411), _IQ(0.0357)};
    
    //###########################################################################
    //						main code									
    //###########################################################################
    void main(void)
    {
    	int counter=0;	// binary counter for digital output
    
    	InitSysCtrl();	// Basic Core Init from DSP2833x_SysCtrl.c
    
    	EALLOW;
       	SysCtrlRegs.WDCR= 0x00AF;	// Re-enable the watchdog 
       	EDIS;			// 0x00AF  to NOT disable the Watchdog, Prescaler = 64
    
    	DINT;				// Disable all interrupts
    	
    	Gpio_select();		// GPIO9, GPIO11, GPIO34 and GPIO49 as output
    						// to 4 LEDs at Peripheral Explorer	
    
    	Setup_ePWM1A();		// init of ePWM1A
    
    	Setup_ADC();		// initialize ADC channel A2
    
    	InitPieCtrl();		// basic setup of PIE table; from DSP2833x_PieCtrl.c
    	
    	InitPieVectTable();	// default ISR's in PIE
    
    	EALLOW;
    	PieVectTable.TINT0 = &cpu_timer0_isr;
    	PieVectTable.ADCINT = &adc_isr;
    	EDIS;
    
    	InitCpuTimers();	// basic setup CPU Timer0, 1 and 2
    
    	ConfigCpuTimer(&CpuTimer0,150,20);	// 50 kHz sample period
    
    	PieCtrlRegs.PIEIER1.bit.INTx7 = 1;	// CPU -Timer 0
    	PieCtrlRegs.PIEIER1.bit.INTx6 = 1;	// ADC
    
    	IER |=1;
    
    	EINT;
    	ERTM;
    
    	CpuTimer0Regs.TCR.bit.TSS = 0;	// start timer0
    
    	while(1)
    	{    
    	  		while(CpuTimer0.InterruptCount < 5000);
    			CpuTimer0.InterruptCount = 0;
    			
    			EALLOW;
    			SysCtrlRegs.WDKEY = 0x55;	// service WD #1
    			EDIS;
    
    	  		counter++;
    			if(counter&1) GpioDataRegs.GPASET.bit.GPIO9 = 1;
    				else GpioDataRegs.GPACLEAR.bit.GPIO9 = 1;
    			if(counter&2) GpioDataRegs.GPASET.bit.GPIO11 = 1;
    				else GpioDataRegs.GPACLEAR.bit.GPIO11 = 1;
    			if(counter&4) GpioDataRegs.GPBSET.bit.GPIO34 = 1;
    				else GpioDataRegs.GPBCLEAR.bit.GPIO34 = 1;
    			if(counter&8) GpioDataRegs.GPBSET.bit.GPIO49 = 1;
    				else GpioDataRegs.GPBCLEAR.bit.GPIO49 = 1;
    	}
    } 
    
    void Gpio_select(void)
    {
    	EALLOW;
    	GpioCtrlRegs.GPAMUX1.all = 0;		// GPIO15 ... GPIO0 = General Puropse I/O
    	GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1;	// ePWM1A active
    
    	GpioCtrlRegs.GPAMUX2.all = 0;		// GPIO31 ... GPIO16 = General Purpose I/O
    	GpioCtrlRegs.GPBMUX1.all = 0;		// GPIO47 ... GPIO32 = General Purpose I/O
    	GpioCtrlRegs.GPBMUX2.all = 0;		// GPIO63 ... GPIO48 = General Purpose I/O
    	GpioCtrlRegs.GPCMUX1.all = 0;		// GPIO79 ... GPIO64 = General Purpose I/O
    	GpioCtrlRegs.GPCMUX2.all = 0;		// GPIO87 ... GPIO80 = General Purpose I/O
    	 
    	GpioCtrlRegs.GPADIR.all = 0;
    	GpioCtrlRegs.GPADIR.bit.GPIO9 = 1;	// peripheral explorer: LED LD1 at GPIO9
    	GpioCtrlRegs.GPADIR.bit.GPIO11 = 1;	// peripheral explorer: LED LD2 at GPIO11
    
    	GpioCtrlRegs.GPBDIR.all = 0;		// GPIO63-32 as inputs
    	GpioCtrlRegs.GPBDIR.bit.GPIO34 = 1;	// peripheral explorer: LED LD3 at GPIO34
    	GpioCtrlRegs.GPBDIR.bit.GPIO49 = 1; // peripheral explorer: LED LD4 at GPIO49
    	
    	GpioCtrlRegs.GPCDIR.all = 0;		// GPIO87-64 as inputs
    	EDIS;
    }  
    
    void Setup_ePWM1A(void)
    {
    	EPwm1Regs.TBCTL.bit.CLKDIV =  0;	// CLKDIV = 1		
    	EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0;	// HSPCLKDIV = 1
    	EPwm1Regs.TBCTL.bit.CTRMODE = 2;	// up - down mode
    
    	EPwm1Regs.AQCTLA.all = 0x0060;		// set ePWM1A on CMPA up
    										// clear ePWM1A on CMPA down
    
    	EPwm1Regs.TBPRD = 37500;			// 2KHz - PWM signal
    	// TBPRD = fCPU / ( 2 * fPWM *CLKDIV * HSPCLKDIV)
    	// TBPRD = 150MHz / (2 * 2kHz * 1 *1)
    	// TBPRD = 37500
    	EPwm1Regs.CMPA.half.CMPA  = 28125;	// 25% duty cycle
    	// CMPA = (100% - duty cycle)*TBPRD
    	// CMPA = 0.75 * 37500 = 28125
    } 
    
    interrupt void cpu_timer0_isr(void)
    {
    	CpuTimer0.InterruptCount++;
    	AdcRegs.ADCTRL2.bit.SOC_SEQ1 = 1;	// start ADC by software
    	EALLOW;
    	SysCtrlRegs.WDKEY = 0xAA;	// service WD #2
    	EDIS;
    	
    	PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
    }
    
    void Setup_ADC(void)
    {
    	InitAdc();
    	// Configure ADC
       	AdcRegs.ADCTRL1.bit.SEQ_CASC = 1;	   // Cascaded Sequencer Mode
       	AdcRegs.ADCTRL1.bit.CONT_RUN = 0;	   // No Continuous run
       	AdcRegs.ADCTRL1.bit.CPS = 0;		   // prescaler = 1	
       	AdcRegs.ADCTRL2.bit.EPWM_SOCA_SEQ1 = 0;  // Disable EPWM_SOCA to start SEQ1
    	AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1;  // Enable SEQ1 interrupt
    	AdcRegs.ADCTRL2.bit.INT_MOD_SEQ1 = 0;  // with every EOS
    	AdcRegs.ADCTRL3.bit.ADCCLKPS = 3;	   // Divide HSPCLK by 6
    	AdcRegs.ADCMAXCONV.all = 0;		       // 1 Conversion per start  
        AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 2;   // Setup ADCINA2 as input channel.
    }
    
    interrupt void adc_isr(void)
    {
    	static Uint16 index=0;                     	    // index into ADC buffers
    	AdcBuf[index] = _IQmpy(AdcFsVoltage, _IQ16toIQ((_iq)AdcRegs.ADCRESULT0));
    	
    	/*** Call the filter function ***/
    	xBuffer[0] = AdcBuf[index];				// Add the new entry to the delay chain
    	AdcBufFiltered[index] = IQssfir(xBuffer, coeffs, 5);
    
    	index++;									// Increment the index
    	if(index == AdcBufLen) index = 0;			// Rewind the pointer to beginning
    	// Reinitialize for next ADC sequence
      	AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1;   	// Reset SEQ1
      	AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1;		// Clear INT SEQ1 bit
      	PieCtrlRegs.PIEACK.all = 1;   			// Acknowledge interrupt to PIE
    }
    //===========================================================================
    // End of SourceCode.
    //===========================================================================
    

    当您同时获得屏幕截图和 PDF 后、能否回答我的问题? 我希望我的问题微不足道、也不复杂、可以扩展该线程。

    等待解决方案。

    此致、

    Ankit

    研究学生

    VIT, Vellore, India

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Omer:

    出于某些原因、Ankit、我们的客户无法上传主题中的文件、因此我将代表他上传文件。



    e2e.ti.com/.../Piccolo-workshop-PDF.pdf



    e2e.ti.com/.../Module-17-TMDSPREX28335-workshop-PDF-by-Frank-Bormann.pdf



    此致、

    Jejomar

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Jejomar

    在我深入回答问题之前、我想对一些问题进行无序的解释。 对于问题7、F28335中集成了 FPU、这意味着您可以使用浮点运算而不是定点数学运算(如 IQmath 中)。 如果客户 使用的系统已经使用 IQmath 或项目要求他们使用 IQmath、则他们可以使用 IQmath。 使用 FPU 优化启用支持时的浮点运算。 对于指定器件、只能将 FPU32用于--float_support 处理器选项(只有 F2838x 器件支持 FPU64)。

    (问题1-3)如果客户需要使用 IQmath、我可以解释格式的工作方式。 IQN 中的 N 决定了定点数的精度水平、 或者、为数字的小数部分保留32位整数中的位数(即、对于 IQ30、为小数部分保留30位、为整数部分保留2位、值范围为-2至1.999 999 999 999)。 您可以在位于 C2000Ware 的 IQmath_Quickstart.pdf (C2000Ware_4_XX_XX_XX_XX\libraries\fath\IQmath\c28\docs)中找到此信息。  某些整数转换为_iq 数据类型时、这基本上意味着编译器要针对 IQmath 将该数字的一部分转换为整数、其余部分转换为分数。 通常、 如果要将整数转换为定点格式、需要将位向左移动以考虑数字的额外小数部分、我不确定您附加的代码为什么不这样做。

    如果 客户对其他用户的代码有疑问、最好询问该用户。 除非是 TI 的一个示例、否则在尝试确定其他客户代码背后的逻辑时、可能需要进行大量猜测。 我将尝试寻找我们的专家来回答与 IQmath/FPU 无关的其他问题。

    对于问题5、如果要比较的两个 IQ 数字采用相同的 IQN 格式、则可以像任何其他整数一样对它们进行比较、因为它们是定点。 如果有任何问题、请告诉我、我可以 尝试在我身边进行测试。

    对于问题6、 您需要修改实际头文件中的 global-Q 值、没有办法通过函数修改它、也没有办法创建自己的宏、而不覆盖该头文件。

    我不是该领域的专家、因此我会尝试寻找其他人来回答问题4。

    此致、

    Omer Amir

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Jejomar 和 Omer:

    Jejomar、非常感谢您代我上传屏幕截图和 PDF。

    我感谢你的诚实和礼仪,当你承认你不是相关领域的问题4。 非常感谢您对其余6个问题的澄清。

    我承认,我在上一次答复中所提供的网址包含一个可能已被该用户修改的代码,该代码超出了要查看的范围。 但是、Jejomar 先生确实提供了两个不同的屏幕截图、其中包含属于 TI 的类似行。 或许我随附的那些 PDF、屏幕截图和代码(由 TI 工程师编码)可能已经被引用了。 不管怎样,我应用了一些推理和逻辑自己,我能够找到确切的答案,所有的问题。

    应答1:(_IQ) AdcRegs.ADCRESULT0确实会将 AdcRegs.ADCRESULT0寄存器的内容预先指定为 IQ 格式(即使没有像 IQ 这样的实际数据类型)。

    答案2:AdcRegs.ADCRESULT0和 AdcMirror.ADCRESULT0都是16位寄存器、而 ADC 结果只有12位。  对于 AdcRegs.ADCRESULT0寄存器、第0位到第3位不可访问(因为它是左对齐的)、对于 AdcMirror.ADCRESULT0 (因为它是左对齐的)、第12位到第15位不可访问。 因此、即使代码包括_IQ16toIQ ((_IQ) AdcRegs.ADCRESULT0)或_IQ16toIQ ((_IQ) AdcMirror.ADCRESULT0)、也只会从两个寄存器的相关12位中提取信息。 _IQ12toIQ ((_IQ) AdcMirror.ADCRESULT0)将信息直接转换为全局 Q 格式(因为 AdcMirror.ADCRESULT0包含默认右对齐格式的数据)、而_IQ12toIQ ((_IQ) AdcRegs.ADCRESULT0>>4)在正确对齐内容后将信息转换为全局 Q 格式。 因此、基本上所有三种样式(a)_IQ16toIQ (_IQ) AdcRegs.ADCRESULT0)、b)_IQ12toIQ ((_IQ) AdcRegs.ADCRESULT0>4)、c)_IQ12toIQ ((_IQ) AdcMirror.ADCRESULT0))都能产生完全相同的结果。 但是、由于 AdcMirror 寄存器不需要等待、因此我更倾向于_IQ12toIQ ((_IQ) AdcMirror.ADCRESULT0)。

    回答3:用户可以使用自己的校准 M 和 C 值实现 Y = Mx + C 方法。 不过、语法正确。 例如:_IQ (M)*_IQ12toIQ ((_IQ) AdcMirror.ADCRESULT0)+_IQ (C);

    AN 4:获得调制指数后、用户可以将最终结果转换为 CMPA 或 CMPB 寄存器的数据类型(在检查观察窗口后为 unsigned int)。

    回答5:If 语句的执行方式与其他数据类型相同。

    应答6:用户可以使用以下语法在主代码本身中定义 GLOBAL_Q 值:

    #define global-Q 22 //默认 Q 为24

    #include "IQmathlib.h"

    只需在代码中包含 IQmath.h 之前定义 Global-Q 即可

    回答7:用户可以将 FPU32保留在"指定浮点支持"中、并在主代码中包括 IQmath.h。

    期待您的意见。

    此致、

    Ankit

    研究学生

    VIT, Vellore, India

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Ankit 您好!

    是的、它会被转换; 从技术上讲、_iq 或 IQN 是一个数据类型、但它实际上只是一个 由 IQmath 库指定的长整数。

    2.从我在参考手册中看到的内容来看,这是正确的

    3、我认为这个代码来自另外一个用户、所以你可以与他们确认这个代码(在 这里的 TI 实验课程中我找不到这个代码)

    4.我以前不理解你的问题,但是的,你的理解是正确的;确保如果你 转换来自 IQ 数据类型的结果,你移动相应的位以截断整数的小数部分。

    5.是的、因为它不是浮点、所以是定点

    6.是的、如果您在 IQmathlib.h 头文件之前定义它、那么这将起作用;请确保使用 GLOBAL_Q、因为这是所使用的实际宏。

    7.你可以这样做,但我不明白你为什么要这样做。 当 FPU 在器件上不可用时、IQMath 用于代替浮点。 您在 F2833x 器件上有一个 FPU、因此可以使用 IQmath 并包含 FPU32 以提供浮点支持、但保留此项的唯一原因是在项目完全基于 IQmath (定点数学)的情况下将其中一个转换为另一个。 但您需要使用浮点以获得更高的精度。 如果在这种情况下、您所处理的项目需要它、那就没问题了。 我只想确保大家知道、IQmath 基本上只是由于遗留原因而包含在内、 其目的是让用户将浮点数学用于精密数学运算、因为硬件支持这种数学运算。

    此致、

    Omer Amir

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Omer:

    非常感谢您对我的解释的认可、也感谢您纠正我的假设、即 IQ 不是数据类型。 在我的所有问题都已回答后、您可以继续并关闭此主题。

    非常感谢 Jejomar 也张贴的查询和屏幕截图和 PDF 代表我.

    此致、

    Ankit

    研究学生

    VIT, Vellore, India