Coding conventions followed in CMSIS:
Finally, we are there on the main subject : Getting started!
So let us attempt to write our first example for toggling GPIO (Embedded world's "HELLO World" using Keil. It should be straight forward port for IAR and other tool chains, I guess though I cannot comment on that much since I have not had an experience using them for this controller.
Understanding the NU-LB-NUC140 board for the LED experiment:
The chip used on this EVM board is NUC140VE3CN. This board is powered by an external 12MHz crystal as well as 32.768Khz crystal for RTC applications. In this experiment we will attempt our first "Hello World" program by lighting up the LED for sometime and switching it off and continue doing that. The LEDs on this EVM board are connected on GPIO-PortC on pins -12,13,14 & 15. These LEDs are connected in SINK mode. Hence to turn on a LED you must place a ZERO on the GPIO pin and to switch off you must place a ONE.
First Step: Creating your project in KEIL:
E_SYS_CHIP_CLKSRC
Enumeration_identifier Value Description
E_SYS_XTL12M 0 Select External 12M Crystal
E_SYS_XTL32K 1 Select External 32K Crystal
E_SYS_OSC22M 2 Select Internal 22M Oscillator
E_SYS_OSC10K 3 Select Internal 10K Oscillator
E_SYS_PLL 4 Select PLL clock
Here is the main program:
#include "NUC1xx.h"
#include "Driver\DrvSYS.h"
#include "Driver\DrvGPIO.h"
void delay_loop(void)
{
uint32_t i,j;
for(i=0;i<10;i++)
{
for(j=0;j<60000;j++);
}
}
int main(void)
{
//First set the clock input to the processor.
//The choices are internal clock or external crystal.
/* Unlock the protected registers */
UNLOCKREG();
//We will use the internal 22.1184Mhz
DrvSYS_SetOscCtrl(E_SYS_OSC22M,1);
//wait till the clock is stable
while (DrvSYS_GetChipClockSourceStatus(E_SYS_OSC22M) != 1);
/* HCLK clock source. 0: external 12MHz; 7:internal 22MHz RC oscillator */
DrvSYS_SelectHCLKSource(7);
/*lock the protected registers */
LOCKREG();
/* HCLK clock frequency = HCLK clock source / (HCLK_N + 1) */
/* Max divisor value: 0b1111 and min value: 0b0000 */
DrvSYS_SetClockDivider(E_SYS_HCLK_DIV, 0);
//Configure GPIO - GPC- pin 12 as output port pin. LEDS are connected on 12, 13, 14, 15.
DrvGPIO_Open(E_GPC, 12, E_IO_OUTPUT);
while(1)
{
DrvGPIO_SetBit(E_GPC,12);
delay_loop();
DrvGPIO_ClrBit(E_GPC,12);
delay_loop();
}
}
Compile, build and burn the hex file using Nuvoton's ICP programming tool and see the led blinking. You can burn either the .hex file or the .bin file on the EVM board and check your program.
Slight Tweaks for replacing the delay_loop() : Exploring System Tick - a 24bit Timer available on ARM CortexM0.
To replace the above delay_loop() , we made a new function:
void systemDelay(void) //1 sec delay if running out of 12Mhz.
{
//Considering 1tick = 12Mhz so for 1 sec, reload value = 12000000.
SysTick->LOAD = 12000000; //Reload value in register for SysTick for 1 sec delay
SysTick->VAL = (0x000000); //default value of SysTick Current value register.
//Switch on sysTick timer.
//System Clock source is (optional) external reference clock
SysTick->CTRL = (1<<SysTick_CTRL_ENABLE_Pos);
/* Waiting for down-count to zero . 16th bit in SYST_CSR */
while((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0);
}
One more important point to remember:
DrvSYS_SelectSysTickSource(0); //Select External 12Mhz as the system Tick's clock #else
DrvSYS_SelectSysTickSource(7); //Select Internal 22.1184Mhz/2 as the system Tick's
#endif
So the above code could be re-written to replace the delay_loop with systemTick timer in this manner (main_gpio.c)
-------------------------------------------------------------------------------------------------------------------------------
#include "NUC1xx.h"
#include "Driver\DrvSYS.h"
#include "Driver\DrvGPIO.h"
#define CRYSTAL 0 //if you want to use crystal pass 1 else 0 for 22.1184Mhz internal oscillator.
void systemDelay(void) //1 sec delay.
{
SysTick->LOAD = 12000000; //Reload register for SysTick.
SysTick->VAL = (0x000000); //default value of the Current Value SysTick Current value SysTick->CTRL = (1<<SysTick_CTRL_ENABLE_Pos); //Switch on sysTick timer.
/* Waiting for down-count to zero */
while((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0);
}
int main(void)
{
//First set the clock input to the processor.
//The choices are internal clock or external crystal or PLL.
/* Unlock the protected registers */
UNLOCKREG();
#if CRYSTAL
DrvSYS_SetOscCtrl(E_SYS_XTL12M,1); //To use the external 4-24Mhz crystal.
#else
DrvSYS_SetOscCtrl(E_SYS_OSC22M,1); //To use the internal 22.1184Mhz
#endif
//wait till the clock is stable
#if CRYSTAL
while (DrvSYS_GetChipClockSourceStatus(E_SYS_XTL12M) != 1);
#else
while (DrvSYS_GetChipClockSourceStatus(E_SYS_OSC22M) != 1);
#endif
//HCLK clock source. 0: external 12MHz; 7:internal 22MHz RC oscillator
#if CRYSTAL
DrvSYS_SelectHCLKSource(0);
#else
DrvSYS_SelectHCLKSource(7);
#endif
#if CRYSTAL
DrvSYS_SelectSysTickSource(0); //Select External 12Mhz as the system Tick's clock #else
DrvSYS_SelectSysTickSource(7); //Select Internal 22.1184Mhz/2 as the system Tick's #endif
/*lock the protected registers */
LOCKREG();
// HCLK clock frequency = HCLK clock source / (HCLK_N + 1)
// Max divisor value: 0b1111 and min value: 0b0000 DrvSYS_SetClockDivider(E_SYS_HCLK_DIV, 0);
//Configure GPIO - GPC- pin 12 as output port pin. LEDS are connected on 12, 13, 14, 15.
DrvGPIO_Open(E_GPC, 12, E_IO_OUTPUT);
while(1)
{
DrvGPIO_SetBit(E_GPC,12);
//delay_loop();
systemDelay(); //function uses systick timer
DrvGPIO_ClrBit(E_GPC,12);
//delay_loop();
systemDelay();
}
}
- All the data types used in CMSIS is based out of stdint.h file. Data structures for core registers are defined in the CMSIS header core_cm0.h file.
- The core registers, peripheral registers and the CPU instructions follow capital naming convention. Eg: NVIC->ISER[0], GPIOA->ISRC,
- Peripheral access functions and interrupts use camel casing convention. Eg: DrvGPIO_Open().
- CMSIS allows C++ like commenting method (eg: // this is used for commenting.)
Finally, we are there on the main subject : Getting started!
So let us attempt to write our first example for toggling GPIO (Embedded world's "HELLO World" using Keil. It should be straight forward port for IAR and other tool chains, I guess though I cannot comment on that much since I have not had an experience using them for this controller.
Understanding the NU-LB-NUC140 board for the LED experiment:
The chip used on this EVM board is NUC140VE3CN. This board is powered by an external 12MHz crystal as well as 32.768Khz crystal for RTC applications. In this experiment we will attempt our first "Hello World" program by lighting up the LED for sometime and switching it off and continue doing that. The LEDs on this EVM board are connected on GPIO-PortC on pins -12,13,14 & 15. These LEDs are connected in SINK mode. Hence to turn on a LED you must place a ZERO on the GPIO pin and to switch off you must place a ONE.
- NUC140 has 80 general purpose IOs.
- 80 IOs are arranged in GPIOA, GPIOB, GPIOC, GPIOD, GPIOE.
- On reset all IO pins stay in Quasi-Bidirectional mode.
- IOs as outputs can support source/sink capability.
First Step: Creating your project in KEIL:
- Open KEIL -> New uVision Project -> <Select your custom folder> <select the chip as NUC140VE3CN.
- If asked for adding the startup file, please add to the project.
- For simplicity, click on "File Extensions" icon in KEIL and create two sub folders apart from source files folder. Name them as "Library" and "CMSIS" files. Move the files "startup_NUC1xx.s" to "CMSIS" folder. Open the "File Extensions" folder and add "startup_NUC1xx.c" and "core_cm0.c" files into the "CMSIS" folder.
- The "core_cm0.c" file is found in the location <systemInstalledDrive>:\Nuvoton\BSP Library\NUC100SeriesBSP_CMSIS_v1.05.003\CMSIS\CM0\CoreSupport.
- The "startup_NUC1xx.c" file is found in the location: <systemInstalledDrive>:\Nuvoton\BSP Library\NUC100SeriesBSP_CMSIS_v1.05.003\CMSIS\CM0\DeviceSupport\Nuvoton\NUC1xx
- Add a main_gpio.c file to the "Source Files" folder. This is where your main() function will reside and do all the necessary work.
- As we studied in the earlier sections of CMSIS article, we will be using the library provided Nuvoton to program the GPIOs via the nuvoton library wrapper for easy portability rather than directly fiddling with the hardware registers. (Though it is perfectly fine if you wish to directly fiddle around the hardware registers like you do for the conventional 8-bit microcontrollers. I am using the Nuvoton library wrapper approach.
- Since in this example we are going to set the GPIOs, we will first include the GPIO library wrapper provided by Nuvoton by the name "DrvGPIO.c". This file is found in the location: <systemInstalledDrive>:\Nuvoton\BSP Library\NUC100SeriesBSP_CMSIS_v1.05.003\NuvotonPlatform_Keil\Src\Driver\
- Since the clock control is handled by the file "DrvSYS.c". This file should also be included similar to the "DrvGPIO.c".
- It is important to let Keil's project settings know where your include library and core_cm0 headers files reside. Hence add the include paths by clicking on options for the project settings >> C/C++ >> Include Paths and give the following paths in the following order:
- ./
- ../../../CMSIS/CM0/CoreSupport
- ../../../CMSIS/CM0/DeviceSupport/Nuvoton/NUC1xx
- ../../../Include
- ../../../Include/Driver
- ../../../Include/NUC1xx-LB_002
- Then select in options for project settings >> Linker and add "--map --first='startup_NUC1xx.o(RESET)' --datacompressor=off --info=inline --entry Reset_Handler" to Misc controls. Also untick "Use Memory Layout from target Dialog". (refer: http://www.keil.com/support/man/docs/uv4/uv4_dg_adsld.htm)
- One more setting to go, under Users, set "Run User Programs After build/rebuild"
- Under Run1: Tick Run1 checkbox and argument as: fromelf --bin ".\obj\@L.axf" --output ".\obj\@L.bin"
- Under Run2: Tick Run2 checkbox and argument as: fromelf --text -c ".\obj\@L.axf" --output ".\obj\@L.txt"
- Create a folder as obj in the working directory and select this folder for your output dump in Keil's project settings.
- Now let us get to the action.
E_SYS_CHIP_CLKSRC
Enumeration_identifier Value Description
E_SYS_XTL12M 0 Select External 12M Crystal
E_SYS_XTL32K 1 Select External 32K Crystal
E_SYS_OSC22M 2 Select Internal 22M Oscillator
E_SYS_OSC10K 3 Select Internal 10K Oscillator
E_SYS_PLL 4 Select PLL clock
Here is the main program:
#include "NUC1xx.h"
#include "Driver\DrvSYS.h"
#include "Driver\DrvGPIO.h"
void delay_loop(void)
{
uint32_t i,j;
for(i=0;i<10;i++)
{
for(j=0;j<60000;j++);
}
}
int main(void)
{
//First set the clock input to the processor.
//The choices are internal clock or external crystal.
/* Unlock the protected registers */
UNLOCKREG();
//We will use the internal 22.1184Mhz
DrvSYS_SetOscCtrl(E_SYS_OSC22M,1);
//wait till the clock is stable
while (DrvSYS_GetChipClockSourceStatus(E_SYS_OSC22M) != 1);
/* HCLK clock source. 0: external 12MHz; 7:internal 22MHz RC oscillator */
DrvSYS_SelectHCLKSource(7);
/*lock the protected registers */
LOCKREG();
/* HCLK clock frequency = HCLK clock source / (HCLK_N + 1) */
/* Max divisor value: 0b1111 and min value: 0b0000 */
DrvSYS_SetClockDivider(E_SYS_HCLK_DIV, 0);
//Configure GPIO - GPC- pin 12 as output port pin. LEDS are connected on 12, 13, 14, 15.
DrvGPIO_Open(E_GPC, 12, E_IO_OUTPUT);
while(1)
{
DrvGPIO_SetBit(E_GPC,12);
delay_loop();
DrvGPIO_ClrBit(E_GPC,12);
delay_loop();
}
}
Compile, build and burn the hex file using Nuvoton's ICP programming tool and see the led blinking. You can burn either the .hex file or the .bin file on the EVM board and check your program.
Slight Tweaks for replacing the delay_loop() : Exploring System Tick - a 24bit Timer available on ARM CortexM0.
- SysTick provides a simple, 24-bit clear-on-write, decrementing, wrap-on-zero counter with a flexible control mechanism. It comprises of SYST_CVR, SYST_RVR and SYST_CSR registers.
- SYST_CVR is the Current Value Register while SYST_RVR is the Reload Value Register.
- SYST_CSR is the Systick timer control and status register.
- Systick uses either the same CORE_CPU clock or any of the external clock. To use the CORE_CPU clock, the value of bit named CLKSRC in SYST_CSR should be "ONE".
- If CLKSRC in SYST_CSR is "ZERO" then based on STCLK_S in CLKSEL0 register decides the external clock speed to be used for SYSTICK timer.
To replace the above delay_loop() , we made a new function:
void systemDelay(void) //1 sec delay if running out of 12Mhz.
{
//Considering 1tick = 12Mhz so for 1 sec, reload value = 12000000.
SysTick->LOAD = 12000000; //Reload value in register for SysTick for 1 sec delay
SysTick->VAL = (0x000000); //default value of SysTick Current value register.
//Switch on sysTick timer.
//System Clock source is (optional) external reference clock
SysTick->CTRL = (1<<SysTick_CTRL_ENABLE_Pos);
/* Waiting for down-count to zero . 16th bit in SYST_CSR */
while((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0);
}
One more important point to remember:
- You cannot have systick timer source using a different clock source as compared to the main system clock even though it is configurable. Meaning, if your CPU clock is out of external crystal then you cannot select your systick to be coming out internal oscillator at the same time, when I experimented on this board. (I thought it should be possible!). Same way, if you ran the CPU over internal oscillator then trying to run the systick timer clock to be running out of external crystal does not work.
- I decided to use pre-processor directive for this reason as shown below. This code should be part of the protected register write along with setting the CPU clock.
DrvSYS_SelectSysTickSource(0); //Select External 12Mhz as the system Tick's clock #else
DrvSYS_SelectSysTickSource(7); //Select Internal 22.1184Mhz/2 as the system Tick's
#endif
So the above code could be re-written to replace the delay_loop with systemTick timer in this manner (main_gpio.c)
-------------------------------------------------------------------------------------------------------------------------------
#include "NUC1xx.h"
#include "Driver\DrvSYS.h"
#include "Driver\DrvGPIO.h"
#define CRYSTAL 0 //if you want to use crystal pass 1 else 0 for 22.1184Mhz internal oscillator.
void systemDelay(void) //1 sec delay.
{
SysTick->LOAD = 12000000; //Reload register for SysTick.
SysTick->VAL = (0x000000); //default value of the Current Value SysTick Current value SysTick->CTRL = (1<<SysTick_CTRL_ENABLE_Pos); //Switch on sysTick timer.
/* Waiting for down-count to zero */
while((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0);
}
int main(void)
{
//First set the clock input to the processor.
//The choices are internal clock or external crystal or PLL.
/* Unlock the protected registers */
UNLOCKREG();
#if CRYSTAL
DrvSYS_SetOscCtrl(E_SYS_XTL12M,1); //To use the external 4-24Mhz crystal.
#else
DrvSYS_SetOscCtrl(E_SYS_OSC22M,1); //To use the internal 22.1184Mhz
#endif
//wait till the clock is stable
#if CRYSTAL
while (DrvSYS_GetChipClockSourceStatus(E_SYS_XTL12M) != 1);
#else
while (DrvSYS_GetChipClockSourceStatus(E_SYS_OSC22M) != 1);
#endif
//HCLK clock source. 0: external 12MHz; 7:internal 22MHz RC oscillator
#if CRYSTAL
DrvSYS_SelectHCLKSource(0);
#else
DrvSYS_SelectHCLKSource(7);
#endif
#if CRYSTAL
DrvSYS_SelectSysTickSource(0); //Select External 12Mhz as the system Tick's clock #else
DrvSYS_SelectSysTickSource(7); //Select Internal 22.1184Mhz/2 as the system Tick's #endif
/*lock the protected registers */
LOCKREG();
// HCLK clock frequency = HCLK clock source / (HCLK_N + 1)
// Max divisor value: 0b1111 and min value: 0b0000 DrvSYS_SetClockDivider(E_SYS_HCLK_DIV, 0);
//Configure GPIO - GPC- pin 12 as output port pin. LEDS are connected on 12, 13, 14, 15.
DrvGPIO_Open(E_GPC, 12, E_IO_OUTPUT);
while(1)
{
DrvGPIO_SetBit(E_GPC,12);
//delay_loop();
systemDelay(); //function uses systick timer
DrvGPIO_ClrBit(E_GPC,12);
//delay_loop();
systemDelay();
}
}
Hello Sir..I currently working for my Final Year Project. Can I ask you something about example code to save data inside Nuvoton Rom using this Nuvoton Nuc140..Kindly e-mail me at shafiesplitz@gmail.com..Thank You..
ReplyDeleteThank you, your blog is really useful. Looking forward for future posts.
ReplyDeletecan I know which KEIL you have used??
ReplyDelete