Подскажите для чего нужен DMA и если можно пример как его использовать. Пример "фирменный" или свой - в чем "СОЛЬ" DMA ?
beer_warrior
Jan 31 2007, 01:07
DMA aka Direct Memory Access - дает возможность принимать данные полностью на аппаратном уровне. Проинициализировав DMA один раз можно уже не беспокоиться об обработке приема/передачи. Например УАРТ надо постоянно поллить или вызывать прерывание по получению байта. С помощью DMA указываеться адрес буфера и количество байт. После этого ядро будет заниматься своими делами, а данные потихоньку капать в буфер. Прерывание вызоветься уже по окончании приема. То же самое и с передачей.
Спасибо. подскажите апноут какой нить чтоб посмотреть конкретно.
и еще - этот буфер будет требовать данные или принимть по мере помтупления ? т.е. как передатчик даст ... или по всякому бывает.
и еще - ресь идет о внутренней РАМ или и внешней тоже ?
и какова скорость приема дданных ? например по параллельному интерфейсу в LPC или SAM - во сколько раз медленней такта ?
defunct
Jan 31 2007, 02:36
DMA - это маленький простеникий и тупой slave сопроцессор, который занимается только тем что копирует данные из одного места в другое. Сам по себе DMA работать не может. Основной процессор должен инициализировать DMA - задать ему адрес источника данных, адрес приемника данных, количество данных, после чего - запустить DMA. DMA оповестит основной процессор с помощью прерывания после того как окончит работу.
Скорость работы DMA - определяется источником и приемником данных. С памятью DMA работает точно также как и процессор, т.о. он может писать и во внешнюю память и во внутреннюю. За исключением того, что DMA и процессору приходится делить шину памяти. Доминирует на шине процессор, тобиш если процессору срочно потребовалось что-то прочитать/записать в/из памяти - то DMA в это время будет курить в сторонке.
beer_warrior
Jan 31 2007, 10:27
Цитата
Спасибо. подскажите апноут какой нить чтоб посмотреть конкретно.
Нечего там смотреть. Прописываются два регистра - указатель на буфер и количество байт. Для SAM все описание узла страницы на три включая раскладку регистров.
Цитата
и еще - этот буфер будет требовать данные или принимть по мере помтупления ? т.е. как передатчик даст ... или по всякому бывает.
По сути DMA это второй счетчик адреса. Т.е. в обычном режиме идет пересылка IO -> регистры ядра -> RAM, с DMA IO -> RAM (для передачи наоборот) пока не насчитает заданное количество байт.
Для IO будет ждать готовности данных, для памяти будет молотить пока свободна шина.
большое спасибо за помощь. сижу разбираюсь.
zltigo
Jan 31 2007, 20:34
Цитата(defunct @ Jan 31 2007, 01:36)

Доминирует на шине процессор, тобиш если процессору срочно потребовалось что-то прочитать/записать в/из памяти - то DMA в это время будет курить в сторонке.
C сточностью до наоборот - курить будет процессор, а DMA будет его тормозить и тупо выполнять его приказ.
Dron_Gus
Jan 31 2007, 22:17
Цитата(zltigo @ Jan 31 2007, 20:34)

C сточностью до наоборот - курить будет процессор, а DMA будет его тормозить и тупо выполнять его приказ.
Странно. Всегда считал наоборот. Например DMA у ARMов от TI курят как раз, когда проц работает.
beer_warrior
Jan 31 2007, 22:35
Цитата
C сточностью до наоборот - курить будет процессор, а DMA будет его тормозить и тупо выполнять его приказ.
А и действительно. Не дадите ли ссылку на доку? Момент ведь весьма принципиальный.
zltigo
Режим работы контроллера DMA определяется в первую очередь типом используемой накристальной шины. В частности, шина AHB может иметь несколько ведущих и ведомых, и несколько шина адреса данных, подключаемых через мультиплексор. Поэтому время задержки транзакции незначительно, то есть как CPU, так и DMA имеют, в среднем, одинаковые возможности для доступа к ресурсам.
Поправите, если не прав.
Abakt
В режиме DMA не используются ресурсы (регистры, АЛУ и т.п.) на операции пересылки данных.
sonycman
Feb 1 2007, 00:40
Цитата(beer_warrior @ Jan 31 2007, 23:35)

А и действительно. Не дадите ли ссылку на доку? Момент ведь весьма принципиальный.
Вот что написано в мануале к SAM7S:
The Memory Controller has a simple, hard-wired priority bus arbiter that gives the control of the
bus to one of the two masters. The Peripheral DMA Controller has the highest priority; the ARM
processor has the lowest one.
[AT91SAM7S.pdf, 6175G–ATARM–22-Nov-06, page 120]
Действительно, курить в сторонке будет процессор, а не DMA...
Помогите, пожалуйста, с конкретной задачей: Необходимо через ДМА передавать массив в SPI и получать из него ответ (контроллер SAM7S64). Какие операции необходимо произвести?
Цитата(Karl @ Feb 1 2007, 08:49)

Помогите, пожалуйста, с конкретной задачей: Необходимо через ДМА передавать массив в SPI и получать из него ответ (контроллер SAM7S64). Какие операции необходимо произвести?
Вот как делаю я (коментарии от специалистов приветствуются

)
Здесь только отсылка, ну и особенности от FreeRTOS, но общий смысл должен быть понятен.
Код
void SPI_task(void *pvParameters)
{
unsigned portCHAR i;
unsigned portCHAR SPI_buf[8];
( void ) pvParameters;
portENTER_CRITICAL();
AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC, 1 << AT91C_ID_SPI);
AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, AT91C_PA11_NPCS0 |
AT91C_PA12_MISO |
AT91C_PA13_MOSI |
AT91C_PA14_SPCK, 0);
AT91F_SPI_Reset(AT91C_BASE_SPI);
AT91C_BASE_SPI->SPI_MR=0x0E0011;//Master mode, fixed select, disable decoder, FDIV=0 (MCK), PCS=1110
AT91F_SPI_CfgCs(AT91C_BASE_SPI, 0, AT91C_SPI_NCPHA | (128 << 8) | 0x04 );
AT91F_SPI_Enable(AT91C_BASE_SPI);
AT91F_PDC_Open(AT91C_BASE_PDC_SPI);
for(i=0;i<8;i++) SPI_buf[i]=i;
portEXIT_CRITICAL();
for(;;)
{
while(AT91F_SPI_SendFrame(AT91C_BASE_SPI,(char*) &SPI_buf[0],8,0,0) == 0)
vTaskDelay(portTICK_RATE_MS * 10);
i++;
SPI_buf[7]=i;
vTaskDelay(portTICK_RATE_MS * 1000);
}
}
Спасибо, вроде все понятно. А с приемом Вы не разбирались?
Цитата(Karl @ Feb 1 2007, 13:26)

Спасибо, вроде все понятно. А с приемом Вы не разбирались?
Нет, мне пока без надобности. Я на прием PDC использую с ADC. Код надо?
Dron_Gus
Feb 1 2007, 14:53
Цитата(Kitsok @ Feb 1 2007, 14:08)

Нет, мне пока без надобности. Я на прием PDC использую с ADC. Код надо?
Мне надо.

Если не затруднит.
Цитата(Kitsok @ Feb 1 2007, 16:08)

Цитата(Karl @ Feb 1 2007, 13:26)

Спасибо, вроде все понятно. А с приемом Вы не разбирались?
Нет, мне пока без надобности. Я на прием PDC использую с ADC. Код надо?
Да, пригодится. Кстати, передача заработала сразу

Спасибо!
У меня на SPI АЦП сидит. Так что надо и передачу и прием.
Цитата(sonycman @ Jan 31 2007, 23:40)

The Memory Controller has a simple, hard-wired priority bus arbiter that gives the control of the
bus to one of the two masters. The Peripheral DMA Controller has the highest priority; the ARM
processor has the lowest one.
[AT91SAM7S.pdf, 6175G–ATARM–22-Nov-06, page 120]
Действительно, курить в сторонке будет процессор, а не DMA...
Причем по другому для периферийного железа которое не имеет своих буферов и быть не может,
например, летит в 100 Mbit интерфейс 1500 байтовый пакет. Куда ему прикажете его девать?
Тут уж или терять, или процессор идет на перекур. Без вариантов.
Цитата(ASN @ Jan 31 2007, 22:05)

zltigo
Режим работы контроллера DMA определяется в первую очередь типом используемой накристальной шины.
Определяется, например, свежие LPC23xx имеют два банка памяти и соответственно две шины,
что позволяет достаточно независимо работать DMA (в своих 8K) и CPU каждому в своем банке большую часть времени. Обычные ARM7 такого не имеют.
defunct
Feb 2 2007, 01:46
Цитата(zltigo @ Feb 1 2007, 18:38)

Причем по другому для периферийного железа которое не имеет своих буферов и быть не может,
например, летит в 100 Mbit интерфейс 1500 байтовый пакет. Куда ему прикажете его девать?
Тут уж или терять, или процессор идет на перекур. Без вариантов.
Действительно.
My bad..
Это инициализация
Код
#define NCHANNELS 8
extern volatile unsigned portSHORT sADC_RAW[NCHANNELS*2];
#define TRGEN (0x0) // Hardware triggering
#define TRGSEL (0x0) // Use a Timer output signal (on rising edge) from TIOA0 (for this example)
#define LOWRES (0x0) // 8-bit result output
#define SLEEP (0x0) // Normal Mode
#define PRESCAL (0x0f) // Max value
#define STARTUP (0xc) // This time period must be higher than 20 ╣s and not 20 ms
#define SHTIM (0x2) // Must be higher than 3 ADC clock cycles but depends on output
// impedance of the analog driver to the ADC input
void InitADC(void) {
// Reset ADC
AT91F_ADC_SoftReset (AT91C_BASE_ADC);
// Open PDC for ADC
AT91F_PDC_Open (AT91C_BASE_PDC_ADC);
// Configure PDC for 2 buffers NCHANNELS bytes long each (Warning! Depends on LOWRES bit)
AT91F_PDC_ReceiveFrame ( AT91C_BASE_PDC_ADC ,
(char *) &sADC_RAW[0], NCHANNELS,
(char *) &sADC_RAW[NCHANNELS], NCHANNELS);
// Configure ADC
AT91F_ADC_CfgModeReg (AT91C_BASE_ADC, (SHTIM << 24) | (STARTUP << 16) | (PRESCAL << 8) |
(SLEEP << 5) | (LOWRES <<4) | (TRGSEL << 1) | (TRGEN ) );
// Enable all 8 channes
AT91F_ADC_EnableChannel(AT91C_BASE_ADC, (1<<NCHANNELS) - 1);
// Fire conversion
AT91F_ADC_StartConversion(AT91C_BASE_ADC);
}
А это собственно работа
Код
// Check what buffer to use from ADC
if (AT91C_BASE_PDC_ADC->PDC_RCR == 0)
{
ptrADC_RAW=NCHANNELS;
AT91F_PDC_ReceiveFrame ( AT91C_BASE_PDC_ADC ,
(char *) &sADC_RAW[0], NCHANNELS,
(char *) &sADC_RAW[NCHANNELS], NCHANNELS);
}
else
{
ptrADC_RAW=0;
}
// Re-fire conversion
AT91F_ADC_StartConversion(AT91C_BASE_ADC);
Тут возможно следовало бы использовать библиотечные функции, но я не запарился и читаю напрямую PDC_RCR.
ptrADC_RAW нужен для того, чтобы знать, в какой части буфера лежат свежие данные:
Код
// Convert 10-bit wide ADC results into 8-bit wide
// Clean target array
for(i=0;i<(NCHANNELS*10/8);i++) ucAxes[i]=0;
// Do bit manipulations
// The trick with +0x0200 & 0x03ff is - we need negative 10-bit wide number
for(i=0;i<(NCHANNELS*10);i++)
{
ucAxes[i/8] += (((((sADC_RAW[ptrADC_RAW+(i/10)] + 0x0200)&0x03ff)>> (i%10)) & 0x01) << (i%8));
}
Можно ли одновременно передавать массив данных в SPI через ДМА и получать данные из SPI через тот же ДМА? Что-то у меня не получается такой обмен... Передача идет, а прием - нет.
Цитата(Karl @ Feb 6 2007, 13:16)

Можно ли одновременно передавать массив данных в SPI через ДМА и получать данные из SPI через тот же ДМА? Что-то у меня не получается такой обмен... Передача идет, а прием - нет.
Ну вообще, PDA - штука двунаправленная, но конкретику надо смотреть в даташите и всяких примерах. Я не реализовывал двунаправленную передачу по SPI.
Цитата(Kitsok @ Feb 6 2007, 17:28)

Цитата(Karl @ Feb 6 2007, 13:16)

Можно ли одновременно передавать массив данных в SPI через ДМА и получать данные из SPI через тот же ДМА? Что-то у меня не получается такой обмен... Передача идет, а прием - нет.
Ну вообще, PDA - штука двунаправленная, но конкретику надо смотреть в даташите и всяких примерах. Я не реализовывал двунаправленную передачу по SPI.
Спасибо, вроде разобрался. Заработало.
Kitsok
Feb 12 2007, 19:01
Цитата(Karl @ Feb 7 2007, 08:44)

Спасибо, вроде разобрался. Заработало.
Добрый день!
А можете код показать? Удалось ли принимать данные с использованием PDC?
Цитата(Kitsok @ Feb 12 2007, 21:01)

Цитата(Karl @ Feb 7 2007, 08:44)

Спасибо, вроде разобрался. Заработало.
Добрый день!
А можете код показать? Удалось ли принимать данные с использованием PDC?
Да, передачу и прием по PDC организовать удалось. Проблемы, как оказалось, были не с PDC, а с некорректной работой с SPI.
Инициализация SPI:
Код
void SPI_ini(void)
{
AT91F_SPI_Reset(AT91C_BASE_SPI);
//delay_ms(5);
// Инициализация SPI. Запустим на работу с частотой примерно 2,5 МГц
// Конфигурация канала АЦП (2 канал)
AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC, 1 << AT91C_ID_SPI); // Тактирование на SPI
AT91F_SPI_CfgMode(AT91C_BASE_SPI,
1<<0| //Интерфейс работает в режиме ведущего.
0<<1| //Фиксированный выбор корпуса внешнего периферийного устройства.
0<<2| //PCSDEC: Декодирование выбора корпуса 0 = Периферийные устройства непосредственно подключены к выводам выбора корпуса.
0<<4| //MODFDIS: Определение ошибки режима работы 0 =Определение ошибки режима работы запрещено.
0<<7| // Зацикливание 0 - отключено
0xb<<16); // PCS: Выбор корпуса периферии Корпус 3
AT91F_SPI_CfgCs(AT91C_BASE_SPI,2,
AT91C_SPI_BITS_8| // 8 бит в пакете
400<<8| // Частота SPI в 20 наза меньше MCK (необходимо не более 2,5 МГц)
2<<16| // Задержка перед выдачей тактовой частоты
0<<24| // Задержка между последовательными передачами данных
1<<1);
AT91F_SPI_Enable(AT91C_BASE_SPI);
AT91F_PDC_Open(AT91C_BASE_PDC_SPI);
}
Собственно работа:
Код
for(;;)
{
while(AT91F_SPI_SendFrame(AT91C_BASE_SPI,(char*) &SPI_buf_TX[0],sizeof(SPI_buf_TX),0,0) == 0);
u08 a = AT91F_SPI_ReceiveFrame(AT91C_BASE_SPI,(char*) &SPI_buf_RX[0],sizeof(SPI_buf_TX),0,0);
// Ожидаем, пока приемный буфер заполнится. В дальнейшем включу на прерывание.
while(!( a = AT91C_BASE_SPI->SPI_SR & AT91C_SPI_ENDRX));
...
...
...
}
Kitsok
Feb 15 2007, 18:55
Спасибо большое!
Я так и думал, что в общем-то ничего сложного.
beer_warrior
Feb 15 2007, 19:59
Цитата
Собственно работа:
Код
for(;;)
{
while(AT91F_SPI_SendFrame(AT91C_BASE_SPI,(char*) &SPI_buf_TX[0],sizeof(SPI_buf_TX),0,0) == 0);
u08 a = AT91F_SPI_ReceiveFrame(AT91C_BASE_SPI,(char*) &SPI_buf_RX[0],sizeof(SPI_buf_TX),0,0);
// Ожидаем, пока приемный буфер заполнится. В дальнейшем включу на прерывание.
while(!( a = AT91C_BASE_SPI->SPI_SR & AT91C_SPI_ENDRX));
...
...
...
}
Кстати абсолютно не вижу кайфа в такой конструкции.
ДМА гонит данные, ядро ожидает завершения. Точно так же можно использовать и программные счетчик и указатель.
Чтобы действительно получить выигрыш, флажок завершения приема/передачи надо прицепить к прерыванию. В таком случае действительно ДМА будет жить своей жизнью, а ядро отвлекаться на подготовку/обработку данных один раз на фрейм, а в остальное время заниматься чем-то полезным.
Цитата(beer_warrior @ Feb 15 2007, 21:59)

Цитата
Собственно работа:
Код
for(;;)
{
while(AT91F_SPI_SendFrame(AT91C_BASE_SPI,(char*) &SPI_buf_TX[0],sizeof(SPI_buf_TX),0,0) == 0);
u08 a = AT91F_SPI_ReceiveFrame(AT91C_BASE_SPI,(char*) &SPI_buf_RX[0],sizeof(SPI_buf_TX),0,0);
// Ожидаем, пока приемный буфер заполнится. В дальнейшем включу на прерывание.
while(!( a = AT91C_BASE_SPI->SPI_SR & AT91C_SPI_ENDRX));
...
...
...
}
Кстати абсолютно не вижу кайфа в такой конструкции.
ДМА гонит данные, ядро ожидает завершения. Точно так же можно использовать и программные счетчик и указатель.
Чтобы действительно получить выигрыш, флажок завершения приема/передачи надо прицепить к прерыванию. В таком случае действительно ДМА будет жить своей жизнью, а ядро отвлекаться на подготовку/обработку данных один раз на фрейм, а в остальное время заниматься чем-то полезным.
А Вы прочитайте внимательнее комментарии. Я выложил кусок тестовой програмки. Так проще разобраться в коде тем, кто будет смотреть. Реально у меня все на прерываниях.
Kitsok
Feb 16 2007, 11:46
Цитата(beer_warrior @ Feb 15 2007, 19:59)

Кстати абсолютно не вижу кайфа в такой конструкции.
ДМА гонит данные, ядро ожидает завершения. Точно так же можно использовать и программные счетчик и указатель.
Чтобы действительно получить выигрыш, флажок завершения приема/передачи надо прицепить к прерыванию. В таком случае действительно ДМА будет жить своей жизнью, а ядро отвлекаться на подготовку/обработку данных один раз на фрейм, а в остальное время заниматься чем-то полезным.
Ну почему-же нету кайфа.
Я например под FreeRTOS клепаю, так у меня, пока данные не готовы, будет отдаваться выполнение другим задачам

Другой вопрос меня интересует, может доку не внимательно читал.
Вот допустим, я так сделал железо, что один и тот-же ChipSelect у меня работает и на передачу и на прием (разнос по сигналами MOSI/MISO).
Запускаю я одновременно и передачу и прием.
SPI может дупелксно и передавать и принимать?
HARMHARM
Feb 16 2007, 12:39
Цитата(Kitsok @ Feb 16 2007, 10:46)

Другой вопрос меня интересует, может доку не внимательно читал.
Вот допустим, я так сделал железо, что один и тот-же ChipSelect у меня работает и на передачу и на прием (разнос по сигналами MOSI/MISO).
Запускаю я одновременно и передачу и прием.
SPI может дупелксно и передавать и принимать?
The SPI is a full duplex serial interface, designed to be able to handle multiple masters and slaves connected to a given bus.
Это иэ доки на LPC2194.
Одновременно сдвигаются и регистр приема, и регистр передачи.
Цитата(Kitsok @ Feb 16 2007, 13:46)

Вот допустим, я так сделал железо, что один и тот-же ChipSelect у меня работает и на передачу и на прием (разнос по сигналами MOSI/MISO).
Запускаю я одновременно и передачу и прием.
SPI может дупелксно и передавать и принимать?
SPI работает в дуплексном режиме. По каждому тактовому сигналу один битик передается и один принимается.
Slonic
Feb 16 2007, 14:35
Подскажите, пожалуйста, можно ли использовать DMA в случае, когда мне необходимо принимать поток данных с 8-разрядной параллельной шины (быстрый АЦП)? Или это все работает только для внутренних переферийных устройств процессора?
Kitsok
Feb 20 2007, 01:03
Цитата(Karl @ Feb 16 2007, 12:53)

SPI работает в дуплексном режиме. По каждому тактовому сигналу один битик передается и один принимается.
А вот тут возникает интересный вопрос с ChipSelect - если в процессе передачи я меняю адрес устройства для приема, то что будет, старый PCS так и останется активным и новый PCS станет активным, или нет?
Цитата(Kitsok @ Feb 20 2007, 03:03)

Цитата(Karl @ Feb 16 2007, 12:53)

SPI работает в дуплексном режиме. По каждому тактовому сигналу один битик передается и один принимается.
А вот тут возникает интересный вопрос с ChipSelect - если в процессе передачи я меняю адрес устройства для приема, то что будет, старый PCS так и останется активным и новый PCS станет активным, или нет?
Изменяемый выбор периферийного устройства позволяет вести буферизованную передачу данных с различными периферийными устройствами без перепрограммирования содержимого регистра управления режимом работы. Информация записывается в передающий регистр данных словом в 32-битной ширины, и содержит передаваемые данные и номер периферийного устройства, которому они предназначены. При использовании контроллера ПДП периферии (PDC) в этом режиме необходимы 32 - битные буферы, с хранением данных в LSB и полями PCS и LASTXFER в MSB. Интерфейс может управлять количеством бит данных, передаваемых по линиям MISO и MOSI за один сеанс передачи (8 или 16) с помощью регистров управления выбором корпуса. Если исходить из размера выделяемой под буфера памяти, это не самый оптимальный способ, но он предоставляет очень эффективные возможности для обмена данных с несколькими периферийными устройствами без занятия процессора.
Kitsok
Feb 20 2007, 12:23
Всем привет!
Вчера вперся в непонятную проблему, решения (кроме совсем тупого) пока не нашел.
Итак, вывод данных у меня сделан с помощью 74HC595, ввод - 74НС165.
Висят на одном CS, точноо, к '165 CS вообще не подключен.
В настройках CPOL=0, NCPHA = 1.
Делаю пока без PDC, потому что не могу элементарно найти глюк.
Отсылаю байт, жду, принимаю байт.
Отсылка работает нормально, '595 принимает байт, на выводах все, что нужно. А вот при приеме затесывается лишняя единичка в начале (LSB). Т.е. у '165 все входы подтянуты к земле, однако принимаю я 0х01.
Меняю CPOL=1, начинает работать ввод, но теряется один бит на выводе.
Учитался даташитов, не могу понять, что не так.
Тупое решение проблемы - раздельный вывод и ввод, т.е. менять CSR между циклом вывода и ввода. Но это значет, что прощай дуплекс, сначала 40 мс ждем пока все данные улетят в вывод, потом столько-же, пока данные прилетят со ввода.
Что делать - не понятно...
Dron_Gus
Feb 20 2007, 17:21
А обьясните мне глупому по PDC у SAM7. Предположим хочу использовать его для чтения DataFlash. Но ведь чтоб пришел байт, нужно и послать байт. Значит для чтения нужно "гнать" какие-нить левые данныеи и "туда"? И абсолютно безразлично какие? Т.е. я могу настроить блок PDC на передачу на тот же буфер, что и приемник, например?
Сергей Борщ
Feb 20 2007, 18:01
Цитата(Dron_Gus @ Feb 20 2007, 16:21)

Значит для чтения нужно "гнать" какие-нить левые данныеи и "туда"? И абсолютно безразлично какие? Т.е. я могу настроить блок PDC на передачу на тот же буфер, что и приемник, например?
Да.
Цитата(Kitsok @ Feb 20 2007, 11:23)

Отсылка работает нормально, '595 принимает байт, на выводах все, что нужно. А вот при приеме затесывается лишняя единичка в начале (LSB). Т.е. у '165 все входы подтянуты к земле, однако принимаю я 0х01.
Увы, так и есть. 595 защелкивает по фронту тактового импульса, 165 выдает первый бит по этому же фронту, т.е. уже после чтения в SPI. Вот и получается сдвиг на бит. К сожалению, в SPI нет режима, в котором данные выставляются по фронту а читаются по срезу. Можно включить инвертор в цепь CLK одного из регистров, может и поможет - надо помедитировать над времянкой.
Для просмотра полной версии этой страницы, пожалуйста,
пройдите по ссылке.