Exynos4412 베어 메탈 개발 제품군 자습서 - TICK 메커니즘
4491 단어 Exynos4412
Tick , , , , , , , , , , , 。
마찬가지로 누드기기의 경우 많은 임무를 수행할 능력이 없지만 tick 메커니즘을 실현하여 비교적 복잡한 누드기기 소프트웨어를 작성할 수 있고 구조가 참신하고 가독성이 강하며 확장이 간단하다.물론 만약에 우리가 누드 기기에서 다양한 임무를 수행해야 한다면 예를 들어 백엔드에서 http 페이지를 다운로드할 때 다운로드 진도를 동시에 표시하고 사용자의 모든 클릭 조작, 예를 들어 정지 버튼 등을 수신할 수 있다.우리는 협정을 통해 다중 임무를 실현할 수 있다. 협정의 본질은 프로그래머가 각종 임무의 교체를 제어하는 것이지 운영체제 자체가 일정한 알고리즘에 따라 스케줄링하는 것이 아니다.물론 전통적인 MCU 프로그래밍 방법, 백엔드 시스템으로도 가능하다.방법은 많지만 모두 이곡동공으로 큰길이 로마로 통한다.
Tick은 사실 매우 간단하다. 바로 주기적으로 실행되는 코드이다. cpu가 현재 어떤 작업을 수행하든지, 시간이 되면 이 코드는 반드시 실행되어야 한다.이 주기적인 코드에서 우리는 전역 변수를 하나 더 추가하여 흐르는 시간을 기록할 수 있다. 구체적인 응용 프로그램에서 우리는 이 변수에 근거하여 정확한 지연 시간을 실현할 수 있다.
여기까지 알고 나면 여러분은 어떻게 해야 하는지 알고 있습니다. 바로 주기적으로 인터럽트를 촉발하는 타이머를 신청한 다음에 인터럽트 처리 함수에 해당하는 코드를 추가하는 것입니다.
Exynos4412 누드기기 개발 시리즈 강좌에서 100HZ의 타이머를 사용했는데 최대 정밀도는 10ms이고 10ms보다 적은 시간 지연은 정확하지 않다. 물론 우리는 1000HZ까지 높일 수 있다. 이렇게 하면 정밀도는 1ms까지 갈 수 있다.
자, 구체적인 실현을 봅시다.먼저 타이머를 초기화하고 적절한 레지스터를 설정하고 인터럽트 처리 함수를 등록해야 합니다.
void exynos4412_tick_initial(void)
{
u64_t pclk;
if(!clk_get_rate("pclk", &pclk))
return;
if(!request_irq("TIMER4", timer_interrupt, 0))
return;
/* Using pwm timer 4, prescaler for timer 4 is 16 */
writel(EXYNOS4412_TCFG0, (readl(EXYNOS4412_TCFG0) & ~(0xff<<8)) | (0x0f<<8));
/* Select mux input for pwm timer4 is 1/2 */
writel(EXYNOS4412_TCFG1, (readl(EXYNOS4412_TCFG1) & ~(0xf<<16)) | (0x01<<16));
/* Load value for 10 ms timeout */
writel(EXYNOS4412_TCNTB4, (u32_t)(pclk / (2 * 16 * 100)));
/* Auto load, manaual update of timer 4 and stop timer4 */
writel(EXYNOS4412_TCON, (readl(EXYNOS4412_TCON) & ~(0x7<<20)) | (0x06<<20));
/* Enable timer4 interrupt and clear interrupt status bit */
writel(EXYNOS4412_TINT_CSTAT, (readl(EXYNOS4412_TINT_CSTAT) & ~(0x1<<4)) | (0x01<<4) | (0x01<<9));
/* Start timer4 */
writel(EXYNOS4412_TCON, (readl(EXYNOS4412_TCON) & ~(0x7<<20)) | (0x05<<20));
/* initial system tick */
tick_hz = 100;
jiffies = 0;
}
인터럽트 처리 함수에서 전역 변수 jiffies만 간단하게 하나를 추가하고 인터럽트 표시를 지웁니다.
static void timer_interrupt(void * data)
{
/* tick count */
jiffies++;
/* Clear interrupt status bit */
writel(EXYNOS4412_TINT_CSTAT, (readl(EXYNOS4412_TINT_CSTAT) & ~(0x1f<<5)) | (0x01<<9));
}
물론 여기에 보조 함수와 변수가 있습니다. 여기에 한 번 붙여주세요.volatile u32_t jiffies = 0;
static u32_t tick_hz = 0;
u32_t get_system_hz(void)
{
return tick_hz;
}
u64_t clock_gettime(void)
{
if(get_system_hz() > 0)
return (u64_t)jiffies * 1000000 / get_system_hz();
return 0;
}
이상의tick 메커니즘이 있으면 우리는 비교적 사용하는 기능을 실현할 수 있다. 예를 들어 시간 지연 함수는 구체적으로 다음과 같다.static volatile u32_t loops_per_jiffy = 0;
void __attribute__ ((noinline)) __delay(volatile u32_t loop)
{
for(; loop > 0; loop--);
}
void udelay(u32_t us)
{
u32_t hz = get_system_hz();
if(hz)
__delay(us * loops_per_jiffy / (1000000 / hz));
else
__delay(us);
}
void mdelay(u32_t ms)
{
u32_t hz = get_system_hz();
if(hz)
__delay(ms * loops_per_jiffy / (1000 / hz));
else
__delay(ms * 1000);
}
static void calibrate_delay(void)
{
u32_t ticks, loopbit;
s32_t lps_precision = 8;
u32_t hz = get_system_hz();
if(hz > 0)
{
loops_per_jiffy = (1<<12);
while((loops_per_jiffy <<= 1) != 0)
{
/* wait for "start of" clock tick */
ticks = jiffies;
while (ticks == jiffies);
/* go ... */
ticks = jiffies;
__delay(loops_per_jiffy);
ticks = jiffies - ticks;
if(ticks)
break;
}
loops_per_jiffy >>= 1;
loopbit = loops_per_jiffy;
while(lps_precision-- && (loopbit >>= 1))
{
loops_per_jiffy |= loopbit;
ticks = jiffies;
while(ticks == jiffies);
ticks = jiffies;
__delay(loops_per_jiffy);
/* longer than 1 tick */
if(jiffies != ticks)
loops_per_jiffy &= ~loopbit;
}
}
else
{
loops_per_jiffy = 0;
}
}
void exynos4412_tick_delay_initial(void)
{
calibrate_delay();
}
여기 관건적인 함수가calibratedelay, 이것은 loops를 계산하는 데 사용됩니다per_jiffy, 계산 방법은 차례로 접근하는 것이다. 이것은 linux 내부에서 bogomips를 계산하는 방법이다. 즉, 초당 몇 개의 지령을 운행하는지는 하나의 상대량이고 플랫폼과 환경을 비교해야 의미가 있다.이 변수를 계산한 후에 우리는 정확한 시간 지연을 실현할 수 있다. 예를 들어 mdelay와 udelay 함수는 모두가 스스로 실험을 해서 정확한지 확인할 수 있다.
네, 원본이 필요한 친구는 본 블로그에서 링크를 찾거나 QQ8192542를 직접 추가할 수 있습니다.