User Info
Добро пожаловать, Guest Пользователей: 5 Поисковых ботов: 3 Гостей: 1
Pavel Ivanchenko
Admin
Пункты: 93008
Регистрация: 24.03.2009
Пол: Мужчина
Цитата Доброго времени суток!
Вопрос следующего плана..
Написал программку на С под Linux, которая конфигурирует и запускает счетчик (ТС). ARM - AT91SAM9260.
Собственно проблема следующая после конфигурирования и запуска счетчика вычитываю значения счетчика (в идеале счетчик работает совершенно не зависимо от ОС то есть в разные промежутки времени значения счетчика должны быть разные), вычитанные значения равны нулю, то есть счетчик не работает...
Проверял на различных каналах счетчика (TC0, TC1, TC2), подозревал что один счетчик встроенный Linux использует...
Получил результат что счетчик вообще не работает...
Привожу код программы (доступ к памяти реализовал через "dev/mem", аналогично тому что приведен для "мигания светодиодами"):
Код
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
//-------------------------------------------------------------
#define T_CHANNEL0 0xfffa0000
#define T_CHANNEL1 0xfffa0040
#define T_CHANNEL2 0xfffa0080
#define TIMER_CONTROLL 0x00
#define TIMER_CONFIG 0x04
#define TIMER_REGA 0x14
#define TIMER_REGB 0x18
#define TIMER_REGC 0x1c
#define TIMER_INT_DIS 0x28
#define TIMER_VALUE 0x10
#define TIMER_STATE 0x20
//-------------------------------------------------------------
#define MAP_BASE (T_CHANNEL0)
#define MAP_SIZE 8192Ul
#define MAP_MASK (MAP_SIZE - 1)
#define IntDisableValue 0x000000ff
#define ConfigValue 0x0000800c //0x0006c000
#define StopValue 0x00000002
#define RunValue 0x00000001
#define RegAValue 0x00000002
#define RegCValue 0x0000FFF0 //00000014
int main(void)
{
int fd;
void *mapped_base;
int * config;
int * command;
int * intdis;
int * reg_A;
int * reg_B;
int * reg_C;
int * TCValue;
int * TCState;
if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1)
{
fprintf(stderr, "Cannot open /dev/mem.\n");
exit(EXIT_FAILURE);
}
fprintf(stderr, "/dev/mem opened.\n");
mapped_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, MAP_BASE & ~MAP_MASK);
if (mapped_base == (void *) -1)
{
fprintf(stderr, "Memory mapping error.\n");
exit(EXIT_FAILURE);
}
fprintf(stderr, "Memory block mapped at address %p.\n", mapped_base);
mapped_base+=(MAP_BASE & MAP_MASK);
fprintf(stderr, "Target address mapped 0x%08x-->0x%08x\n",(int) MAP_BASE,(int)mapped_base);
config = (int*)(mapped_base + TIMER_CONFIG);
command = (int*)(mapped_base + TIMER_CONTROLL);
reg_A = (int*)(mapped_base + TIMER_REGA);
reg_B = (int*)(mapped_base + TIMER_REGB);
reg_C = (int*)(mapped_base + TIMER_REGC);
intdis = (int*)(mapped_base + TIMER_INT_DIS);
TCValue = (int*)(mapped_base + TIMER_VALUE);
TCState = (int*)(mapped_base + TIMER_STATE);
*command = StopValue;
*intdis = IntDisableValue;
*config = ConfigValue;
*reg_A = RegAValue;
*reg_C = RegCValue;
*command = RunValue;
fprintf(stderr, "%08x\n", *config);
while (1) {
fprintf(stderr, "%08x State: %08x\n", *TCValue, *TCState);
usleep(1000000);
}
sleep(10);
*command = StopValue;
return 0;
}
Собственно счетчик конфигурирую в режиме Waveform.... с разными вариациями - результат нулевой...
Кто сталкивался с этой проблемой, прошу помочь...
Есть подозрение что работаю не с той областью памяти...хотя в Атмеловской спецификации указаны эти адреса...
Jury093
Пункты: 54271
Регистрация: 25.05.2009
Пол: Мужчина
Из: Санкт-Петербург
я бы сделал вывод содержимого регистров (те которые RW) до загрузки и после - чисто чтобы убедиться что запись проходит не в пустоту
и еще, глянув на семпл от Атмела, могу предположить, что не разрешен Peripheral Clock на соответствующий таймер
вот кусок инита от Атмела (прерывание можно пропустить)
Код //------------------------------------------------------------------------------
/// Configure Timer Counter 0 to generate an interrupt every 250ms.
//------------------------------------------------------------------------------
void ConfigureTc(void)
{
unsigned int div;
unsigned int tcclks;
// Enable peripheral clock
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_TC0;
// Configure TC for a 4Hz frequency and trigger on RC compare
TC_FindMckDivisor(4, BOARD_MCK, &div, &tcclks);
TC_Configure(AT91C_BASE_TC0, tcclks | AT91C_TC_CPCTRG);
AT91C_BASE_TC0->TC_RC = (BOARD_MCK / div) / 4; // timerFreq / desiredFreq
// Configure and enable interrupt on RC compare
AIC_ConfigureIT(AT91C_ID_TC0, AT91C_AIC_PRIOR_LOWEST, ISR_Tc0);
AT91C_BASE_TC0->TC_IER = AT91C_TC_CPCS;
AIC_EnableIT(AT91C_ID_TC0);
// Start the counter if LED is enabled.
if (pLedStates[1]) {
TC_Start(AT91C_BASE_TC0);
}
}
На любой вопрос есть любой ответ.
sasamy
Пункты: 83777
Регистрация: 14.08.2009
Цитата
fprintf(stderr, "Memory block mapped at address %p.\n", mapped_base);
>>>mapped_base+=(MAP_BASE & MAP_MASK);<<<
fprintf(stderr, "Target address mapped 0x%08x-->0x%08x\n",(int) MAP_BASE,(int)mapped_base);
Что это за непонятные манипуляции с указателем ? Куда он после этого указывает ?
Fireball
Пункты: 1075
Регистрация: 16.02.2010
2 sasamy :
Этот участок код был взят с Wiki с примера по работе с GPIO. Собственно необходимость этого смещения заключается в вызове функции mmap:
mapped_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, MAP_BASE & ~MAP_MASK );
То есть что бы выделить участок памяти блоком в 4096 байт нужно что чуток подкорректировать адрес при вызове mmap, а потом произвести обратное действие через ту команду которую Вы привели.
2 Jury093:
Спасибо, Ваш пост помог мне) Действительно надо сконфигурировать PMC для запуска триггера. Но не смотря на это счетчик все равно не запустился. Состояние счетчика уже отлично от 0, но значение счетчика равно 0 всегда.
Сделал как Вы порекомендовали вывод Config Register, Reg_A, Reg_C до и после записи в них значений. пишется не в пустоту. При этом при следующем старте программы значением "до записи" является то значение которое я установил в предыдущем пуске.
Есть сомнения по поводу запуска счетчика:
Для запуска триггера я пишу значение 0x00000002 по адресу 0xfffa0000
Если Вас не затруднит скиньте пожалуйста ссылку на пример от Atmel для работы с TC.
Jury093
Пункты: 54271
Регистрация: 25.05.2009
Пол: Мужчина
Из: Санкт-Петербург
Цитата сконфигурировать PMC для запуска триггера
полагаю, что все же
таймера ..
Цитата Есть сомнения по поводу запуска счетчика:
Для запуска триггера я пишу значение 0x00000002 по адресу 0xfffa0000
и небезосновательно:
Код #define TIMER_CONTROLL 0x00
command = (int*)(mapped_base + TIMER_CONTROLL);
#define RunValue 0x00000001
*command = RunValue;
или где-то в конфигурации наврано или неправильно смаплено (не по тем адресам)
возможно еще источник тактовой для таймера неправильно селектирован
попробуйте (для теста) почитать какой-нить заведомо описанный регистр
update ссылку на архив от Атмела я приводил вот тут:
линк
из
getting-started-project
На любой вопрос есть любой ответ.
sasamy
Пункты: 83777
Регистрация: 14.08.2009
(MAP_BASE & MAP_MASK)
Да - я невнимательно посмотрел, почему-то решил что это (MAP_BASE & ~MAP_MASK) и указатель в этом случае указывает не туда :)
MAP_BASE & ~MAP_MASK - это нужно чтобы выровнять адрес по границе страницы памяти - 4 кб.
Вот пример практически 1:1 с примеров атмел - это мой системный таймер в prex
http://sasamy.narod.ru/clock.c
Код
/*
* clock.c - Atmel at91 TC clock driver
*/
#include <kernel.h>
#include <timer.h>
#include <irq.h>
#include <cpufunc.h>
#include <sys/ipl.h>
#include "platform.h"
/* Peripheral Clock Enable Register */
#define PMC_PCER (*(volatile uint32_t *)(0xfffffc10)) /* Peripheral Clock Enable Register */
/* Peripheral id TC0 */
#define ID_TC 17
/* Timer Counter registers */
#define TC_CCR (*(volatile uint32_t *)(TC_BASE + 0x0000)) /* Channel Control Register */
#define TC_CMR (*(volatile uint32_t *)(TC_BASE + 0x0004)) /* Channel Mode Register */
#define TC_RC (*(volatile uint32_t *)(TC_BASE + 0x001c)) /* Register C */
#define TC_SR (*(volatile uint32_t *)(TC_BASE + 0x0020)) /* Status Register */
#define TC_IER (*(volatile uint32_t *)(TC_BASE + 0x0024)) /* Interrupt Enable Register */
#define TC_IDR (*(volatile uint32_t *)(TC_BASE + 0x0028)) /* Interrupt Disable Register */
#define CCR_CLKEN (0x1 << 0) /* Counter Clock Enable Command */
#define CCR_CLKDIS (0x1 << 1) /* Counter Clock Disable Command */
#define CCR_SWTRG (0x1 << 2) /* Software Trigger Command */
#define CMR_WAVE (0x1 << 15)
#define CMR_WAVESEL_UP_AUTO (0x2 << 13) /* UP mode with automatic trigger on RC Compare */
#define SR_CPCS (0x1 << 4) /* RC Compare */
/*
* Finds the best MCK divisor given the timer frequency and MCK
*/
unsigned char
find_divisor(unsigned int freq,
unsigned int *div,
unsigned int *tcclks)
{
const unsigned int divisors[5] = {2, 8, 32, 128, CONFIG_MCK / 32768};
unsigned int index = 0;
/* Satisfy lower bound */
while (freq < ((CONFIG_MCK / divisors[index]) / 65536)) {
index++;
/* If no divisor can be found, return 0 */
if (index == 5)
return 0;
}
/* Try to maximise DIV while satisfying upper bound */
while (index < 4) {
if (freq > (CONFIG_MCK / divisors[index + 1]))
break;
index++;
}
/* Store results */
if (div)
*div = divisors[index];
if (tcclks)
*tcclks = index;
return 1;
}
/*
* Clock interrupt service routine.
* No H/W reprogram is required.
*/
static int
clock_isr(void *arg)
{
volatile uint32_t piv;
/* Acknowledging the interrupt */
piv = TC_SR;
splhigh();
timer_handler();
spl0();
return INT_DONE;
}
/*
* Initialize clock H/W chip.
* Setup clock tick rate and install clock ISR.
*/
void
clock_init(void)
{
irq_t clock_irq;
unsigned int div, tcclks, sr;
/* Enable TC */
PMC_PCER = 1 << ID_TC;
find_divisor(CONFIG_HZ, &div, &tcclks);
/* Disable TC clock */
TC_CCR = CCR_CLKDIS;
/* Disable interrupts */
TC_IDR = 0xffffffff;
/* Clear status register */
sr = TC_SR;
/* Set mode */
TC_CMR = tcclks | CMR_WAVE | CMR_WAVESEL_UP_AUTO;
TC_RC = CONFIG_MCK / (CONFIG_HZ * div);
clock_irq = irq_attach(ID_TC, IPL_CLOCK, 1, &clock_isr, IST_NONE, NULL);
/* Enable timer interrupt */
TC_IER = SR_CPCS;
/* Start timer */
TC_CCR = CCR_CLKEN | CCR_SWTRG;
DPRINTF(("Clock rate: %d ticks/sec\n", CONFIG_HZ));
}
Если найду - где-то я использовал ТС как источник тактовой для аудиокодека - выложу, еще использовал его в качестве триггера для ацп, но то же где-то все утерялось из-за пропажи интереса...
sasamy
Пункты: 83777
Регистрация: 14.08.2009
Нашел драйвер аудиокодека, вот кусок настройки ТС - это из модуля ядра:
Код
/* Enable DAC master clock. */
tc0_clk = clk_get(NULL, "tc0_clk");
clk_enable(tc0_clk);
tc0_base = ioremap_nocache(AT91SAM9260_BASE_TC0, 256);
if (!tc0_base) {
clk_disable(tc0_clk);
printk(KERN_ERR "at91adc: Can't remap TC0 register area\n");
return -EACCES;
}
at91_set_A_periph(AT91_PIN_PA26, 0);
at91_tc_write(AT91_TC_CMR, AT91_TC_WAVE | AT91_TC_WAVESEL_UP_AUTO
| AT91_TC_TIMER_CLOCK1 | AT91_TC_ACPA_CLEAR | AT91_TC_ACPC_SET);
/* set rate */
at91_tc_write(AT91_TC_RC, 4);
/* duty 50 % */
at91_tc_write(AT91_TC_RA, 2);
at91_tc_write(AT91_TC_IDR, -1);
at91_tc_write(AT91_TC_CCR, AT91_TC_SWTRG | AT91_TC_CLKEN);
sasamy
Пункты: 83777
Регистрация: 14.08.2009
Нашел я свой драйвер для ацп
http://sasamy.narod.ru/my_adc.c
про сам драйвер не знаю но таймер там вроде работал - у меня к тому времени ацп уже был выбит статикой так что я так и не проверил.. хотел осцилоскоп замутить :)
Fireball
Пункты: 1075
Регистрация: 16.02.2010
2 Jury093:
Прошу прощения за мою невнимательность....
Цитата сконфигурировать PMC для запуска триггера
Прошу прощения... конечно же таймера=)
Цитата Для запуска триггера я пишу значение 0x00000002 по адресу 0xfffa0000
значение 0x00000001 для запуска, а для останова таймера ...02
Просто опечатка
Спасибо за ссылки на исходники Атмела.
2 sasamy:
Спасибо, Ваш исходник очень помог, завел я таймер....
Собственно для запуска триггера в по адресу 0xfffa0000 надо писать значение 5, то есть 2 бита SWTRG и CLKEN.
Изначально я думал что достаточно только последнего потому что CLKEN = "
Enables the clock if CLKDIS is not 1."
Потом посмотрел Ваш исходник и увидел что стартует счетчик с обоими битами...
SWTRG = "A software trigger is performed: the counter is reset and the clock is started."
Спасибо за помощь=)Очень признателен
Jury093 и
sasamy за терпение=))
Alfamayonez
Пункты: 3702
Регистрация: 04.10.2009
Пол: Мужчина
Цитата Нашел я свой драйвер для ацп
http://sasamy.narod.ru/my_adc.c
про сам драйвер не знаю но таймер там вроде работал - у меня к тому времени ацп уже был выбит статикой так что я так и не проверил.. хотел осцилоскоп замутить :)
Не могу понять, зачем АЦП таймер? У меня ацп без него работает :), вобще в чем смысл этого таймера?
Драйвер ацп делал по даташиту...