Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: DMAC + SSP на STR912
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Dron_Gus
Пытаюсь переписать функции для работы с MMC картой для работы через DMA Полный ноль.

Для начала, как я понимаю, запрос на ДМА воникает при приеме данных. Чтобы что-то принять нужно что-то передать. Настраиваю один канал на прием, второй на передачу. Как запустить передачу? Сигнал SSP TX постоянно активен, пока не заполнено FIFO? Или надо произвести какие-то телодвижения, чтобы запустить передачу?



Пока имею такой код. ДМА не запускается, код ничего никуда не грузит... sad.gif



Код
   DMA_InitTypeDef  DMA_InitStruct;
  
  DMA_ChannelCmd(DMA_Channel0, DISABLE);                      // Disable the DMA channel
  DMA_ChannelCmd(DMA_Channel1, DISABLE);                      // Disable the DMA channel
  
  /* RECIVE */
  DMA_StructInit(&DMA_InitStruct);

  /* Write the first LLI*/
  DMA_InitStruct.DMA_Channel_LLstItm = 0;                     // Set the addresses of next linked list for the first LLI structure
  DMA_InitStruct.DMA_Channel_SrcAdd = (u32)&(SSP0->DR);       // source address for the first LLI structure
  DMA_InitStruct.DMA_Channel_DesAdd = (u32)pData;             // Destination address for the first LLI structure
  DMA_InitStruct.DMA_Channel_SrcWidth = DMA_SrcWidth_Byte;    // The source bus width is a byte
  DMA_InitStruct.DMA_Channel_DesWidth = DMA_SrcWidth_Byte;    // The Destination bus width is a byte
  DMA_InitStruct.DMA_Channel_FlowCntrl = DMA_FlowCntrl_Perip2;// DMA is The flow controller
  DMA_InitStruct.DMA_Channel_TrsfSize = 512;                  // Transfer size
  DMA_InitStruct.DMA_Channel_Src = DMA_SRC_SSP0_RX;           // Chanel source

  /* Configure the DMA channel1 "the chosen channel to perform the transfer" */

  DMA_ChannelSRCIncConfig (DMA_Channel0, DISABLE);
  DMA_ChannelDESIncConfig (DMA_Channel0, ENABLE);
  DMA_SyncConfig(0, ENABLE);

  DMA_Init(DMA_Channel0,&DMA_InitStruct);                     // update the DMA channel1 registers with the cfirst LLI structure
  
  /* TRANSFER */
  DMA_StructInit(&DMA_InitStruct);

  /* Write the first LLI*/
  DMA_InitStruct.DMA_Channel_LLstItm = 0;                     // Set the addresses of next linked list for the first LLI structure
  DMA_InitStruct.DMA_Channel_SrcAdd = (u32)pData;             // source address for the first LLI structure
  DMA_InitStruct.DMA_Channel_DesAdd = (u32)&(SSP0->DR);       // Destination address for the first LLI structure
  DMA_InitStruct.DMA_Channel_SrcWidth = DMA_SrcWidth_Byte;    // The source bus width is a byte
  DMA_InitStruct.DMA_Channel_DesWidth = DMA_SrcWidth_Byte;    // The Destination bus width is a byte
  DMA_InitStruct.DMA_Channel_FlowCntrl = DMA_FlowCntrl_Perip1;// DMA is The flow controller
  DMA_InitStruct.DMA_Channel_TrsfSize = 512;                  // Transfer size
  DMA_InitStruct.DMA_Channel_Src = DMA_DES_SSP0_TX;           // Chanel destination

  /* Configure the DMA channel1 "the chosen channel to perform the transfer" */

  DMA_ChannelSRCIncConfig(DMA_Channel1, ENABLE);
  DMA_ChannelDESIncConfig(DMA_Channel1, DISABLE);
  DMA_SyncConfig(1, ENABLE);

  DMA_Init(DMA_Channel1,&DMA_InitStruct);                     // update the DMA channel1 registers with the cfirst LLI structure
  
  DMA_ChannelCmd(DMA_Channel0,ENABLE);                        // Enable the DMA channel
  DMA_ChannelCmd(DMA_Channel1,ENABLE);                        // Enable the DMA channel
  
  

  /*wait for the fifo to be empty*/
  while(DMA_GetChannelActiveStatus(DMA_Channel1));
Bosicc
Цитата(Dron_Gus @ Jan 2 2008, 03:36) *
Как запустить передачу? Сигнал SSP TX постоянно активен, пока не заполнено FIFO? Или надо произвести какие-то телодвижения, чтобы запустить передачу?


Как я понимаю, после настройки SSP в режиме Мастер передача должна начаться, сразу после того, как Вы положите байт в TX FIFO. По окончанию передачи ( когда FIFO пусто ) будет интерапт, и можно проверить буфер приёма и послать следующий байт. Это если я все правильно понимаю. smile.gif

Если так оно не работает, то смотрите на настройки SSP.
Dron_Gus
Так оно работает. Но я пытаюсь прикрутить DMA. С этим то и вопрос - по какому сигналу DMA начинает класть данные в выходное FIFO?
Dron_Gus
Код
void MmcReceiveBlock (pInt8U pData, Int32U Size)
  {
  char SSP_WriteByte = 0xff;
  DMA_InitTypeDef  DMA_InitStruct;
  
  /* Enable FIFO */
  SSP_DMACmd(SSP0, SSP_DMA_Receive, ENABLE);
  SSP_DMACmd(SSP0, SSP_DMA_Transmit, ENABLE);
  
  /* RECIVE - DMA ch 0 */
  DMA_StructInit(&DMA_InitStruct);
  
  /*No linked lists used*/
  DMA_InitStruct.DMA_Channel_LLstItm = 0;
  /*Source address*/
  DMA_InitStruct.DMA_Channel_SrcAdd = (u32)(&SSP0->DR);
  /*Destination address*/
  DMA_InitStruct.DMA_Channel_DesAdd = (u32)pData;
  /*Source width of one byte*/
  DMA_InitStruct.DMA_Channel_SrcWidth = DMA_SrcWidth_Byte;
  /*DMA burst size of 4 data*/
  DMA_InitStruct.DMA_Channel_SrcBstSize = DMA_SrcBst_4Data;
  /*Destination width of one byte*/
  DMA_InitStruct.DMA_Channel_DesWidth = DMA_DesWidth_Byte;
  /*The flow controller is the DMAC*/
  DMA_InitStruct.DMA_Channel_FlowCntrl = DMA_FlowCntrl2_DMA;
  /* DMA req source */
  DMA_InitStruct.DMA_Channel_Src = DMA_SRC_SSP0_RX;
  /*Transfer size */
  DMA_InitStruct.DMA_Channel_TrsfSize = Size;
  /*We increment the destination not the source */
  DMA_ChannelSRCIncConfig (DMA_Channel0, DISABLE);
  DMA_ChannelDESIncConfig (DMA_Channel0, ENABLE);
  /* Init */
  DMA_Init(DMA_Channel0, &DMA_InitStruct);
  
  /* TRANSFER - DMA ch 1 */
  DMA_StructInit(&DMA_InitStruct);
  
  /*No linked lists used*/
  DMA_InitStruct.DMA_Channel_LLstItm = 0;
  /*Source address*/
  DMA_InitStruct.DMA_Channel_SrcAdd = (u32)&SSP_WriteByte;
  /*Destination address*/
  DMA_InitStruct.DMA_Channel_DesAdd = (u32)((&SSP0->DR));
  /*Source width of one byte*/
  DMA_InitStruct.DMA_Channel_SrcWidth = DMA_SrcWidth_Byte;
  /*Destination width of one byte*/
  DMA_InitStruct.DMA_Channel_DesWidth = DMA_DesWidth_Byte;
  /*The flow controller is the DMAC*/
  DMA_InitStruct.DMA_Channel_FlowCntrl = DMA_FlowCntrl1_DMA;
  DMA_InitStruct.DMA_Channel_Des = DMA_DES_SSP0_TX;
  /*Transfer size */
  DMA_InitStruct.DMA_Channel_TrsfSize = Size;
  /*We increment not the source not the destination*/
  DMA_ChannelSRCIncConfig (DMA_Channel1, DISABLE);
  DMA_ChannelDESIncConfig (DMA_Channel1, DISABLE);
  /* Init */
  DMA_Init(DMA_Channel1, &DMA_InitStruct);
  
  /* Start transfer */
  DMA_ChannelCmd(DMA_Channel0, ENABLE);
  DMA_ChannelCmd(DMA_Channel1, ENABLE);
  
  /* wait for the transmit channel FIFO to be empty */
  while(DMA_GetChannelActiveStatus(DMA_Channel1));
  /* wait for the recive channel FIFO to be empty */
  while(DMA_GetChannelActiveStatus(DMA_Channel0));


Рабочий код. Производительность не мерил, но с ним уже удается без остановки "кормить" двухканальный PWM wav-файлом (44100 16 x 2) через EFSL. Это апримерно 176 Кбайт/сек. В два раза ускоряю - уже не вытягивает.

На всяк случай косяки на которые я наступил:
DMA на прием запускается не сразу - поэтому ловить его остановку стоит только после остановки DMA на передачу, иначе можем выскочить из функции еще до того, как DMA на прем принял хоть байт.
На сайте ST есть прмеры DMA под многие периферии. Правда примеров с одновременной передачей-приемом нет.

З.Ы. от шумов (потрескивания) PWM можно как-нить избавиться? Сейчас на выходе стоит цепочка (резистор)-(кондер на землю)-(резистор)-(кондер на землю). Противно посчелкивает сразу же после включения PWM.
AlexandrY
К сожалению не могу помочь вам решить проблему используя либы от ST.
Но по ссылке есть пакет примеров для STR91x c DMA и для SD карт и для аудиокодека.
http://aly.ogmis.lt/Projects/ARMDominator/ProbePrg_ArmD3.zip

Схема тестовой платформы здесь:
http://aly.ogmis.lt/Projects/ARMDominator/ARMDominator.htm


Цитата(Dron_Gus @ Jan 3 2008, 04:38) *
[code]
Рабочий код. Производительность не мерил, но с ним уже удается без остановки "кормить" двухканальный PWM wav-файлом (44100 16 x 2) через EFSL. Это апримерно 176 Кбайт/сек. В два раза ускоряю - уже не вытягивает.

На всяк случай косяки на которые я наступил:
DMA на прием запускается не сразу - поэтому ловить его остановку стоит только после остановки DMA на передачу, иначе можем выскочить из функции еще до того, как DMA на прем принял хоть байт.
На сайте ST есть прмеры DMA под многие периферии. Правда примеров с одновременной передачей-приемом нет.

З.Ы. от шумов (потрескивания) PWM можно как-нить избавиться? Сейчас на выходе стоит цепочка (резистор)-(кондер на землю)-(резистор)-(кондер на землю). Противно посчелкивает сразу же после включения PWM.
Dron_Gus
Цитата(AlexandrY @ Jan 3 2008, 09:24) *
К сожалению не могу помочь вам решить проблему используя либы от ST.


Да, либы их малость "монстроидальные". Смысла их пользовать нет.



Цитата(AlexandrY @ Jan 3 2008, 09:24) *
Но по ссылке есть пакет примеров для STR91x c DMA и для SD карт и для аудиокодека.


Спасибо огромное, уже изучаю. Как раз стоит задача прикрутить TLV320AIC к STR912. Что-то мне подсказывает, что это не реально...
sergvks
Цитата(Dron_Gus @ Jan 3 2008, 16:02) *
Да, либы их малость "монстроидальные". Смысла их пользовать нет.





Спасибо огромное, уже изучаю. Как раз стоит задача прикрутить TLV320AIC к STR912. Что-то мне подсказывает, что это не реально...


TLV320AIC10 прекрасно пашет с str911, кодек работает мастером - так проще.
Dron_Gus
Можно поподробней? К какому интерфейсу Вы его прикрутили? К SSP я, как ни прикидывал, не смог прикрутить. sad.gif
SGP
У меня DMA и SSP удалось подружить всё работает, единственно что третий канал DMA с прерыванием не удалось подружить. привожу фрагменты кода "как есть".
==============================================
последовательность програмироания при инициализации

Init_SSP0_fast_DMA()
---------------------------------------
SSP0->CR0 = 0xxxxx; // параметры ssp по месту
SSP0->CR1 = 0xxx;
SSP0->PR = 0xxx;
SSP0->IMSCR = 0x04; /* SSP0 Inerrupt enable: RXIM - receive */
SSP0->ICR = 0x00; /* SSP0 Inerrupt clear */
SSP0->DMACR = 0x03; /* SSP0 DMA control - Transmit */
----------------------------------------------------

SSP0_enable();
----------------------------------------------------
k = SSP0->DR; /* byte 1 load 8 byte only Read */
k = SSP0->DR; /* byte 2 load 8 byte only Read */
k = SSP0->DR; /* byte 3 load 8 byte only Read */
k = SSP0->DR; /* byte 4 load 8 byte only Read */
k = SSP0->DR; /* byte 5 load 8 byte only Read */
k = SSP0->DR; /* byte 6 load 8 byte only Read */
k = SSP0->DR; /* byte 7 load 8 byte only Read */
k = SSP0->DR; /* byte 8 load 8 byte only Read */

SSP0->ICR = 0x03; // clear IRQ
SSP0->CR1 |= 0x02;
----------------------------------------------------

Init_DMA_IRQ ();
----------------------------------------------------
VIC0->VAiR[12] = (unsigned int)DMA_IRQ_Handler; /* Setup DMA IRQ Hndl addr */
VIC0->VCiR[12] |= 12; /* Specify the interrupt number */
VIC0->VCiR[12] |= 0x20; /* Enable the vector interrupt */
VIC0->INTER |= (1<<12); /* Enable SSP0 interrupt */
----------------------------------------------------

Init_DMA_5_6_7 ();
----------------------------------------------------
DMA->SYNR = 0x00; /* SYNC = 0 enable */
DMA->CNFR = 0x01; /* EN = 1 enable DMA */
/* ------------ INIT DMA CHANEL 5 SPI_0 Resiver ------------------------------------------*/
DMA_Channel5->SRC = 0x4C007008; /* DMA-5 Source Address SSP0_DR */
DMA_Channel5->DES = (u32)(&bufer[3]); /* DMA-5 Destination Address + 4; 4000000 - RAM */
DMA_Channel5->LLI = 0; /* DMA-5 LLI => 0 */
DMA_Channel5->CC = 0xE80003A1; /* interrupt enable, cacheable, bufferable, */
/* INC dst, DWIDTH = 8 bit, SWIDTH = 8 bit, */
/* DBsize = 1 byte, SBsize = 1 byte, N -= 4 ... */
DMA_Channel5->CCNF = 0x00009358; /* interrupt enable, periph-mem DMA, ok */
/* SrcPeriph = SSP_0 RX */
/* ------------ INIT DMA CHANEL 6 SPI_0 Transmit -----------------------------------------*/
DMA_Channel6->SRC = (u32)(&bufer[3]); /* DMA-6 Source Address + 4; 4000000 - RAM */
DMA_Channel6->DES = 0x4C007008; /* DMA-6 Destination Address SSP0_DR */
DMA_Channel6->LLI = 0; /* DMA-6 LLI => Link[0] */
DMA_Channel6->CC = 0xE40003A1; /* interrupt enable, cacheable, bufferable, ok */
/* INC src, DWIDTH = 8 bit, SWIDTH = 8 bit, */
/* DBsize = 1 byte, SBsize = 1 byte, N -= 4 ... */
DMA_Channel6->CCNF = 0x00000B58; /* interrupt disable, mem-periph DMA, not IRQ */
/* DstPeriph = SSP_0 TX N = BAH = 186 - max */
/* ------------ INIT DMA CHANEL 7 Memory to Memory ---------------------------------------*/
DMA_Channel7->SRC = (u32)(&bufer[0]); /* DMA-7 Source Address kadr_bufer - RAM */
DMA_Channel7->DES = (u32)(&e_bufer[0]); /* DMA-7 Destination Address picture_bufer */
DMA_Channel7->LLI = 0; /* DMA-7 LLI => Link[0] */
DMA_Channel7->CC = 0xCC4800E9; /* interrupt enable, INC dst, INC src, */
/* DWIDTH = 32 bit, SWIDTH = 32 bit, */
/* DBsize = 0, SBsize = 0, N = E9 = 233 (*4) */
DMA_Channel7->CCNF = 0x00000084; /* interrupt disable, mem-mem DMA, */
----------------------------------------------------

Init_DMA_4 ();
----------------------------------------------------
/* ------------ RE_INIT DMA CHANEL 7 Mem to Mem copy -----------------------------------*/
DMA_Channel4->SRC = (u32)(&bufer[0]); /* DMA-7 Source Address bufer - RAM */
DMA_Channel4->DES = (u32)(&e_bufer[0]); /* DMA-7 Destination Address e_bufer */
DMA_Channel4->LLI = 0; /* DMA-7 LLI => Link[0] */
DMA_Channel4->CC = 0xCC4800E9; /* interrupt enable, INC dst, INC src, */
DMA_Channel4->CCNF = 0x00000084; /* interrupt disable, mem-mem DMA, */
----------------------------------------------------

Enable_DMA ();
----------------------------------------------------
DMA_Channel7->CCNF |= 0x01; /* DMA enable */

----------------------------------------------------

Chip_SPI_Reset();

и далее по тексту....
==========================================
В самой программе необходимо обеспечить старт а затем всё будет обеспечиваться самим DMA
DMA_Channel5->CCNF |= 0x00000001; /* DMA-5 SPI_0 reseive START */
DMA_Channel6->CCNF |= 0x00000001; //

============================================================================
============================================================================
DMA_IRQ_Handler

PUSH {R0-R3,LR}
;-------- control chanel 5 & 6 ---------------------
LDR R0,=DMA_BASE ; =DMA_BASE
;
MOV R1,#0x00000060 ;-------- Chanel 5 & 6 -----------------------------
STR R1,[R0,#0x08] ; clear Terminal Count Interrupt 5 & 6
;-------- DMA --------------------------------------
LDR R2,=DMA_CC5_init ;
STR R2,[R0,#DMA_CC5] ; DMA_CC5

LDR R2,=DMA_CC6_init ;
STR R2,[R0,#DMA_CC6] ; DMA_CC6

MOV R2,#0 ; LLI = 0
STR R2,[R0,#DMA_LLI5] ; LLI5 = 0
STR R2,[R0,#DMA_LLI6] ; LLI6 = 0
;-------- update kad -------------------------
LDR R2,=kadr_kadr ; Load adres
LDRB R3,[R2,#0] ; Load = kad
ADD R3,R3,#1 ;
AND R3,R3,#mask_DMA_number_kadr ;
STRB R3,[R2,#0] ; save new kad
;-------- new adres bufera -------------------------
LDR R1,=kadr_bufer ; Load adres
MOV R2,#size_kadr ; size_kad (offset)
MLA R2,R2,R3,R1 ; new adres bufera DST
;-------- program DMA_5_6 -------------------------
STR R2,[R0,#DMA_DEST5] ; DMA_DEST5
STR R2,[R0,#DMA_SRC6] ; DMA_SRC6
;-------- Ready_Date -------------------------------
LDR R1,=Ready_Date ; Load adres
LDRB R2,[R1,#0] ; Load = Ready_Date
ADD R2,R2,#1 ;
STRB R2,[R1,#0] ; Save = Ready_Date
CMP R2,#15 ;
BHS Konec_DMA ; if BUSY go to
;-------- start DMA_5_6 ----------------------------
LDR R3,[R0,#DMA_CCNF5] ; chanel 5 RX SSP_0
ORR R3,R3,#0x00000001 ;
STR R3,[R0,#DMA_CCNF5] ;

LDR R3,[R0,#DMA_CCNF6] ; chanel 6 TX SSP_0
ORR R3,R3,#0x00000001 ;
STR R3,[R0,#DMA_CCNF6] ;
Konec_DMA ;-------- END ------------------------------------
MOV R1,#0 ;
STR R1,[R1,#-0xfd0] ;
MOV R1,#0 ;
MOV R2,#0xfc000000 ;
STR R1,[R2,#0x30] ;
POP {R0-R3,LR} ;
SUBS PC,LR,#4 ;
Dron_Gus
Спасибо! А аппаратно какие сигналы TLV320AIC к каким сигналам SSP идут? И в каком режиме работает TLV320AIC? DSP Mode?
sergvks
Цитата(Dron_Gus @ Jan 13 2008, 13:03) *
Спасибо! А аппаратно какие сигналы TLV320AIC к каким сигналам SSP идут? И в каком режиме работает TLV320AIC? DSP Mode?

FS Pulse Mode (M1M0 = 00)

/* TIMER2 for codec clock configuration -------------------------------------*/
SCU_APBPeriphClockConfig(__TIM23, ENABLE); /* Enable the clock for TIM2*/
TIM_DeInit(TIM2);
TIM_DeInit(TIM3);
/* GPIO6 configuration (PWM on P6.4) */
GPIO_Struct.GPIO_Pin = GPIO_Pin_4;
GPIO_Struct.GPIO_Direction = GPIO_PinOutput;
GPIO_Struct.GPIO_Type = GPIO_Type_PushPull;
GPIO_Struct.GPIO_IPConnected = GPIO_IPConnected_Disable;
GPIO_Struct.GPIO_Alternate = GPIO_OutputAlt2;
GPIO_Init(GPIO6,&GPIO_Struct);

/* TIM02 Structure Initialization */
TIM_StructInit(&TIM_InitStruct);

/* TIM2 Configuration in PWM Mode */
TIM_InitStruct.TIM_Mode = TIM_PWM;
TIM_InitStruct.TIM_Clock_Source = TIM_CLK_APB;
TIM_InitStruct.TIM_Prescaler = 0x00;
TIM_InitStruct.TIM_Pulse_Level_1 = TIM_HIGH;
TIM_InitStruct.TIM_Period_Level = TIM_LOW;
TIM_InitStruct.TIM_Pulse_Length_1 = 10;
TIM_InitStruct.TIM_Full_Period = 19;
TIM_Init (TIM2, &TIM_InitStruct);

/* SSP0 for codec configuration ----------------------------------------------*/
/* Enable the __SSP0 Clock */
SCU_APBPeriphClockConfig(__SSP0 ,ENABLE);
SSP_DeInit(SSP0);
/*Gonfigure SSP0_CLK, SSP0_MOSI, SSP0_nSS pins */
SCU_APBPeriphClockConfig(__GPIO5,ENABLE);
GPIO_DeInit(GPIO5);
GPIO_Struct.GPIO_Direction = GPIO_PinInput;
GPIO_Struct.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
GPIO_Struct.GPIO_Type = GPIO_Type_PushPull ;
GPIO_Struct.GPIO_IPConnected = GPIO_IPConnected_Enable;
GPIO_Struct.GPIO_Alternate = GPIO_InputAlt1 ;
GPIO_Init (GPIO5, &GPIO_Struct);

/*Gonfigure SSP0_MISO pin GPIO5.6*/
GPIO_Struct.GPIO_Direction = GPIO_PinOutput;
GPIO_Struct.GPIO_Pin = GPIO_Pin_6;
GPIO_Struct.GPIO_Type = GPIO_Type_PushPull ;
GPIO_Struct.GPIO_IPConnected = GPIO_IPConnected_Disable;
GPIO_Struct.GPIO_Alternate = GPIO_OutputAlt2 ;
GPIO_Init (GPIO5, &GPIO_Struct);

/* SSP0 configuration */
SSP_DeInit(SSP0);
SSP_InitStruct.SSP_FrameFormat = SSP_FrameFormat_TI ;
SSP_InitStruct.SSP_Mode = SSP_Mode_Slave;
SSP_InitStruct.SSP_CPOL = SSP_CPOL_Low;
SSP_InitStruct.SSP_CPHA = SSP_CPHA_2Edge;
SSP_InitStruct.SSP_DataSize = SSP_DataSize_16b;
SSP_InitStruct.SSP_ClockRate = 2;
SSP_InitStruct.SSP_ClockPrescaler = 12;
SSP_InitStruct.SSP_SlaveOutput = SSP_SlaveOutput_Enable;
SSP_Init(SSP0, &SSP_InitStruct);

/* SSP0 enable */
SSP_Cmd(SSP0, ENABLE);
SGP
У меня микроконтроллер работал с датчиком, в силу особенностей которого чип-селект формировался программно. По поводу TLV320AIC ничего не могу сказать.
Dron_Gus
Всем спасибо! tlv320aic к str912 попробую прицепить на выходных. К сам7 прицепился без проблем. Но у того спец. интерфейс под это есть.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.