GNU ARM汇编--(九)s3c2440的PWM
依旧从datasheet开始看起,锻炼下阅读英文技术手册的能力
PWM TIMER
概述
s3c2440有5个16bit的定时器.Timer0,1,2和3有PWM功能.Timer4是没有输出管脚的内部定时器.Timer0有一个dead-zone产生器,用于大电流设备.
Timer0和1共用一个8bit的预分频器,而timer2,3,4共用另外一个8bit的预分频器.每一个定时器都有一个时钟除法器,可以产生5个不同的除法信号(1/2,1/4,1/8,1/16和TCLK).每一个定时器从时钟除法器接收各自的时钟信号,除法器从各自的8bit预分频器接收时钟.8bit的预分频器是可编程控制的,将PCLK除成要加载的值,存在TCFG0和TCFG1中.
当定时器启用时定时器计数缓冲寄存器(TCNTBn)将初始值加载到down-counter中.定时器比较缓冲寄存器(TCMPBn)将初始值加载到比较寄存器中与donw-counter的值比较.当频率变化时TCNTBn和TCMPBn的双缓冲属性保证定时器产生稳定的输出.
每个定时器有自己的16bit down-counter,由定时器时钟驱动.当down-counter为0,定时器中断请求产生来通知CPU定时器操作以及完成了.当定时器的计数器为0,相关的TCNTBn的值会自动的加载到down-counter中来继续下一次操作.然而,当定时器停止,比如在定时器运行中清除TCONn的定时器使能位,TCNTBn不会重加载到计数器中.
TCMPBn的值被用于PWM.当down-counter的值与比较寄存器的值吻合时,定时器控制逻辑会改变输出电平.所以比较寄存器决定PWM输出的打开时间.
属性
5个16bit的定时器
两个8bit的预分频器&两个4bit的除法器
输出波形可编程控制
自动重加载模式或one-shot pulse mode
dead-zone产生器
PWM TIMER OPERATION
Prescaler&Divider
BASIC TIMER OPERATION
一个定时器(除了5)有TCNTBn,TCNTn,TCMPBn和TCMPn.当定时器为0时TCNTBn和TCMPBn加载到TCNTn和TCMPn中.当TCNTn为0,如果中断开启的话就会产生一个中断请求.
AUTO RELOAD&DOUBLE BUFFERING
PWM定时器有一个双缓冲功能,保证下次定时器操作时重加载的值改变时无需停止当前的定时器操作.所以新的定时器的值设定,当前的定时器操作也可以成功完成.
定时器的值可以写到TCNTBn中,定时器的当前计数值可以从TCNTOn中读到.TCNTBn被读取的值,不表明计数器的当前状态,而是下一次定时器持续期间的重加载值.
当TCNTn为0,自动重加载操作会复制TCNTBn到TCNTn中.如果TCNTn为0,而自动重加载的使能位为0,那么TCNTn不会再操作了.
TIMER INITIALIZATION USING MANUAL UPDATE BIT AND INVERTER BIT
当down-counter为0,定时器的自动重加载操作就会动作.所以user要预先定义TCNTn的初始值.在这种情况下,初始值通过手动更新位进行加载.下面的步骤描述如何启动一个定时器:
1.向TCNTBn和TCMPBn中些初始值
2.设置定时器的手动更新位.推荐配置inverter on/off bit(不管用不用)
3.设置定时器的开始位来启动定时器(同时清除手动更新位)
如果定时器被强行停止,TCNTn保持计数器的值而且不会从TCNTBm中重加载.如果要设置一个新值,要执行手动更新.
注意:不论何时TOUT inverter on/off bit被更改,在定时器运行时TOUTn的逻辑值都会改变.因此,最好在配置手动更新位的时候配置inverter on/off bit.
TIMER OPERATION
上面的图是下面操作的结果:
1.使能自动重加载功能.设置TCNTBn为160(50+110),TCMPBn为110.设置手动更新位并配置反转位(on/off).手动更新位将TCNTn和TCMPn的值更新到TCNTBn和TCMPBn中. 接下来设置TCNTBn为80(40+40),TCMPBn为40,它们决定下一次重加载的值.
2.设置开始位,手动更新位设为0.关闭反转器,打开自动重加载.在等待时间后定时器开始计数
3.当TCNTn和TCMPn有相同的值时,TOUTn的逻辑电平由低变为高
4.当TCNTn为0时,中断请求产生,TCNTBn的值被加载到一个临时寄存器.在下次定时器周期,TCNTn会重载临时寄存器的值.
5.在中断服务例程中,TCNTBn设为80(20+60),TCMPBn设置为60,同样用于下一次周期.
6.当TCNTn和TCMPn有相同的值时,TOUTn的逻辑电平由低变为高
7.当TCNTn为0,TCNTn自动加载TCNTBn的值,触发中断请求.
8.在中断服务例程中自动重加载和中断请求被禁用,停止定时器
9.当TCNTn与TCMPn有相同值,TOUTn的逻辑电平由低变为高
10.即使TCNT0为0,因为自动重加载被禁用了所以TCNTn不会再重加载,定时器停止了
11.没有中断请求产生了
PULSE WIDTH MODULATION(PWM)
通过使用TCMPBn来实现PWM功能.PWM的频率由TCNTBn来决定.
减少TCMPBn的值可以有更高的PWM值.增加TCMPBn的值可以有更低的PWM值.如果输出反转使能了,增加和减少操作也要反转.
双缓冲功能允许在ISR中将下一次PWM的TCMPBn的值在当前PWM的周期的任何一个时间点被写入.
OUTPUT LEVEL CONTROL
假定反转功能时关闭的,下面的步骤描述如何保证TOUT是高还是低:
1.关闭自动重加载位.TOUTn时高电平,当TCNTn为0时定时器停止.
2.通过清除定时器的开始位来停止定时器.如果TCNTn<=TCMPn,输出高;如果TCNTn>TCMPn,输出低.
3.可以通过TCON的反转开关来决定TOUT是否反转.反转器会移除额外的电流来适应输出电平.
PWM的各个寄存器描述
(注:因为TQ2440的板子是用TOUT0来控制蜂鸣器的,所以有些寄存器就不关注了)
定时器配置寄存器0(TCFG0)
定时器输入时钟周期 = PCLK/(prescaler + 1)/(divider value)
{prescaler} = 0~255
{divider value} = 2,4,8,16
Register Address R/W Description
TCFG0 0x51000000 R/W 配置两个8的预分频器
TCFG0 Bit Description
Dead zone length [23:16] 死区的长度,长度等于timer0的单位长度
Prescaler1 [15:8] 这个是给Timer2 3 4用的
Prescaler0 [7:0] 这个是给Timer0 1用的
定时器配置寄存器1(TCFG1)
Register Address R/W Description
TCFG1 0x51000004 R/W 5路MUX和DMA模式选择寄存器
TCFG1 Bit Description
DMA mode [23:20] 选择DMA请求通道
0000=No select 0001=Timer0 0010=Timer1
0011=Timer2 0100=Timer3 0101=Timer4
MUX4 [19:16] 为PWM Timer4选择复用输入
0000=1/2 0001=1/4 0000=1/8
0011=1/16 01xx=External TCLK1
MUX3 [15:12] 为PWM Timer3选择复用输入
0000=1/2 0001=1/4 0000=1/8
0011=1/16 01xx=External TCLK1
MUX2 [11:8] 为PWM Timer2选择复用输入
0000=1/2 0001=1/4 0000=1/8
0011=1/16 01xx=External TCLK1
MUX1 [7:4] 为PWM Timer1选择复用输入
0000=1/2 0001=1/4 0010=1/8
0011=1/16 01xx=External TCLK1
MUX0 [3:0] 为PWM Timer0选择复用输入
0000=1/2 0001=1/4 0010=1/8
0011=1/16 01xx=External TCLK1
- <pre name="code" class="cpp">/*
- copyleft@ dndxhej@gmail.com
- */
- .equ NOINT, 0xc0
- .equ GPBCON, 0x56000010 @led
- .equ GPBDAT, 0x56000014 @led
- .equ GPBUP, 0x56000018 @led
- .equ GPFCON, 0x56000050 @interrupt config
- .equ EINTMASK, 0x560000a4
- .equ EXTINT0, 0x56000088
- .equ EXTINT1, 0x5600008c
- .equ EXTINT2, 0x56000090
- .equ INTMSK, 0x4A000008
- .equ EINTPEND, 0x560000a8
- .equ SUBSRCPND, 0x4a000018
- .equ INTSUBMSK, 0x4a00001c
- .equ SRCPND, 0X4A000000
- .equ INTPND, 0X4A000010
- .equ GPHCON, 0x56000070
- .equ GPHDAT, 0x56000074
- .equ GPB5_out, (1<<(5*2))
- .equ GPB6_out, (1<<(6*2))
- .equ GPB7_out, (1<<(7*2))
- .equ GPB8_out, (1<<(8*2))
- .equ GPBVALUE, (GPB5_out | GPB6_out | GPB7_out | GPB8_out)
- .equ LOCKTIME, 0x4c000000
- .equ MPLLCON, 0x4c000004
- .equ UPLLCON, 0x4c000008
- .equ M_MDIV, 92
- .equ M_PDIV, 1
- .equ M_SDIV, 1
- .equ U_MDIV, 56
- .equ U_PDIV, 2
- .equ U_SDIV, 2
- .equ CLKDIVN, 0x4c000014
- .equ DIVN_UPLL, 0
- .equ HDIVN, 1
- .equ PDIVN, 1 @FCLK : HCLK : PCLK = 1:2:4
- .equ WTCON, 0x53000000
- .equ Pre_scaler, 249
- .equ wd_timer, 1
- .equ clock_select, 00 @316
- .equ int_gen, 1 @开中断
- .equ reset_enable, 0 @关掉重启信号
- .equ WTDAT,0x53000004
- .equ Count_reload,50000 @定时器定为2S PCLK = 100M PCLK/(Pre_scaler+1)/clock_select = 100M/(249+1)/16=25k 50000/25k=2s
- .equ WTCNT,0x53000008
- .equ Count,50000
- .equ TCFG0,0x51000000
- .equ Prescaler1,0x00 @[15:8]Timer234
- .equ Prescaler0,249 @[7:0]Timer01
- .equ TCFG1,0x51000004
- .equ DMA_MODE,0x0 @[23:20]no dma channal
- .equ MUX0,0x2 @[3:0] 1/8
- @定时器输入时钟周期 = PCLK/(prescaler + 1)/(divider value)
- @clk = 100M/(249+1)/8=25k
- .equ TCON,0x51000008
- .equ DZ_eable,0 @[4]关闭死区的操作
- .equ auto_reload,1 @[3]auto_reload
- .equ inverter,1 @[2]打开电平反转
- .equ man_update,1 @[1]手动更新
- .equ clear_man_update,0
- .equ start,1 @[0]开始
- .equ stop,0 @[0]停止
- .equ TCNTB0,0x5100000c
- .equ TCMPB0,0x51000010
- .equ TCNTO0,0x51000014
- .equ ULCON0, 0x50000000
- .equ IR_MODE, 0x0 @[6]正常模式
- .equ Parity_Mode, 0x0 @[5:3]无校验位
- .equ Num_of_stop_bit, 0x0 @[2]一个停止位
- .equ Word_length, 0b11 @[1:0]8个数据位
- .equ UCON0, 0x50000004
- .equ FCLK_Div, 0 @[15:12] 时钟源选择用PCLK,所以这里用默认值
- .equ Clk_select, 0b00 @[11:10] 时钟源选择使用PCLK
- .equ Tx_Int_Type, 1 @[9] 中断请求类型为Level
- .equ Rx_Int_Type, 0 @1 @[8] 中断请求类型为Level
- .equ Rx_Timeout, 0 @[7]
- .equ Rx_Error_Stat_Int, 1 @[6]
- .equ Loopback_Mode, 0 @[5] 正常模式
- .equ Break_Sig, 0 @[4] 不发送终止信号
- .equ Tx_Mode, 0b01 @[3:2] 中断请求或轮循模式
- .equ Rx_Mode, 0b01 @[1:0] 中断请求或轮循模式
- .equ UFCON0, 0x50000008
- .equ Tx_FIFO_Trig_Level, 0b00 @[7:6]
- .equ Rx_FIFO_Trig_Level, 0b00 @[5:4]
- .equ Tx_FIFO_Reset, 0b0 @[2]
- .equ Rx_FIFO_Reset, 0b0 @[1]
- .equ FIFO_Enable, 0b0 @[0] 非FIFO模式
- .equ UMCON0, 0x5000000C @这个寄存器可以不管的
- .equ UTRSTAT0, 0x50000010
- .equ UERSTAT0, 0x50000014
- .equ UFSTAT0, 0x50000018
- .equ UMSTAT0, 0x5000001C
- .equ UTXH0, 0x50000020 @(L 小端)
- .equ URXH0, 0x50000024 @(L 小端)
- .equ UBRDIV0, 0x50000028
- .equ UBRDIV, 0x35 @PCLK=400M/4=100M UBRDIV = (int)(100M/115200/16) - 1 = 53 = 0x35
- .global Buzzer_Freq_Set
- .global _start
- _start: b reset
- ldr pc, _undefined_instruction
- ldr pc, _software_interrupt
- ldr pc, _prefetch_abort
- ldr pc, _data_abort
- ldr pc, _not_used
- @b irq
- ldr pc, _irq
- ldr pc, _fiq
- _undefined_instruction: .word undefined_instruction
- _software_interrupt: .word software_interrupt
- _prefetch_abort: .word prefetch_abort
- _data_abort: .word data_abort
- _not_used: .word not_used
- _irq: .word irq
- _fiq: .word fiq
- .balignl 16,0xdeadbeef
- reset:
- ldr r3, =WTCON
- mov r4, #0x0
- str r4, [r3] @ disable watchdog
- ldr r0, =GPBCON
- ldr r1, =0x15400 @这个时候暂不配置GPB0为TOUT0,这时候只是配置GPB0为TOUT0
- str r1, [r0]
- ldr r2, =GPBDAT
- ldr r1, =0x160
- str r1, [r2]
- bl clock_setup
- bl uart_init
- //bl delay
- msr cpsr_c, #0xd2 @进入中断模式
- ldr sp, =3072 @中断模式的栈指针定义
- msr cpsr_c, #0xd3 @进入系统模式
- ldr sp, =4096 @设置系统模式的栈指针
- @--------------------------------------------
- ldr r0, =GPBUP
- ldr r1, =0x03f0
- str r1, [r0]
- ldr r0, =GPFCON
- ldr r1, =0x2ea@0x2
- str r1, [r0]
- ldr r0, =EXTINT0
- @ldr r1, =0x8f888@0x0@0x8f888 @~(7|(7<<4)|(7<<8)|(7<<16)) //低电平触发中断
- ldr r1, =0xafaaa@0x0@0x8f888 //下降沿触发中断
- str r1, [r0]
- ldr r0, =EINTPEND
- ldr r1, =0xf0@0b10000
- str r1, [r0]
- ldr r0, =EINTMASK
- ldr r1, =0x00@0b00000
- str r1, [r0]
- ldr r0, =SRCPND
- ldr r1, =0x3ff@0x1@0b11111
- str r1, [r0]
- ldr r0, =SUBSRCPND
- ldr r1, =0x1<<13
- str r1, [r0]
- ldr r0, =INTPND
- ldr r1, =0x3ff@0x1@0b11111
- str r1, [r0]
- ldr r0, =INTSUBMSK
- ldr r1, =0x0<<13
- str r1, [r0]
- ldr r0, =INTMSK
- ldr r1, =0xfffff000@0b00000
- str r1, [r0]
- MRS r1, cpsr
- BIC r1, r1, #0x80
- MSR cpsr_c, r1
- bl main
- irq:
- sub lr,lr,#4
- stmfd sp!,{r0-r12,lr}
- bl irq_isr
- ldmfd sp!,{r0-r12,pc}^
- irq_isr:
- ldr r2, =GPBDAT
- ldr r1, =0x0e0
- str r1, [r2]
- ldr r0,=EINTPEND
- ldr r1,=0xf0
- str r1,[r0]
- ldr r0, =SRCPND
- ldr r1, =0x3ff@0b11111
- str r1, [r0]
- ldr r0, =SUBSRCPND
- ldr r1, =0x3ff@0x1<<13
- str r1, [r0]
- ldr r0, =INTPND
- ldr r1, =0x3ff@0b11111
- str r1, [r0]
- mov pc,lr
- delay:
- ldr r3,=0xffffff
- delay1:
- sub r3,r3,#1
- cmp r3,#0x0
- bne delay1
- mov pc,lr
- clock_setup:
- ldr r0,=LOCKTIME
- ldr r1,=0xffffffff
- str r1, [r0]
- ldr r0,=CLKDIVN
- ldr r1,=(DIVN_UPLL<<3) | (HDIVN<<1) | (PDIVN<<0)
- str r1, [r0]
- ldr r0,=UPLLCON
- ldr r1,=(U_MDIV<<12) | (U_PDIV<<4) | (U_SDIV<<0) @Fin=12M UPLL=48M
- str r1, [r0]
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- ldr r0,=MPLLCON
- ldr r1,=(M_MDIV<<12) | (M_PDIV<<4) | (M_SDIV<<0) @Fin=12M FCLK=400M
- str r1, [r0]
- mov pc,lr
- uart_init:
- ldr r0,=GPHCON
- ldr r1,=0x2aaaa @配置GPIO复用规则为串口
- str r1, [r0]
- ldr r0,=ULCON0
- ldr r1,=(IR_MODE<<6) | (Parity_Mode<<3) | (Num_of_stop_bit<<2) | (Word_length<<0) @
- str r1, [r0]
- ldr r0,=UCON0
- ldr r1,=(FCLK_Div<<12) | (Clk_select<<10) | (Tx_Int_Type<<9) | (Rx_Int_Type<<8) | (Rx_Timeout<<7) | (Rx_Error_Stat_Int<<6) |(Loopback_Mode<<5) | (Break_Sig<<4) | (Tx_Mode<<2) | (Rx_Mode<<0)
- str r1, [r0]
- ldr r0,=UFCON0
- ldr r1,=(Tx_FIFO_Trig_Level<<6) | (Rx_FIFO_Trig_Level<<4) | (Tx_FIFO_Reset<<2) | (Rx_FIFO_Reset<<1) | (FIFO_Enable<<0) @
- str r1, [r0]
- ldr r0,=UBRDIV0
- ldr r1,=(UBRDIV<<0)
- str r1, [r0]
- mov pc,lr
- Buzzer_Freq_Set:
- //ldr r0, =GPBCON
- //ldr r1, =0x15400 @这个时候暂不配置GPB0为TOUT0,这时候只是配置GPB0为TOUT0
- //str r1, [r0]
- ldr r2, =GPBDAT
- ldr r1, =0x1c1
- str r1, [r2]
- ldr r2, =GPBCON
- ldr r1,[r2]
- ldr r1,[r1]
- //ldr r1, =0x15400
- bic r1,r1,#0x3
- orr r1,r1,#0x2
- str r1,[r2]
- ldr r2, =GPBDAT
- ldr r1, =0x1a0
- str r1, [r2]
- ldr r1,=TCFG0
- ldr r2,=(Prescaler0<<0)
- str r2, [r1]
- ldr r1,=TCFG1
- ldr r2,=(DMA_MODE<<20) | (MUX0<<0)
- str r2, [r1]
- // ldr r3,[r0]
- // str r3,[r2]
- //mov r2, r0
- ldr r1,=TCNTB0
- //ldr r2,=200
- str r0, [r1]
- mov r0,r0,LSR #2
- ldr r1,=TCMPB0
- //ldr r2,=50
- str r0, [r1]
- ldr r1,=TCON
- ldr r2,=(DZ_eable<<4) | (auto_reload<<3) | (inverter<<2) | (man_update<<1) | (start<<0)
- str r2, [r1]
- ldr r1,=TCON
- ldr r2,=(DZ_eable<<4) | (auto_reload<<3) | (inverter<<2) | (clear_man_update<<1) | (start<<0)
- str r2, [r1]
- ldr r2, =GPBDAT
- ldr r1, =0x1a0
- str r1, [r2]
- mov pc,lr
- main:
- ldr r1,=TCON
- ldr r2,=(DZ_eable<<4) | (auto_reload<<3) | (inverter<<2) | (man_update<<1) | (stop<<0)
- str r2, [r1]
- ldr lr, =loop
- ldr pc, _pwm_uart_test
- _pwm_uart_test: .word pwm_uart_test
- loop:
- b loop @ 死循环
- undefined_instruction:
- nop
- software_interrupt:
- nop
- prefetch_abort:
- nop
- data_abort:
- nop
- not_used:
- nop
- fiq:
- nop</pre><br>
- <pre></pre>
- <pre></pre>
- #include "pwm_uart_test.h"
- extern void Buzzer_Freq_Set(int freq);
- //extern void Buzzer_Freq_Set( void );
- char uart_GetByte(void)
- {
- while(!(rUTRSTAT0 & 0x1)); //Wait until THR is empty.
- return RdURXH0();
- }
- void uart_GetString(char *pt)
- {
- while(*pt)
- uart_GetByte();
- }
- void uart_SendByte(int data)
- {
- if(data=='\n')
- {
- while(!(rUTRSTAT0 & 0x2));
- WrUTXH0('\r');
- }
- while(!(rUTRSTAT0 & 0x2)); //Wait until THR is empty.
- WrUTXH0(data);
- }
- //====================================================================
- void uart_SendString(char *pt)
- {
- while(*pt)
- uart_SendByte(*pt++);
- }
- void uart_test(void)
- {
- char str[20] = "\nhello world\n";
- int a = 97;
- //while(1)
- // uart_SendByte(a);
- uart_SendString(str);
- char s = uart_GetByte();
- //if(s == 'a')
- if(s == 97)
- rGPBDAT = 0x1c0;
- //uart_SendByte(a);
- //uart_SendByte(97);
- //uart_SendByte('a');
- uart_SendByte((int)s);
- uart_SendByte((int)'s');
- }
- void pwm_uart_test(void)
- {
- int freq = 10;
- Buzzer_Freq_Set( freq ) ;
- //Buzzer_Freq_Set( ) ;
- uart_SendString("start\n");
- /*
- int i;
- for(i=0;i<1000;i++)
- uart_SendString("wait\n");
- while( 1 )
- {
- char key = uart_GetByte();
- uart_SendByte(key);
- if( key == 'a' || key == 'A' )
- {
- if( freq < 2000 ) //lci 20000
- freq += 10 ;
- uart_SendByte('a');
- Buzzer_Freq_Set( freq ) ;
- }
- if( key == 'b' || key == 'B' )
- {
- if( freq > 11 )
- freq -= 10 ;
- uart_SendByte('b');
- Buzzer_Freq_Set( freq ) ;
- }
- //uart_SendString( "\tFreq = %d\n", freq ) ;
- //if( key == ESC_KEY )
- //{
- // Buzzer_Stop() ;
- // return ;
- //}
- }
- */
- }
@clk = 100M/(249+1)/8=25k