为了给老婆换Visa,准备21号去Canada。因为正值租房到期,头一天搬家搬到晚上十一点半。结果本来说好的打包什么都没做就一头躺倒睡了。
第二天一早,一边看着屋子里的杂物因为家具都搬走了而乱七八糟的摆在地上,一边收拾行装准备出发去Ottawa。本来因为上午睡到十点半,开始的就晚,临行前又突然起意,觉得有一个旅行箱更好(家里的两个箱子太大,而且已经装了东西运到新家那里去了),于是又去沃尔玛买箱子。最后全部打点好出发已经是下午六点半了。
开车到Ogdensburg时已经将近七点。按着提前下好离线地图的Google Map指引,我们没有进市内,而是沿着市外的高速公路向东北开了大概半mile,来到美加边境的桥边。路口有一个大牌子指示“To Canada”。我沿着指示牌拐下了高速路,远远看到前面有一个关口,连忙问老婆护照在不在手边。老婆说都放在后备箱里面的背包里。于是赶紧靠边停车,下车把护照和一干文档取到手里。
结果等我们又上了路,接近一看那不是关口,而是个收费站。每车过桥USD 2.75。过了收费站就直接上边境大桥了。大桥的桥面不是混凝土,全是钢筋铁条,轮胎走在上面嘎嘎作响,直让我担心会不会毁车。
早就听说加拿大的限速都是km/h标识,大桥上的限速就已经是mile/h和km/h并存了。限速只有25mile/h,于是慢慢挪过了桥。风景是很优美的,不过担心着第一次过关,不知道Border会问些什么问题,没有太多心情关心风景。
下了桥向前走,远远就看到了边境关卡。大概因为不是假期,排队的人很少。我前面只有一辆车。乖乖的按照指示停在等候线外,等前面车过去了以后,我来到窗口。负责的是一个胖胖的圆脸中年男子,皮肤有点黑,留着点小髭须。后来在问话的过程中,我才注意到他的名牌叫“Forget”。不知道是不是我看错了...
我递过护照,问候了一声。他接过去,首先问从哪里来。我傻傻地答:“United States”。他似乎有点不耐烦,“United States is a big country.” 我恍然大悟,“New York States”. 他还不满意,"Which city?" "Potsdam." "Did you live there?" "Yes."
随后开始盘问目的地,“Where do you plan to go?” "Ottawa" "What's your purpose?" "Shopping and traveling". 行前我就和老婆说,为了减少麻烦,索性不要说申请签证的事情,就说shopping和traveling好了。“How long will you stay?” "Two days." "Where will you stay?" "A hotel." "Which hotel?" 我赶紧拿出打印好的旅馆预订单递过去。他看了看,似乎满意了,开始问车上的东西,“Besides your closet, what do you carry in your car?” “Just some snacks.” "Is there anything you want to report in your car?" 我竟然正经开始想......他不耐烦了,接着开始问,“Alcohol or tobacoo?” "No" "Firearm?" "No." "Is there anything you will leave in Canada?" "No." 最后一个问题似乎是问plant和animal,我没听清,总之No。
他最后撕下一张黄纸递给我,说“Take this yellow slip and stop your car.....” ,然后指了指关口后面的一排带顶棚的停车场。后面半截我因为紧张竟然没听清,但是又怕碍事,于是赶紧驱车离开了关口,在那几个停车位前面慢慢开过去,看到顶棚上面有一些多少多少m的标识,以为不同的车有不同的停车位置。结果从第一号位一直滑到第十号,发现标识都是一样的。于是把车停在了第九号位上。这才注意到每个号位旁边都有一个不锈钢台子。老婆说,没明白就去问问呗。我正端详那张黄纸,一个移民官赶到我车旁边。
我正想向他打招呼问下一步。他打断我的说话先向我发令,"Turn off your car"。我有点懵,但是照做。随后“Give me your key”. "Get off the car". 然后转向我老婆,“You also get off the car”. 等我们都出来了,他才问“Why do you stop this far? We start to run after you.” 原来是要我们停车到这里接受检查的意思,前面好几个车位都是空的,但是我走到最远的九号位去停,他们以为我要跑掉闯关......我的天,如果被认为是闯关,估计麻烦就大了。
我赶紧解释"I am just looking at the yellow slip and go too far. I had thought all these places can be used to park." 总不能跟他说我太紧张结果没听明白前面关口的人说啥,解释起来也太烦了。估计是因为让他跑太远了,他火气有点大,“All these positions can be used for parking but you choose the farthest one.” 我只好不说话了。他态度变好了点,“Did you see the door with words 'Reception' on that building?” 我扶了扶眼镜,用力瞪大了眼睛,看到了远处墙上的字样,赶紧点头“Yes”。他说“Get into there”。随后头前走了。
我带着老婆进到那个building,是一个不大的大厅,有点像候诊室,有大概20多个人的位子。大理石台面后面放着八九台电脑。我正要走向唯一有人的那台电脑,刚才那个移民官从一扇门进到台子后面,他指示我们到最边上的一台电脑旁。随后要走了护照,开始盘问。
关于准备逗留多久的问题,他又盘问了一遍。我这次说了“Next Tuesday”,他反问“Next Tuesday?”我一想不对,今天是周末,赶紧改口,“This Tuesday”。他追问“Two days?”我自作聪明“Two nights and three days.” 结果突然意识到我们晚上七点钟才过关,如果住两个晚上,根本在Canada待不到“Three days”。 还好他没有追究....
再来就是Background Check。“Do you have any crime in US, Canada and China?” "Never"
又开始问身份,“What do you do in United States?” "I am a student. She's my wife." "What kind of visa did she hold?" "F2 Visa". 我忙把我们的I-20拿出来,列给他看。“This is the I20 Form for me and my wife”.
他看了一会,开始翻护照。这次又对我们的美国签证开始感兴趣,问“Your US Visa are expired. Why do both of you have your visa expired?”. 我有点懵,只好回答“That's because we apply for the visa together so that they expire together.”这好像是个废话,也不是他要的答案。于是他又问“How do you go back to US with a expired visa.” 我赶紧跟他解释“If we visit Canada, our visa will be automatically renewed when we go back to US.” 这个是我临行前看来的US Visa Automatic Revalidation。看他一副不信的样子,我忙又解释“You see, we cannot renew our visa when I was still in United States.” 他再问,“How long will you stay in United States?” 我忙指着I20上面说,“You can see here, it will take four years for me to get my degree. After that I will go back to China.” 他这次似乎终于满意了,说“You can have a seat”.
我忙拉着老婆到一边椅子上坐着,这时才有闲暇扫一下大厅里的装饰。这个大厅很像国内的火车站候车室,挑高很大,有大块的玻璃天窗。我们坐的地方对面有一个标本柜,里面钉着巨只的蝴蝶,还有一只像龟的东西,以及其他诡异的动物。
大概有五分钟,他又叫我们到柜台前,递还了护照和收走的车钥匙,道”Have a good trip“。我忙说”Thank you. You too.“ 话出口才意识到自己you too习惯了。人家又不trip....算了不管了。总之终于过关。
出了大厅,回到车边打火走人。在乱七八糟的小路上绕了一会,终于和Google Map的路线对上了号。开始驶向Ottawa。
Tech Writing
Sunday, July 21, 2013
Thursday, June 6, 2013
Arduino: Write a stepper motor library
Arduino has a Stepper library within the distribution, which can be used to drive most stepper motors. However, by writing a driving library by self, one can more clearly understand how stepper motor works.
I use a 28BYJ-48 Stepper motor, together with a ULN2003 chip. ULN2003 is a high-voltage transistor array, which can be used to convert 5v input from Arduino to 50v output, which is sufficient to drive the stepper motor.
Here's a datasheet for 28BYJ-48. It is very easy to drive the motor: just send the signals in sequence, will the motor rotate clockwise. If reversing the signal sequence, the motor will rotate counter-clockwise.
Here's a sample program that drives the motor continuously. It uses pin 8,9,10,11 to drive the four input of 28BYJ-48.
We will now discuss how to control the rotation speed and angle. From the datasheet it is known that the step angle for this motor is 5.625° / 64, which means it will take 64 steps for the motor to turn a round and each step is 5.625°.
By changing the interval after each steps, the rotation speed can be adjusted. However, for a stepper motor the rotation speed is not what we care most. We care more about the rotation angle, which can be adjusted by the step count. Thus we could do it by simply adding such a for loop to control the steps it goes. For this motor 64 steps make a round and half a round is 32 steps.
I use a 28BYJ-48 Stepper motor, together with a ULN2003 chip. ULN2003 is a high-voltage transistor array, which can be used to convert 5v input from Arduino to 50v output, which is sufficient to drive the stepper motor.
Here's a datasheet for 28BYJ-48. It is very easy to drive the motor: just send the signals in sequence, will the motor rotate clockwise. If reversing the signal sequence, the motor will rotate counter-clockwise.
Here's a sample program that drives the motor continuously. It uses pin 8,9,10,11 to drive the four input of 28BYJ-48.
int interval = 1; void setup() { pinMode(8, OUTPUT); pinMode(9, OUTPUT); pinMode(10, OUTPUT); pinMode(11, OUTPUT); } void loop() { // step 1 digitalWrite(8, LOW); digitalWrite(9, LOW); digitalWrite(10, LOW); digitalWrite(11, HIGH); delay(interval); // step 2 digitalWrite(8, LOW); digitalWrite(9, LOW); digitalWrite(10, HIGH); digitalWrite(11, HIGH); delay(interval); // step 3 digitalWrite(8, LOW); digitalWrite(9, LOW); digitalWrite(10, HIGH); digitalWrite(11, LOW); delay(interval); // step 4 digitalWrite(8, LOW); digitalWrite(9, HIGH); digitalWrite(10, HIGH); digitalWrite(11, LOW); delay(interval); // step 5 digitalWrite(8, LOW); digitalWrite(9, HIGH); digitalWrite(10, LOW); digitalWrite(11, LOW); delay(interval); // step 6 digitalWrite(8, HIGH); digitalWrite(9, HIGH); digitalWrite(10, LOW); digitalWrite(11, LOW); delay(interval); // step 7 digitalWrite(8, HIGH); digitalWrite(9, LOW); digitalWrite(10, LOW); digitalWrite(11, LOW); delay(interval); // step 8 digitalWrite(8, HIGH); digitalWrite(9, LOW); digitalWrite(10, LOW); digitalWrite(11, HIGH); delay(interval); } |
We will now discuss how to control the rotation speed and angle. From the datasheet it is known that the step angle for this motor is 5.625° / 64, which means it will take 64 steps for the motor to turn a round and each step is 5.625°.
By changing the interval after each steps, the rotation speed can be adjusted. However, for a stepper motor the rotation speed is not what we care most. We care more about the rotation angle, which can be adjusted by the step count. Thus we could do it by simply adding such a for loop to control the steps it goes. For this motor 64 steps make a round and half a round is 32 steps.
void loop() { for(int i = 0 ; i < steps ; i++) { step(); } }
Now it should be clear how a stepper motor works. Wrap them all together and you can write your own stepper motor library. Good luck!
Friday, May 17, 2013
Arduino: Get the Character LCD to work
Finally I managed to get my 20x4 Character LCD to work.
Someone in Arduino Forum mentioned that the most common problem in using LCD is connection related. This is proven true once again.
Just after getting the LCD I solder pin headers to the PCB and I thought the connection would no longer be a problem. However, after trying several hours using either Arduino Liquid Crystal Library or my hand-written code, I failed to make the LCD displaying anything. I started thinking it was the LCD's problem and trying to contact the seller from eBay to replace the module.
In the last try, when I was using multimeter to measure the voltage between GND and V0, surprisingly I found that when I put the probes on GND and V0, there's something displaying on the screen, although very dim.
It was very obvious the problem is the connectivity of V0. I had thought the soldering quality is pretty good, but seems it is not :-(. After re-solder that header , the "Hello World" finally displayed on the screen.
Now I will start writing my own LCD library. After all, the best way to understand how one thing works is to try it by yourself, right?
Someone in Arduino Forum mentioned that the most common problem in using LCD is connection related. This is proven true once again.
Just after getting the LCD I solder pin headers to the PCB and I thought the connection would no longer be a problem. However, after trying several hours using either Arduino Liquid Crystal Library or my hand-written code, I failed to make the LCD displaying anything. I started thinking it was the LCD's problem and trying to contact the seller from eBay to replace the module.
In the last try, when I was using multimeter to measure the voltage between GND and V0, surprisingly I found that when I put the probes on GND and V0, there's something displaying on the screen, although very dim.
It was very obvious the problem is the connectivity of V0. I had thought the soldering quality is pretty good, but seems it is not :-(. After re-solder that header , the "Hello World" finally displayed on the screen.
Now I will start writing my own LCD library. After all, the best way to understand how one thing works is to try it by yourself, right?
Monday, May 13, 2013
Arduino: Port Manipulation on Mega 2560
I am writing a library to output PAL/NTSC signals to a TV terminal, and I think it would be better to manipulate the port directly to gain faster access.
From http://www.arduino.cc/en/Reference/PortManipulation it was said PORTD can be used to control pin 0-7. However, sadly it does not work for me. digitalWrite and pinMode works well. But DDRD and PORTD has no response at all.
I start suspecting the document and trying other ports, and happily find PORTE works for pin 2-3. But not for the others. I think now it is time to test other ports and make a new mapping.
By testing, here's the ports corresponding to pin of Mega2560. It seems a little weird and I don't know why. If you have the same problem, I would suggest you to also try different PORT names on your board.
Hope this would help others that encounter the same problem.
From http://www.arduino.cc/en/Reference/PortManipulation it was said PORTD can be used to control pin 0-7. However, sadly it does not work for me. digitalWrite and pinMode works well. But DDRD and PORTD has no response at all.
I start suspecting the document and trying other ports, and happily find PORTE works for pin 2-3. But not for the others. I think now it is time to test other ports and make a new mapping.
By testing, here's the ports corresponding to pin of Mega2560. It seems a little weird and I don't know why. If you have the same problem, I would suggest you to also try different PORT names on your board.
Hope this would help others that encounter the same problem.
PIN number | PORT |
---|---|
2 | PORTE (0x10) |
3 | PORTE (0x20) |
4 | PORTG (0x20) |
5 | PORTE (0x08) |
6 | PORTH (0x08) |
7 | PORTH (0x10) |
8 | PORTH (0x20) |
9 | PORTH (0x40) |
10 | PORTB (0x10) |
11 | PORTB (0x20) |
12 | PORTB (0x40) |
13 | PORTB (0x80) |
22-29 | PORTA (0x01-0x80) |
30-37 | PORTC (0x80-0x01) |
38-45 | PORTD (0x80-0x01) |
46-53 | <NOT_FOUND> |
Arduino: Using ATMega’s Internal Timer/Counter
Internal timer, aka hardware timers are associated with specific hardwares. This article will focus on talking about using hardware timers in Arduino boards using ATMega series processor.
What can a timer/counter do?
One of the most often usage of timer/counter is to trigger interrupts and execute routines in a given interval.
If you have a periodic task to be done using Arduino, one of the way is to use delay() to control the time in the loop iteration. However, as Arduino is a single-threaded environment, it is thus impossible to execute other codes during the delay period. The CPU time is thus wasted during the delay. Moreover, if there are multiple tasks with different intervals, you’ll have to adjust the interval very carefully to make them working together.
Using interrupt triggered by timer can avoid this kind of problem. By setting up a interrupt period and feeding to Arduino a interrupt service routine(ISR), the timer will trigger the routine automatically in the given interval.
Another usage of timer/counter is to generate PWM(Pulse-Width Modulation) signals. This is a way of simulating a continuous voltage area by varying the width of fixed-height pulses. In this article I will NOT talk too much about PWM as this functionality has been provided by Arduino system library and it’s rarely necessary for user to manually create PWM by their own. However, it is good to know that these functions are supported by the processor hardware.
Hardware Architecture
This section and the next one will look in depth of the hardware architecture of the timer module and how it works. It will contains a lot of technical detail help understanding the timer. If you are not interested in them, you can safely skip them and go directly to the “Example of Timer” section and go back to these when necessary.
ATMega640/1280/2560 series processors has 6 timers/counter. The datasheet can be found at: http://www.atmel.com/Images/doc2549.pdf and section 17 talks about timers. The following description text are from corresponding section of the datasheet.
Each timer is also a counter, which uses a register to store the cycles it counts. ATMega has two types of timers: 8-bit and 16-bit. The first one can count up to 0xFF = 255 cycles. The latter one can count up to 0xFFFF = 65535 cycles.
Timer 0 and timer 2 are 8-bit counters. Timer 0 is used by Arduino library to implements millis() and micros() call. Timer 2 is used to implement tone() call.
Timer 1, 3, 4, 5 are 16-bit counters. Timer 1 is used by Arduino Servo library, which is used to control servomotor. If you don’t use that library, it is okay to use this timer. Timer 3, 4, 5 are safe to use as no official library use them. I will focus more on 16-bit counters.
NOTICE
In the following description, hardware register name may be involved. As registers for different timers only differs by their numbers(for example, the counter register for timer 1 is TCNT1, and that for timer 2 is TCNT2), we will use lower case “n” to represents timer number in register name(like TCNTn for counter register).
|
The timer is comprised of three parts: counter unit, compare unit and input capture unit.
Counter Unit
The counting value is stored in register TCNTn.
Depending on the mode of operation used, the counter is cleared, incremented, or decremented
at each timer clock (clkTn). The clkTn can be generated from an external or internal clock source, selected by the Clock Select bits (CSn2:0).
When no clock source is selected (CSn2:0 = 0) the timer is stopped. However, the TCNTn value can be accessed by the CPU, independent of whether clkTn is present or not. A CPU write overrides (has priority over) all counter clear or count operations.
The counting sequence is determined by the setting of the Waveform Generation mode bits (WGMn3:0) located in the Timer/Counter Control Registers A and B (TCCRnA and TCCRnB).
The Timer/Counter Overflow Flag (TOVn) is set according to the mode of operation selected by the WGMn3:0 bits. TOVn can be used for generating a CPU interrupt.
Compare Unit
During the counting, the 16-bit comparator continuously compares TCNTn with the Output Compare Register (OCRnx). If TCNT equals OCRnx the comparator signals a match. A match will set the Output Compare Flag (OCFnx) at the next timer clock cycle.
If interrupts is enabled (OCIEnx = 1), the Output Compare Flag generates an Output Compare interrupt. The OCFnx Flag is automatically cleared when the interrupt is executed.
Input Capture Unit
This unit is only provided in 16-bit counters. Thus timer 0 and timer 2 cannot use this function.
The Timer/Counter incorporates an input capture unit that can capture external events and give them a timestamp indicating time of occurrence. The external signal indicating an event, or multiple events, can be applied via the ICPn pin or alternatively, for the Timer/Counter1 only, via the Analog Comparator unit. The timestamps can then be used to calculate frequency, duty-cycle, and other features of the signal applied. Alternatively the time-stamps can be used for creating a log of the events.
When an event occurs on the Input Capture Pin (ICPn), a capture will be triggered, and the 16-bit value of the counter (TCNTn) is written to the Input Capture Register (ICRn). The Input Capture Flag (ICFn) is set at the same system clock as the TCNTn value is copied into ICRn Register.
If interrupt is enabled (TICIEn =1), the input capture flag generates an input capture interrupt. The ICFn flag is automatically cleared when the interrupt is executed. Alternatively the ICFn flag can be cleared by software by writing a logical one to its I/O bit location.
How to use the timer
We will start by viewing the possible working mode of the timers. The following table describing available modes, in which 0, 4, 12 are the only non-PWM mode and are what will be used by us.
The register TCCRnA and TCCRnB are used to select the mode. Here’re the definition of each bits of these 2 registers. We take TCCR1A/B as an example as TCCRnA/B all work in the same way.
Before talking about the modes, I want to first introduce the concept of prescaler, which will later be used frequently.
Prescaler
How long will it take for a 16-bit counter to generate an overflow, which is by default the longest time between signals?
Here we assume all the timers work in internal clock, which has a frequency of 16MHz. Thus each cycle will take 1/16 us and the time required by the counter to generate an overflow interrupt is 65536/16 = 4096 us, which is only 4 ms. This is too short for most of the periodic tasks.
Prescaler is designed to solve this problem. In prescaler mode, the counter will be operated only when a given number N, of hardware cycles passed. This means the frequency is decreased by N times.
For example, when the prescaler is set to be 2, it takes 2 cycles for the counter to increase by 1. Thus the timer frequency decreased to 8MHz and overflow interval will be 65536/8 = 8192 us.
The prescaler value is controlled by the CSn2:0 bits in TCCRnB. The following table describes how they works. From the table we know that the maximal prescaler value is 1024, which means it takes 64 us for the counter to increase 1, and it takes around 4.2 s for the counter to generate an overflow interrupt. This would be enough for most tasks. If you requires a longer delay, a software counter should be used in the program to control it.
We will start by looking into the first mode, namely normal mode, in which all the WGMn bits are zero.
Normal Mode
The simplest mode of operation is the Normal mode (WGMn3:0 = 0). In this mode the counting direction is always up (incrementing), and no counter clear is performed. The counter simply overruns when it passes its maximum 16-bit value (MAX = 0xFFFF) and then restarts from the BOTTOM (0x0000). In normal operation the Timer/Counter Overflow Flag (TOVn) will be set in the same timer clock cycle as the TCNTn becomes zero. The TOVn Flag in this case behaves like a 17th bit, except that it is only set, not cleared. However, combined with the timer overflow interrupt that automatically clears the TOVn Flag, the timer resolution can be increased by software. There are no special cases to consider in the Normal mode, a new counter value can be written anytime.
The Input Capture unit is easy to use in Normal mode. However, observe that the maximum interval between the external events must not exceed the resolution of the counter. If the interval between events are too long, the timer overflow interrupt or the prescaler must be used to extend the resolution for the capture unit. The Output Compare units can be used to generate interrupts at some given time. Using the Output Compare to generate waveforms in Normal mode is not recommended, since this will occupy too much of the CPU time.
CTC Mode
In Clear Timer on Compare or CTC mode (WGMn3:0 = 4 or 12), the OCRnA or ICRn Register are used to manipulate the counter resolution. In CTC mode the counter is cleared to zero when the counter value (TCNTn) matches either the OCRnA (WGMn3:0 = 4) or the ICRn (WGMn3:0 = 12). The OCRnA or ICRn define the top value for the counter, hence also its resolution. This mode allows greater control of the compare match output frequency. It also simplifies the operation of counting external events.
An interrupt can be generated at each time the counter value reaches the TOP value by either using the OCFnA or ICFn Flag according to the register used to define the TOP value. If the interrupt is enabled, the interrupt handler routine can be used for updating the TOP value. However, changing the TOP to a value close to BOTTOM when the counter is running with none or a low prescaler value must be done with care since the CTC mode does not have the double buffering feature. If the new value written to OCRnA or ICRn is lower than the current value of TCNTn, the counter will miss the compare match. The counter will then have to count to its maximum value (0xFFFF) and wrap around starting at 0x0000 before the compare match can occur. In many cases this feature is not desirable.
For generating a waveform output in CTC mode, the OCnA output can be set to toggle its logical
level on each compare match by setting the Compare Output mode bits to toggle mode
(COMnA1:0 = 1). The OCnA value will not be visible on the port pin unless the data direction for
the pin is set to output (DDR_OCnA = 1). The waveform generated will have a maximum frequency of fOCnA = fclk_I/O/2 when OCRnA is set to zero (0x0000).
The waveform frequency is defined by the following equation:
fOCnA=fclkIO/(2N(1+OCRnA))
The N variable represents the prescaler factor (1, 8, 64, 256, or 1024).
As for the Normal mode of operation, the TOVn Flag is set in the same timer clock cycle that the counter counts from MAX to 0x0000.
It should also be noticed that there are three channels A, B, C available for generating Compare Match Interrupt, but only OCRnA is used to clear the timer. Thus if OCRnB and OCRnC is set a value that is larger than OCRnA, there will be a high possibility that these interrupts not be triggered as the counter never reach their value.
Enabling Interrupts
Interrupts can be enabled or disabled by setting the corresponding bits of TIMSKn register. There are 5 types of interrupt available
ICIEn: Timer/Counter n, Input Capture Interrupt Enable
When this bit is written to one, the Timer/Counter n Input Capture interrupt is enabled. The corresponding Interrupt Vector TIMERn_CAPT_vect is executed when the ICFn Flag, located in TIFRn, is set.
OCIEnC: Timer/Counter n, Output Compare C Match Interrupt Enable
When this bit is written to one, the Timer/Counter n Output Compare C Match interrupt is enabled. The corresponding Interrupt Vector TIMERn_COMPC_vect is executed when the OCFnC Flag, located in TIFRn, is set.
OCIEnB: Timer/Counter n, Output Compare B Match Interrupt Enable
When this bit is written to one, the Timer/Counter n Output Compare B Match interrupt is enabled. The corresponding Interrupt Vector TIMERn_COMPB_vect is executed when the OCFnB Flag, located in TIFRn, is set.
OCIEnA: Timer/Counter n, Output Compare A Match Interrupt Enable
When this bit is written to one, the Timer/Counter n Output Compare A Match interrupt is enabled. The corresponding Interrupt Vector TIMERn_COMPA_vect is executed when the OCFnA Flag, located in TIFRn, is set.
TOIEn: Timer/Countern, Overflow Interrupt Enable
When this bit is written to one, the Timer/Countern Overflow interrupt is enabled. The corresponding Interrupt Vector TIMERn_OVF_vect is executed when the TOVn Flag, located in TIFRn, is set.
How to calculate required frequency
In this section I want to talk about some guideline about efficiently get a required frequency. This will apply only when you are using the internal clock of ATMega.
The easiest way to use the timer is to let it works in normal mode and make use of the Overflow Interrupt. As described above, there are 5 prescaler values: 1, 8, 64, 256, 1024. When working in normal mode, these values corresponding to the frequency of 16MHz, 2MHz, 250KHz, 62.5KHz and 15.625KHz. Thus the final output frequency is 244.1 Hz, 30.5 Hz, 3.8 Hz, 0.95Hz and 0.24Hz. Use the one that is most close to your request.
CTC mode provides more choice for frequency selection. By adjusting the OCRnx register, the frequency generated by CTC varies between the maximal and minimal values. CTC mode could be used generate the following frequencies of strobe signals.
Prescaler Value
|
Maximal Frequency
|
Minimal Frequency
|
1
|
16MHz
|
244Hz
|
8
|
2MHz
|
30.5Hz
|
64
|
250KHz
|
3.8Hz
|
256
|
62.5KHz
|
0.95Hz
|
1024
|
15.625KHz
|
0.24Hz
|
Example code
Example 1 : Using normal mode, Overflow Interrupt
This example use Timer 1 to toggle LED on pin 13 with an almost 4 seconds interval.
#define ledPin 13
void setup()
{
pinMode(ledPin, OUTPUT);
// initialize timer1
cli(); // disable all interrupts
TCCR1A = 0; // Clean the registers
TCCR1B = 0;
TCCR1B |= (1 << CS12) | (1 << CS10); // Prescaler 1024
TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt
sei(); // enable all interrupts
}
ISR(TIMER1_OVF_vect) // interrupt service routine
{
digitalWrite(ledPin, digitalRead(ledPin) ^ 1);
}
void loop()
{
}
|
Example 2 : Normal mode, use Output Compare Match Interrupt
In this example I use 2 Leds, one will be toggled when the counter is 32767, another will be toggled when the counter overflows. This is implemented using output compare match interrupt.
#define ledPin 13
#define ledPin2 31
void setup()
{
// initialize timer1
cli(); // disable all interrupts
TCCR3A = 0;
TCCR3B = 0;
OCR3A = 32767;
TCCR3B |= (1 << CS32); // Prescaler 256
TIMSK3 |= (1 << TOIE3); // enable compare A Match Interrupt
TIMSK3 |= (1 << OCIE3A); // enable compare B Match Interrupt
sei(); // enable all interrupts
pinMode(ledPin, OUTPUT);
pinMode(ledPin2, OUTPUT);
}
ISR(TIMER3_OVF_vect) // interrupt service routine
{
digitalWrite(ledPin, !digitalRead(ledPin));
}
ISR(TIMER3_COMPA_vect) // interrupt service routine
{
digitalWrite(ledPin2, digitalRead(ledPin2)^1);
}
void loop()
{
}
|
Example 3 : Using CTC mode, Output Compare Match Interrupt
This example demonstrate how to use OCR3A register to trigger an interrupt on Compare Match.
In the example, the prescaler was set to be 1024, which means an full overflow cycle will cost around 4s. The OCR3A is set to be 16383, which is 1/4 of the full cycle. It will take around 1s to trigger the compare and match interrupt.
Note here we set OCR3A to be 16383 rather than 16384. This is because the processor need a cycle to clear the TCNTn register and trigger the interrupt.
#define ledPin 13
void setup()
{
pinMode(ledPin, OUTPUT);
// initialize timer1
cli(); // disable all interrupts
TCCR3A = 0; // Clean the registers
TCCR3B = 0;
TCCR3B |= (1 << WGM12); // Mode 4, CTC
OCR3A = 16383; // 1/4 of counter
TCCR3B |= (1 << CS32) | (1 << CS30); // Prescaler 1024
TIMSK3 |= (1 << OCIE3A); // enable compare A Match Interrupt
sei(); // enable all interrupts
}
ISR(TIMER3_COMPA_vect) // interrupt service routine
{
digitalWrite(ledPin, digitalRead(ledPin) ^ 1);
}
void loop()
{
}
|
Complete Reference of all Timer Registers
Subscribe to:
Posts (Atom)