Still working in the world of Cortex M0 familiarization. My objective is still to gain enough understanding of the LPC1114 ecosystem to use the chip in one of my own boards. Today's focus: peripherals.
In the last installment, I learned a few things:
- The LPCXpresso has a pretty good set of sample programs, which I appreciate. They don’t, however, always play nice with each other.
- It’s not terribly difficult (at least for me) to knock the chip out of serial wire debug (SWD) programming mode, requiring an ISP reset to put the chip back in boot loader mode.
- The location of that ISP pin isn’t necessarily obvious (or I just missed it at first).
As I noted in that blog, the header files and includes may need a bit of modification before use in your own application. PWM (pulse width modulation) is a good example. It’s not a difficult problem, but I’ll cover what I ran across and maybe save someone a few minutes. In all of the examples, the peripheral header and include files are in the folder common\driver in the project folder. They look the same but they’re not necessarily so.
This is a part of the timer16.h included with the PWM example. As I noted last time, the init_timer16PWM and setMatch_timer16PWM (lines 59 and 60) functions are in the timer16.h and timer16.c files that come with the PWM example, but not in the timer16 files in any of the other examples.
After creating timer16pwm.h and timer16pwm.c in the last blog installment, PWM was mostly working, but I was still befuddled by some strange symptoms. Sometimes it would come out on the wrong pin and sometimes not at all. I’d set the PWM match value to MATCH0, and the MATCH1 pin would turn on. After some head scratching and single-stepping through the code, I found the cause of this behavior.
First, look at the MATCH defines on lines 48-51 in the image above. If MATCH0 were defined as 0, MATCH1 as 1, etc. it would be fine. But, they are defined as bit positions with shift operators. It’s defining them as 1, 2, 4 and 8. That would be OK except for an inconsistency.
The PWM function setMatch_timer16PWM (0, MATCH0, 1024); passes the timer number, the match number, and the 10-bit PWM value. In the header file, MATCH0 is defined as (1<<0). The switch/case statement used to take that parameter and select the desired match pin, as you can see in the image below, checks for values 0, 1, 2, and 3. I changed the case values to 1, 2, 4, and 8 and was on my way.
In that prior blog, I also noted that I knocked the chip out of programming mode and had to reset the boot loader a few times. Because of the mix up with the match pins, I had been inadvertently using MATCH1, which is also the clock for SWD. I suspect that I crashed the chip by using that pin for a PWM output while also using it for the SWD clock. I haven’t had the boot loader problem since straightening out the match mixup.
With PWM working, I walked through the A2D and I2C headers and includes with much quicker success. Now I’ve got my understanding of the IDE to a usable state, the majority of the peripherals I need to use working as well as in circuit programming. That makes me ready to start putting these chips into my PCBs.
A: The LPCXpresso 1114 as my SWD programmer
B: 3 volt to 5 volt logic level shifter
C: SRF08 ultrasonic range finders
D: PCF8574 I2C I/O expander
E: I2C headers
F: 3 volt power supply for the chips
G: The LPC1114FN28/102
H: 5 volt power supply for the range finders
I: Analog in adjustment
I had been intimidated by all of the startup code required in the ARM world. CMSIS takes care of pretty much all of it. I may find a few additional init things here and there as I dig deeper into the ARM world, but I’m impressed. I really don’t have any chip configuration code to speak of in my application source file.
The peripheral libraries take care of most of the rest. I read through the 13 pages of timer16 information in the LPC1114 user's guide, as well as the 25 pages on the ADC and 42 pages on I2C. That’s a lot, but with just a bit of tweaking on the "includes" and header files for the peripherals done, I’ve condensed most of that configuration code into just a few lines. Here’s all I have to do for A to D: ADCInit( ADC_CLK ); and ADCSingleValue = ADCRead( 1 );.
If you’re new to ARM like me, what are your thoughts about CMSIS? If you’ve been using ARMs for years, have you started using CMSIS in your applications?
Related posts: