> LED and LCDs > ADC > I2C > SPI > PWM > UART > Motor Control > Audio and Digital Audio Processing (DSP)
About the Author Prof Dr Dogan Ibrahim has a BSc degree in electronic engineering, an MSc degree in automatic control engineering, and a PhD degree in digital signal processing. Dogan has worked in many industrial organizations before he returned to academic life. Prof Ibrahim is the author of over 60 technical books and over 200 technical articles on microcontrollers, microprocessors, and related fields. He is a Chartered electrical engineer and a Fellow of the Institution of Engineering Technology.
Get Started with the NXP i.MX RT1010 Development Kit
Conveniently, several on-board debug probes are supplied with the kit allowing you to debug your programs by talking directly to the MCU. Helped by the debugger, you can single-step through a program, insert breakpoints, view and modify variables, and so on. Using the MCUXpresso IDE and the SDK, many working and tested projects are developed in the book based on parts, modules, and technologies, including:
H0W2
At the heart of NXP Semiconductors‘ MIMXRT1010 Development Kit is the i.MX RT1010 Crossover MCU sporting an Arm Cortex-M7 core truly capable of running power- and memory hungry DSP applications. The popular MCUXpresso IDE is key to creating software for the development kit, while a powerful SDK is provided to reduce program development time and effort. The dev kit offers great connectivity through its audio CODECs, 4-way headphone jack, external speaker connection, microphone, and Arduino interface.
H0W2
Volume 3
Get Started with the NXP i.MX RT1010 Development Kit Develop Arm® Cortex®-M7 powered Audio, DSP and Motor Control Projects
Dogan Ibrahim
Dogan Ibrahim
Elektor International Media www.elektor.com
3 Cover HOW2 - Getting Started With NXP iMX Development Board.indd 3
knows how
26-10-2023 10:14
Get Started with the
NXP i.MX RT1010 Development Kit
Develop Arm® Cortex®-M7 powered Audio, DSP and Motor Control Projects
● Dogan Ibrahim
Getting Started with NXP i.MX Development Board - UK.indd 3
26-10-2023 09:34
● This is an Elektor Publication. Elektor is the media brand of Elektor International Media B.V.
PO Box 11, NL-6114-ZG Susteren, The Netherlands Phone: +31 46 4389444
● All rights reserved. No part of this book may be reproduced in any material form, including photocopying, or
storing in any medium by electronic means and whether or not transiently or incidentally to some other use of this publication, without the written permission of the copyright holder except in accordance with the provisions of the Copyright Designs and Patents Act 1988 or under the terms of a licence issued by the Copyright Licencing Agency Ltd., 90 Tottenham Court Road, London, England W1P 9HE. Applications for the copyright holder's permission to reproduce any part of the publication should be addressed to the publishers.
● Declaration The author, editor, and publisher have used their best efforts in ensuring the correctness of the information contained in this book. They do not assume, and hereby disclaim, any liability to any party for any loss or damage caused by errors or omissions in this book, whether such errors or omissions result from negligence, accident or any other cause. All the programs given in the book are Copyright of the Author and Elektor International Media. These programs may only be used for educational purposes. Written permission from the Author or Elektor must be obtained before any of these programs can be used for commercial purposes.
● British Library Cataloguing in Publication Data
A catalogue record for this book is available from the British Library
● ISBN 978-3-89576-582-7 Print ISBN 978-3-89576-583-4
eBook
● © Copyright 2023: Elektor International Media B.V. Editor: Clemens Valens
Prepress Production: D-Vision, Julian van den Berg Print: Ipskamp Printing, Enschede (NL)
Elektor is the world's leading source of essential technical information and electronics products for pro engineers, electronics designers, and the companies seeking to engage them. Each day, our international team develops and delivers high-quality content - via a variety of media channels (including magazines, video, digital media, and social media) in several languages - relating to electronics design and DIY electronics. www.elektormagazine.com
●4
Getting Started with NXP i.MX Development Board - UK.indd 4
26-10-2023 09:34
Contents
Contents Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 Chapter 1 • The MIMXRT1010 Evaluation Kit (EVK) . . . . . . . . . . . . . . . . . . . . . . . . 10 1.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.2 The MIMXRT1010 Evaluation Kit hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.2.1 The i.MX RT1010 processor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.2.2 ON/OFF button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 1.2.3 Reset button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 1.2.4 User button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.2.5 LED indicators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.2.6 JTAG connector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.2.7 Audio input/output connector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.2.8 Power supply . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.2.9 Digital and analog modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 1.3 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Chapter 2 • Installing the Mcuxpresso Software . . . . . . . . . . . . . . . . . . . . . . . . . . 16 2.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 2.2 Installing the MCUXpresso SDK and IDE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 2.3 Testing the installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 2.4 Creating a project from scratch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 2.6 Importing an exported project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.7 MCUXpresso for Visual Studio Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 Chapter 3 • Simple Program Examples and Debugging . . . . . . . . . . . . . . . . . . . . . 30 3.1 Software only programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 3.2 Example 1 – Sum of integer numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 3.3 Example 2 – Table of squares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 3.4 Example 3 – Centigrade to Fahrenheit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 3.5 Example 4 – Times table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 3.6 Example 5 – Table of trigonometric sine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 3.7 Example 6 – Table of trigonometric sine, cosine and tangent . . . . . . . . . . . . . . . . 40 3.8 Example 7 – Integer calculator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
●5
Getting Started with NXP i.MX Development Board - UK.indd 5
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit 3.9 Example 8 – Solution of a quadratic equation . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 3.10 Example 9 – Squares and cubes of numbers . . . . . . . . . . . . . . . . . . . . . . . . . . 46 3.11 Example 10 – Factorial of a number . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 3.12 Debugging a project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 3.12.1 Example debug session . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 Chapter 4 • LED Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 4.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 4.2 Project 1 – Flashing an external LED . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 4.3 Project 2 – LED flashing as Morse SOS signal . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 4.4 Project 3 – Alternately flashing two LEDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 4.5 Project 4 – Chasing LEDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 4.5.1 More efficient program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 4.5.2 Using PortClear and PortSet functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 4.6 Project 5 – Binary counting LEDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 4.7 Project 6 – Random flashing LEDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 4.8 Project 7 – Lucky day of the week . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 4.9 Project 8 – Binary up/down counter with LEDs . . . . . . . . . . . . . . . . . . . . . . . . . . 90 4.10 Project 9 – Binary event counter with LEDs . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 Chapter 5 • 7-Segment LED Displays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 5.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 5.2 7-Segment LED display structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 5.3 Project 1 – 7-Segment 1-digit LED counter . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 5.4 Project 2 – 7-Segment 4-digit multiplexed LED display . . . . . . . . . . . . . . . . . . . 102 5.5 Project 3 – 7-Segment 4-digit multiplexed LED display counter – timer interrupts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 5.6 Project 4 – 7-Segment 4-digit multiplexed LED display counter – blanking leading zeroes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 Chapter 6 • Using Serial Communication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 6.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 6.2 Project 1 – Serial communication between the MIMXRT1010-EVK Development Kit and an Arduino UNO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 Chapter 7 • I²C Bus Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 7.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
●6
Getting Started with NXP i.MX Development Board - UK.indd 6
26-10-2023 09:34
Contents 7.2 The I²C Bus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 7.3 Project 1 – Port expander . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 7.4 Project 2 – TMP102 temperature sensor chip . . . . . . . . . . . . . . . . . . . . . . . . . . 135 Chapter 8 • SPI Bus Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 8.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 8.2 Project 1 – Port expander . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 Chapter 9 • Using LCDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 9.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 9.2 The HD44780 LCD module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 9.3 Project 1 – Displaying text on LCD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 9.4 Project 2 – Using LCDs – simple up counter . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 9.5 Including the LCD codes in a program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 9.6 Project 3 – Using LCDs – simple up counter – including LCD header file . . . . . . . 180 9.7 Project 4 – LCD-based conveyor belt goods counter . . . . . . . . . . . . . . . . . . . . . 182 9.8 Project 5 – Event counter – using external interrupts . . . . . . . . . . . . . . . . . . . . 187 Chapter 10 • Analog-To-Digital Converter (ADC) . . . . . . . . . . . . . . . . . . . . . . . . . 191 10.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 10.2 Project 1 – Voltmeter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 10.3 Project 2 – Analog temperature sensor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 10.4 Project 3 – ON/OFF temperature controller . . . . . . . . . . . . . . . . . . . . . . . . . . 201 10.5 Project 4 – ON/OFF temperature controller – using LCD . . . . . . . . . . . . . . . . . . 206 10.6 Project 5 – Measuring the ambient light intensity . . . . . . . . . . . . . . . . . . . . . . 211 10.7 Project 6 – Ohmmeter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214 Chapter 11 • Using Pulse Width Modulation (PWM) . . . . . . . . . . . . . . . . . . . . . . . 220 11.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220 11.2 Basic theory of the pulse width modulation . . . . . . . . . . . . . . . . . . . . . . . . . . 220 11.3 Features of the i.MXRT1010 processor PWM . . . . . . . . . . . . . . . . . . . . . . . . . . 222 11.4 Operation of the PWM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222 11.5 Project 1 – Mosquito repeller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 Chapter 12 • Electric Motors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232 12.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232 12.2 Project 1 – Two-speed motor speed control . . . . . . . . . . . . . . . . . . . . . . . . . . 232
●7
Getting Started with NXP i.MX Development Board - UK.indd 7
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit 12.3 Project 2 – Varying the motor speed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238 12.4 Project 3 – Changing the speed and motor direction . . . . . . . . . . . . . . . . . . . . 244 Chapter 13 • Using the CMSIS-DSP Library. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254 13.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254 13.2 Project 1 – Matrix addition, multiplication, and transpose . . . . . . . . . . . . . . . . 255 Chapter 14 • Sound and Audio Signal Processing (DSP) . . . . . . . . . . . . . . . . . . . 262 14.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262 14.2 Analog and digital audio sound . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262 14.3 Digital audio sound file formats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263 14.3.1 Uncompressed audio file formats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263 14.3.2 Audio files with lossy compressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264 14.3.3 Audio files with lossless compressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264 14.3.4 Which audio file format to choose? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265 14.3.5 High-quality digital audio sound . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265 14.4 Audio digital signal processing (Audio DSP) . . . . . . . . . . . . . . . . . . . . . . . . . . 265 14.4.1 The SAI module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266 14.4.2 The I2S bus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266 14.4.3 The SAI bus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269 14.5 MIMXRT1010-EVK development kit audio demo project files . . . . . . . . . . . . . . 269 Chapter 15 • References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
●8
Getting Started with NXP i.MX Development Board - UK.indd 8
26-10-2023 09:34
Preface It is becoming important for microcontroller users to adapt to new technologies quickly and learn the architecture and use of high-performance 32-bit microcontrollers. Several manufacturers offer 32-bit microcontrollers as general-purpose processors in embedded applications. For example, companies like NXP Semiconductors, STMicroelectronics and several others offer Arm-based processors for high-speed professional applications. Arm offers 32-bit and 64-bit processors, mainly for embedded applications. Nowadays, the majority of mobile devices such as mobile phones, tablets, and GPS receivers are based on Arm processors. The low cost, low-power consumption, and high performance of Arm processors make them ideal candidates for use in complex communication and mixed-signal applications. This book is about the use of the MIMXRT1010-EVK development kit developed by NXP Semiconductors. This is a two-layer low-cost through-hole USB-powered PCB. At its heart lies the i.MX RT1010 crossover MCU in an 80LQFP package, featuring NXP's advanced implementation of the Arm Cortex-M7 core. This core operates at speeds of up to 500 MHz to provide high CPU performance and best real-time response. It is supported by Zephyr OS for developing the Internet of Things with a free, open-source embedded operating system. The popular MCUXpresso IDE can be used for the development of software for the development kit. Additionally, a powerful SDK is provided which simplifies program development greatly. The kit is shipped with the MIMXRT1011DAE5A MCU, providing 128-Mbit QSPI flash memory, 64 KB ROM, 128 KB RAM, audio codec, 4-way headphone jack, external speaker connection, microphone, ADC, UART, SPI, I2C and additional peripheral support. The board also supports Arduino UNO form factor header pins, making it compatible with many Arduino shields, though NXP also provides dedicated shields for brushless DC and PMSM motor control and LED matrix driver applications. Conveniently, several on-board debug probes are supplied with the kit allowing you to debug your programs by talking directly to the MCU. Helped by the debugger, you can single step through a program, insert breakpoints, view and modify variables and so on. Many working and tested projects have been developed in the book using the popular MCUXpresso IDE and the SDK with various sensors and actuators. The project descriptions, block diagrams, circuit diagrams, complete program listings, and detailed descriptions of all the developed programs are given in the book for all the projects. Use of the popular CMSIS-DSP library is also explained with several matrix operations. The author hopes that readers use the MIMXRT1010-EVK development kit in their future projects. The projects provided in the book can be used without modifications in many applications. Alternatively, readers can base their projects on the ones provided in the book during the development of their projects. Hope you enjoy reading the book. Dr. Dogan Ibrahim London, 2023
●9
Getting Started with NXP i.MX Development Board - UK.indd 9
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Chapter 1 • The MIMXRT1010 Evaluation Kit (EVK) 1.1 Overview The MIMXRT1010 Evaluation Kit (EVK) is an entry-level board, based on the NXP Semiconductor i.MX RT1010 Processor that is fully supported by NXP Semiconductor. This book provides detailed information and various projects on using this evaluation kit. In this chapter you will get to know the most commonly used features of the MIMXRT1010 Evaluation Kit.
1.2 The MIMXRT1010 Evaluation Kit hardware Figure 1.1 shows the MIMXRT1010 evaluation kit. The board features are shown in Table 1.1. The board includes the i.MX RT1010 500 MHz ARM Cortex-M7 Core processor, a large amount of memory, audio (SPDIF and I2S), debug, and USB connectors, Arduino compatible interface, user button, user LED, and a motion sensor.
Table 1.1 MIMXRT1010 board features
● 10
Getting Started with NXP i.MX Development Board - UK.indd 10
26-10-2023 09:34
Chapter 1 • The MIMXRT1010 Evaluation Kit (EVK)
Figure 1.1 The MIMXRT Evaluation Kit. Figure 1.2 shows the block diagram of the Evaluation Kit.
Figure 1.2 Block diagram of the Evaluation Kit.
1.2.1 The i.MX RT1010 processor At the heart of the evaluation kit is the ARM Cortex-M7 Core i.MX RT1010 processor. This is a fast 500 MHz processor with the following features (see Figure 1.3): • Full-featured Floating-Point Unit (FPU) • 128 KB on-chip RAM • 64 KB boot ROM • External memory interface
● 11
Getting Started with NXP i.MX Development Board - UK.indd 11
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
• Two General Programmable Timers (GPT), 4-channel 32-bit resolution each • 4× Watchdogs • 4× Periodic Interrupt Timer (PIT) • FlexPWM • SPDIF audio input and output • 2× I2S, AC97, TDM and Codec/DSP audio interfaces • MQS medium quality audio interface (via GPIO ports) • 2× I2C and 2 x SPI interfaces • 4× UART interfaces • 1× 15-channel Analog-to-Digital Converter (ADC) • 44× GPIO with interrupt capability and FlexIO • Temperature sensor with programmable trim points • Arm® Cortex®-M7 CoreSight debug and trace architecture • Trace Port Interface Unit (TPIU) to support off-chip real-time trace • Support for 5-pin JTAG and SWD debug interfaces • AES-128, SHA-1, SHA-256 and CRC-32 • True random number generation (TRNG) • Real-time clock (RTC) • Secure JTAG controller • Power management controller
Figure 1.3 Features of the i.MXRT 1010 MCU.
1.2.2 ON/OFF button A short pressing of the ON/OFF button turns ON power to the board. A short pressing while in the ON mode generates an interrupt. To turn OFF power, keep the button pressed for approximately 5 seconds.
1.2.3 Reset button There are two Reset buttons. SW9 is the Power Reset button. Pressing the SW9 in the Power ON state will force resetting the system power except for the SNVS domain. The processor will be immediately turned OFF and reinitiate a boot cycle from the Processor Power OFF state. SW3 is the standard POR Reset button.
● 12
Getting Started with NXP i.MX Development Board - UK.indd 12
26-10-2023 09:34
Chapter 1 • The MIMXRT1010 Evaluation Kit (EVK)
1.2.4 User button SW4 is the user button (see Figure 1.1) (GPIO_SD_05 by default) to be used by the developers. Pressing this button changes the state of the port from logic HIGH to LOW.
1.2.5 LED indicators The following LEDs are mounted on the board: • User LED at D25 (turned ON when logic HIGH is applied to GPIO_11) • Main power LED at D27 • Reset LED at D7 • OpenSDA LED at D5
1.2.6 JTAG connector J55 is a standard 10-pin/1.27 mm boxheader Connector for JTAG. The pin definitions are shown in Figure 1.4, support SWD by default.
Figure 1.4 JTAG pin definitions.
1.2.7 Audio input/output connector The audio CODEC used on the MIMXRT1010 EVK Board is Wolfson's Low Power, high-quality Stereo Codec, WM8960. The MIMXRT1010 EVK Board includes one headphone interface (J11), one on-board MIC (P1), two speaker interfaces (J12, J13), and the SPDIF interface (top of the board, J52 & J53, DNP). J11 is a 3.5 mm audio stereo headphone jack, which supports jack detect.
1.2.8 Power supply J41 and J9 (see Figure 1.1) can be used to power the EVK Board. A 5 VDC external power supply can also be used to power the EVK Board by connecting two pins in J1. Different power supplies need different jumper settings of J1. Table 1.2 shows the details.
Table 1.2 Power selection.
● 13
Getting Started with NXP i.MX Development Board - UK.indd 13
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
1.2.9 Digital and analog modules The i.MX RT1010 processors contain various digital and analog modules. A list of all the modules is detailed in the following NXP document: i.MX RT1010 Crossover Processors Data Sheet for Industrial Products, Document Number: IMXRT1010IEC Rev. 0, 09/2019 Some modules are described below. ADC1: The ADC is a 12-bit general-purpose analog to digital converter. DAP: The DAP provides real-time access for the debugger without halting the core to: System memory and peripheral registers and all debug configuration registers. The DAP also provides debugger access to JTAG scan chains. The DAP module is internal to the Cortex-M7 Core Platform. FlexIO1: The FlexIO is capable of supporting a wide range of protocols including, but not limited to: UART, I2C, SPI, I2S, camera interface, display interface, PWM waveform generation, etc. The module can remain functional when the chip is in a low-power mode, provided the clock it is using remains active. FlexPWM1: The pulse-width modulator (PWM) contains four PWM submodules, each of which is set up to control a single half-bridge power stage. Fault channel support is provided. The PWM module can generate various switching patterns, including highly sophisticated waveforms. FlexSPI: FlexSPI acts as an interface to one or two external serial flash devices, each with up to four bidirectional data lines. GPIO1 GPIO2 GPIO5: Used for general purpose input/output to external ICs. Each GPIO module supports up to 32 bits of I/O. GPT1 GPT2: Each GPT is a 32-bit 'free running' or 'set and forget' mode timer with programmable prescaler and compare and capture register. A timer counter value can be captured using an external event and can be configured to trigger a capture event on either the leading or trailing edges of an input pulse. When the timer is configured to operate in 'set and forget' mode, it is capable of providing precise interrupts at regular intervals with minimal processor intervention. The counter has output compare logic to provide the status and interrupt at comparison. This timer can be configured to run either on an external clock or on an internal clock. KPP: The KPP is a 16-bit peripheral that can be used as a keypad matrix interface or as general-purpose input/output (I/O). It supports an 8 × 8 external keypad matrix. The main features are: Multiple-key detection, long key-press detection, Standby key-press detection. Supports a 2-point and 3-point contact key matrix.
● 14
Getting Started with NXP i.MX Development Board - UK.indd 14
26-10-2023 09:34
Chapter 1 • The MIMXRT1010 Evaluation Kit (EVK)
LPI2C1 LPI2C2: The LPI2C is a low power Inter-Integrated Circuit (I²C) module that supports an efficient interface to an I²C bus as a master. The I²C provides a method of communication between a number of external devices. LPUART1 LPUART2 LPUART3 LPUART4: Each of the UART modules support the following serial data transmit/receive protocols and configurations: 7- bit or 8-bit data words, 1 or 2 stop bits, programmable parity (even, odd or none) and programmable baud rates up to 5 Mbps. PIT: The PIT features a 32-bit counter timer, programmable count modules, clock division, interrupt generation, and a slave mode to synchronize count enable for multiple PITs. SAI1 SAI3: The SAI module provides a synchronous audio interface (SAI) that supports full duplex serial interfaces with frame synchronization, such as I2S, AC97, TDM, and codec/ DSP interfaces. SA-TRNG: The SA-TRNG is a hardware accelerator that generates a 512-bit entropy as needed by an entropy consuming module or by other post-processing functions. Temp Monitor: The temperature sensor implements a temperature sensor/conversion function based on a temperature-dependent voltage to time conversion. WDOG1 WDOG2 WDOG3: The Watch Dog Timer supports two comparison points during each counting period. Each of the comparison points is configurable to evoke an interrupt to the Arm core, and a second point evokes an external event on the WDOG line.
1.3 Getting started The board is supplied with a Micro-B USB cable. A demo program is preloaded to the demo kit. Connect the board to a PC using the supplied cable. The demo program flashes the on-board LED. Figure 1.5 shows connecting the board to a PC. The green user LED at the central part of the board should start to flash.
Figure 1.5 Connecting the board to a PC.
● 15
Getting Started with NXP i.MX Development Board - UK.indd 15
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Chapter 2 • I nstalling the MCUXpresso Software Development Kit (SDK) 2.1 Overview In this book, we will be using the MCUXpresso SDK and the IDE for developing projects using the development board. MCUXpresso IDE is an Eclipse-based development environment for NXP MCUs using Cortex-M cores. It supports the i.MX RT, LPC and Kinetis devices, from Cortex-M0+ to up to Cortex-M7. The SDK and the IDE must be installed before they can be used.
2.2 Installing the MCUXpresso SDK and IDE The steps to install the MCUXpresso SDK and IDE are given below: • Go to following website: https://www.nxp.com/design/software/development-software/mcuxpressosoftware-and-tools-/mcuxpresso-integrated-development-environmentide:MCUXpresso-IDE?tid=vanMCUXPRESSO/IDE • Click on DOWNLOADS (Figure 2.1)
Figure 2.1 Click on DOWNLOADS • Click on DOWNLOAD to download the IDE (Figure 2.2)
Figure 2.2 Click to download the IDE • Select your operating system as shown in Figure 2.3 and click to download the IDE
Figure 2.3 Select your operating system
● 16
Getting Started with NXP i.MX Development Board - UK.indd 16
26-10-2023 09:34
Chapter 2 • Installing the MCUXpresso Software Development Kit (SDK)
• Save the file in a folder, unzip it, and click to install it • Run the program by double-clicking on it • Click Download and Install SDKs (Figure 2.4)
Figure 2.4 Click to install SDK • Select the development board as shown in Figure 2.5
Figure 2.5 Select the development board • Click Import SDK Examples • Select examples from the list and click Finish • Download the driver from the following site. At the time of writing this book, it was called: mbedWinSerial_16466.exe. Click to install it: https://www.nxp.com/document/guide/getting-started-with-i-mx-rt1010evaluation-kit:GS-MIMXRT1010-EVK This completes the installation of the MCUXpresso and the IDE.
2.3 Testing the installation Now that the installation is complete, you should check the installation by compiling and running one of the demo programs supplied. Here, you will compile and run the demo example called iled_blinky which flashes the on-board LED. The steps are as follows: • Start the IDE • Select Workspace evkmimxrt1010_iled_blinky (Figure 2.6)
● 17
Getting Started with NXP i.MX Development Board - UK.indd 17
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Figure 2.6 Select the iled_blinky workspace • Click IDE to load the source file (Figure 2.7)
Figure 2.7 Click IDE • You should see the File Explorer in the left window, the program code in the middle window, the header filenames and functions used in the right-hand window, and the console terminal, etc. at the bottom of the window (Figure 2.8). Do not worry if you do not understand how the program works at this stage.
Figure 2.8 The IDE window • The default program is set to flash the on-board LED every second. Let's change the flashing rate to 250 ms. Scroll down and locate the second 'while' statement in the main program. Change 1000U to 250U as shown in Figure 2.9
● 18
Getting Started with NXP i.MX Development Board - UK.indd 18
26-10-2023 09:34
Chapter 2 • Installing the MCUXpresso Software Development Kit (SDK)
Figure 2.9 Change the delay to 250 ms • The program can be uploaded to the development kit and run either in Debug mode or in Release mode. To compile and upload in Release mode, click Project → Build Configurations → Set Active → Release • Click Project → Build Project to build the project. You should see a successful compilation message at the bottom of the screen (Figure 2.10). Make sure the bottom part of the screen is set to Console mode.
Figure 2.10 Project built successfully message • You now have to upload the code to the processor on the development kit. Click Release to expand the folder. Connect USB connector J41 of the development board to your PC. You should see a new drive with the name RT1010-EVK(E:) appear in your devices (The drive letter E: may be different in your computer). • Copy file evkmimxrt1010_iled_blinky.axf and paste it to the new drive RT1010EVK(E:). Initially, nothing seems to happen. Then, press the reset button and after a few seconds, you should see the red LED flashing on the development board during the program loading process. Note, if you have difficulty copying the .axf file to the board, you may have to reinstall the WinUSB driver and remove and reinstall the mbed serial port driver (see https://github.com/ ARMmbed/DAPLink/issues/487). • The .axf file contains both the debug code and running binary code. Alternatively, you can convert the .axf file into a smaller binary file and upload it to the development kit. To achieve this, right-click on the .axf file and select Binary Utilities, followed by Create binary. Copy the .bin file and paste it onto the drive RT1010-EVK (E:).
● 19
Getting Started with NXP i.MX Development Board - UK.indd 19
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
• Press the Reset button at the bottom right of the development board. You should now see the on-board LED flashing every 250 ms. • Alternatively, to run the program in Debug mode, click the Debug button under MCUXpresso IDE – Quickstart Panel at the bottom left-hand corner (pull up the Quickstart Panel if you can't see the debug button). When the debugger is ready, click Run followed by Resume to run the program in debug mode. Click Run followed by Terminate to stop the program. Some information on the program The program generates interrupts at every 250 ms and then the state of the LED is changed. Notice that at the beginning of the program the following header files are included: #include "pin_mux.h" #include "clock_config.h" #include "board.h"
You can right-click on the filenames the right-hand side of the window and display the contents of these files if you wish. File pin_mux.h stores the I/O direction definitions. File clock_config.h stores the clock definitions. File board.h stores the board name, debug and UART configurations, on-board accelerator configurations, user LED configurations, user Button configuration and other configurations. Here we are interested in the user LED configurations, which are defined as follows: /*! @brief The USER_LED used for board */ #define LOGIC_LED_ON (0U) #define LOGIC_LED_OFF (1U) #define BOARD_USER_LED_GPIO GPIO1 #define BOARD_USER_LED_GPIO_PIN (11U)
Logic HIGH and LOW are defined as 0U and 1U, respectively. The on-board user LED GPIO is defined as GPIO1, with the pin number defined as 11 (GPIO_11). Some other on-board LED based definitions are: #define USER_LED_INIT(output) GPIO_PinWrite(BOARD_USER_LED_GPIO, BOARD_USER_LED_ GPIO_PIN, output); BOARD_USER_LED_GPIO->GDIR |= (1U << BOARD_USER_LED_GPIO_PIN)
#define USER_LED_OFF() GPIO_PortClear(BOARD_USER_LED_GPIO, 1U << BOARD_USER_LED_GPIO_PIN)
#define USER_LED_ON() GPIO_PortSet(BOARD_USER_LED_GPIO, 1U << BOARD_USER_LED_GPIO_PIN)
● 20
Getting Started with NXP i.MX Development Board - UK.indd 20
26-10-2023 09:34
Chapter 2 • Installing the MCUXpresso Software Development Kit (SDK)
#define USER_LED_TOGGLE()
GPIO_PinWrite(BOARD_USER_LED_GPIO,
BOARD_USER_LED_GPIO_PIN, 0x1 ^ GPIO_PinRead(BOARD_USER_LED_GPIO,BOARD_USER_LED_GPIO_PIN))
The function GPIO_PinWrite() sets the output state of a port pin to logic LOW or HIGH. Delay is introduced using function SysTick_DelayTicks(). This function waits until the value specified in its argument becomes zero. This value is the required delay in milliseconds. Function SysTick_Handler() is called every millisecond, and it decrements variable g_systickCounter by one every time it is called. i.e. every millisecond. Function SysTick_ Config() initializes the system timer and its interrupt, and starts the system tick number. The Counter is in free-running mode and generates periodic interrupts. Its only argument is the number of ticks between two interrupts. Here, it is set to 1 ms. In this program, we could have used the following GPIO SDK function GPIO_PortToggle() to toggle the LED pin. This simplified program is shown in Figure 2.11 /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include "pin_mux.h" #include "clock_config.h" #include "board.h" /******************************************************************************* * Definitions ******************************************************************************/ #define LED_PIN 11 /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; /******************************************************************************* * Code ******************************************************************************/ void SysTick_Handler(void)
● 21
Getting Started with NXP i.MX Development Board - UK.indd 21
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
{ if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } } /*! * @brief Main function */ int main(void) { /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } while (1) { /* Delay 250 ms */ SysTick_DelayTicks(250U); GPIO_PortToggle(GPIO1, 1u << LED_PIN); } }
Figure 2.11 Program using the GPIO_PortToggle() function
● 22
Getting Started with NXP i.MX Development Board - UK.indd 22
26-10-2023 09:34
Chapter 2 • Installing the MCUXpresso Software Development Kit (SDK)
The GPIO ports must be configured before they can be used (e.g. the input or output mode, default state, analog or digital, etc.). This is normally done in files pin_mux.c and pi_mux.h in folder board. This configuration is already done for the on-board LED. You will learn in the next chapter how to configure the GPIO ports using the ConfigTools menu option of MCUXpresso IDE. Some other GPIO-related functions supported by the SDK are (see web link: https://mcuxpresso.nxp.com/api_doc/dev/1393/a00123.html): Initialize a GPIO pin used by the board: GPIO_PinInit() Set the output level of the multiple GPIO pins to logic 1 or 0: GPIO_PinWrite() Set the output level of the multiple GPIO pins to logic 1: GPIO_PortSet() Set the output level of the multiple GPIO pins to logic 0: GPIO_Port_Clear() Reverse the current output logic of the multiple GPIO pins: GPIO_PortToggle() Read the current input value of the GPIO port: GPIO_PinRead() MCUXpresso IDE is a powerful IDE supporting many features. Interested readers can download the 293-page user guide: MCUXpresso IDE User Guide, Rev. 11.7.1 – 28 March 2023 from the NXP website.
2.4 Creating a project from scratch In this section, you will learn how to create a project from scratch. This is an elementary example project where the message Hello From the MIMXRT1010-EVK is displayed on the Console. The aim of this example is to show how a program can be created, uploaded to the development kit, and then be executed. The steps are: • Start the MCUXpresso IDE and enter a workspace name, e.g. SIMPLE, and click Launch • Click IDE • Click Create a new C/C++ project under the Quickstart Panel • Select MIMXRT1010 as shown in Figure 2.12. Click Next
● 23
Getting Started with NXP i.MX Development Board - UK.indd 23
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Figure 2.12 Select MIMXRT1010 • Enter a project name. e.g. MySimple as shown in Figure 2.13. Note that you can select various drivers at this stage if your program needs them. Here, we have left the default drivers. • Select the Semihost for the SDK Debug Console so that PRINTF displays on the Console. Click Finish.
Figure 2.13 Enter a project name • You will be presented with the screen as shown in Figure 2.14.
● 24
Getting Started with NXP i.MX Development Board - UK.indd 24
26-10-2023 09:34
Chapter 2 • Installing the MCUXpresso Software Development Kit (SDK)
Figure 2.14 MCUXpresso screen (part of the screen is shown) • At the left-hand side, you have the Project Explorer listing all the required project files. The source code is displayed in the middle part of the screen by default. Global variables and header files are displayed at the top right. The bottom part of the screen displays the installed SDKs, Properties, Problems, Console, Terminal, etc. Click to select the Console. • Modify the provided source code as shown in Figure 2.15. /* * Copyright 2016-2023 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ /** * @file
MySimple.c
* @brief
Application entry point.
*/ #include <stdio.h> #include "board.h" #include "peripherals.h" #include "pin_mux.h" #include "clock_config.h" #include "MIMXRT1011.h" #include "fsl_debug_console.h" /* TODO: insert other include files here. */ /* TODO: insert other definitions and declarations here. */ /*
● 25
Getting Started with NXP i.MX Development Board - UK.indd 25
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
* @brief
Application entry point.
*/ int main(void) { /* Init board hardware. */ BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitBootPeripherals(); #ifndef BOARD_INIT_DEBUG_CONSOLE_PERIPHERAL /* Init FSL debug console. */ BOARD_InitDebugConsole(); #endif PRINTF("Hello From the MIMXRT1010-EVK\r\n"); while(1); }
Figure 2.15 Modified source code • You are now ready to compile and run the program in debug mode. Connect your development kit to your PC. Click Debug at the Quickstart Panel and follow the instructions. Select the default debug probe. Wait until the debugger is ready. By default, the program will highlight the first instruction in main() in green and stop there. • Click Run followed by Resume to run the program. • The program will display the message Hello From the MIMXRT1010-EVK on the console as shown in Figure 2.16.
Figure 2.16 Message displayed on the Console • Click Run followed by Terminate to terminate the debug session
● 26
Getting Started with NXP i.MX Development Board - UK.indd 26
26-10-2023 09:34
Chapter 2 • Installing the MCUXpresso Software Development Kit (SDK)
Exporting a project You can save your project by clicking File, followed by Export. Select Archive under General and click Next. Click Select All and give a name to your project. Select to save as .zip (Figure 2.17). Click Finish.
Figure 2.17 Export your project
2.6 Importing an exported project All the projects in this book are available as exported projects in the form of .zip files. The steps to import them are as follows: • Start the MCUXpresso IDE by specifying a new workspace name (e.g. test) and click Launch • Click IDE • In Quickstart Panel, click Import project(s) from file system • Click Browse in the top Archive field • Select the required project folder and click Next • Click Finish When accessing the projects in the book, you are recommended to use the workspace name to be the same name as the project name. For example, to import project folder LM35, set the workspace name to LM35. This way, the project programs will be available in your default workspace. The same project files can then easily be started by entering the same workspace name (e.g. LM35).
● 27
Getting Started with NXP i.MX Development Board - UK.indd 27
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
2.7 MCUXpresso for Visual Studio Code The new software development tool MCUXpresso for Visual Studio Code (VS Code) provides an optimized embedded developer experience for code editing and development. MCUXpresso for VS Code supports NXP MCUs based on Arm Cortex-M cores including MCX, LPC, Kinetis and i.MX RT. MCUXpresso for VS Code allows developers the flexibility to work on projects from Zephyr or MCUXpresso SDK with Open-CMSIS-Packs. A QuickStart panel provides access to the most popular actions. Auto-format and autocomplete features are provided. The debug view provides access to breakpoints, variable/ register views, call stack and thread awareness while using normal debug controls to step through the code. MCUXpresso for VS Code supports various debug probes from NXP, PEmicro and SEGGER. Some features of the MCUXpresso for Visual Code are: • Built on Microsoft Visual Studio Code platform, customize using marketplace extensions • Advanced editing: multi-cursor, auto-format, syntax highlighting, code snippets • Code smarter with Intellisense: completions for variables, methods and imported modules • Flexible use of MCUXpresso SDK using GitHub, with optional Open-CMSIS-Packs • Full support for Zephyr-based project development • Industry-standard GNU tool chain with a choice of libraries: optimized C library or the standard GNU Newlib / Nano library • Easy SDK example importing with MCUXpresso Configuration tools, including pins, clocks, peripheral and trusted execution tools • Source Control Management / Git integration • Project management view displays application information on target architecture, components, build configurations and software repository • Advanced debug capabilities including support for SEGGER J-Link, MCULink or LPC-Link2 probes. Support for FreeRTOS, Azure RTOS ThreadX and Zephyr RTOS. Additionally, improved view capabilities for peripheral registers, global variables, textual/graphical live variables, and integrated GUI Flash programming tool. Figure 2.18 shows the structure of the MCUXpresso for Visual Studio Code.
● 28
Getting Started with NXP i.MX Development Board - UK.indd 28
26-10-2023 09:34
Chapter 2 • Installing the MCUXpresso Software Development Kit (SDK)
Figure 2.18 MCUXpresso for Visual Studio Code The download link for the MCUXpresso for Visual Studio Code is: https://marketplace.visualstudio.com/items?itemName=NXPSemiconductors.mcuxpresso Interested readers can get further information on MCUXpresso for Visual Studio Code from the following websites: https://www.nxp.com/products/processors-and-microcontrollers/mcuxpresso-for-visualstudio-code:MCUXPRESSO-VSC?tid=vanMCUXPRESSO-VSC https://community.nxp.com/t5/MCUXpresso-for-VSCode/bd-p/mcuxpresso-vscode https://github.com/nxp-mcuxpresso/vscode-for-mcux/wiki https://www.nxp.com/products/processors-and-microcontrollers/mcuxpresso-for-visualstudio-code:MCUXPRESSO-VSC?tid=vanMCUXPRESSO-VSC Tutorial and training are available for the MCUXpresso for Studio Code at the following link: https://www.nxp.com/design/training/getting-started-with-mcuxpresso-for-visual-studiocode:TIP-GETTING-STARTED-WITH-MCUXPRESSO-FOR-VS-CODE
● 29
Getting Started with NXP i.MX Development Board - UK.indd 29
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Chapter 3 • Simple Program Examples and Debugging 3.1 Software-only programs In this chapter simple programs are given to review the basic principles of programming in the C language using the MCUXpresso IDE and SDK. The interface to these programs are the PC screen and keyboard. The aim here has been to review the C language programming concepts by developing simple programs, and then uploading and running them on the development kit. All the example programs in this chapter are run under the debugging mode. Examples are given in the chapter on how to use the debugger. Readers who have good working knowledge of the C language and who are familiar using the MCUXpresso IDE can skip this chapter.
3.2 Example 1 – Sum of integer numbers Write a program to read an integer number N from the keyboard, and then calculate and display the sum of all the integer numbers from 1 to N. Solution Figure 3.1 shows the program listing. Comments are used at the beginning of the program to describe the function of the program. It is strongly recommended by the author to include comments in your programs to make them easy to follow and also easy to modify in the future. The program prompts the user by displaying the text 'How many numbers are there ?' by using the debug function PRINTF(). The function SCANF() reads an integer number N from the keyboard. Then, a for loop is formed where the sum of all the integer numbers from 1 to N is calculated and stored in the variable Sum. The sum is finally displayed as an integer number using the function PRINTF(). Running the program Assuming the program has been written, the steps to run the program (and all other programs in this chapter) are as follows (Note: all the programs in this chapter are run in Debug mode): • Click Project → Build Configurations → Set Active → Debug • Click the project name at the top of the Project Explorer window • Click Quick Settings under Miscellaneous at the bottom left window. Then click to select SDK Debug Console, followed by Semihost console (Figure 3.2) • Click Debug at the left-hand side of the window • Select the debugging tool and wait until the program is compiled and loaded into the development kit. The program will display a message similar to: Debug
● 30
Getting Started with NXP i.MX Development Board - UK.indd 30
26-10-2023 09:34
Chapter 3 • Simple Program Examples and Debugging
started on port 58974 @ 127.0.0.1] at the bottom part (Console) of the screen. • Click Run → Resume to run the program • The results of the program will be displayed at the Console part of the screen /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *Sum of numbers 1 to N*/ #include "clock_config.h" #include "board.h" #include "fsl_device_registers.h" #include "fsl_debug_console.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ int N, Sum = 0; /******************************************************************************* * Code ******************************************************************************/ /* Main function */ int main(void) { BOARD_ConfigMPU(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); while(1) { PRINTF("How many numbers are there ? "); SCANF("%d", &N); for (int i = 1; i <= N; i++)
// Do for 1 to N
● 31
Getting Started with NXP i.MX Development Board - UK.indd 31
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
{ Sum = Sum + i;
// Calculate the sum
} PRINTF("\r\nSum of numbers from 1 to %d are: %d", N, Sum); while(1);
// Wait here forever
} }
Figure 3.1 Program listing
Figure 3.2 Select the Semihost console Testing the program • Click Run → Resume • Figure 3.3 shows an example output from the program • Click Run → Terminate to stop the program
Figure 3.3 Example output You could also display the program output using a virtual terminal such as the Tera Term, Putty, or any other one by selecting the serial communication and entering the virtual COM port assigned to your development kit (this can be found from the Device Manager on your PC). You should change the SDK Debug Console to UART, terminate the debugger if it is running, and click Debug before you can direct the output to a virtual serial terminal. The virtual terminal port number is displayed automatically if Tera Term is used (if no other serial devices are used on your PC). An example is shown in Figure 3.4 where the assigned port number was COM14. Make sure that the port speed is set to 115200.
● 32
Getting Started with NXP i.MX Development Board - UK.indd 32
26-10-2023 09:34
Chapter 3 • Simple Program Examples and Debugging
Figure 3.4 Virtual terminal port number
3.3 Example 2 – Table of squares Write a program to tabulate the squares of integer numbers from 1 to 10. Solution Figure 3.5 shows the program listing. A for loop is set up in the main program loop, which tabulates the squares of numbers from 1 to 10. The display items are separated with a tab (i.e. '\t'). /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *Squares*/ #include "clock_config.h" #include "board.h" #include "fsl_device_registers.h" #include "fsl_debug_console.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ /******************************************************************************* * Code ******************************************************************************/ int main(void) {
● 33
Getting Started with NXP i.MX Development Board - UK.indd 33
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
/* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); while (1) { int i, N; PRINTF("TABLE OF SQUARES FROM 1 TO 10\n"); PRINTF("=============================\n"); PRINTF("N\tSQUARE\n"); for (i = 1; i <= 10; i++)
// Do for 1 to N
{ N = i * i;
// Calculate the square
PRINTF("%d\t%d\n", i, N);
// Display
} while(1); } }
Figure 3.5 Program listing Figure 3.6 shows the output from the program, displayed on the Serial Monitor.
Figure 3.6 Output from the program
3.4 Example 3 – Centigrade to Fahrenheit Write a program to receive the temperature as Centigrade and then convert it to Fahrenheit and display on the Serial Monitor. You should read the temperature from the keyboard.
● 34
Getting Started with NXP i.MX Development Board - UK.indd 34
26-10-2023 09:34
Chapter 3 • Simple Program Examples and Debugging
Solution Given the temperature in degrees C, it can be converted into degrees F using the following formula: F = 1.8 × C + 32 Figure 3.7 shows the program listing (CtoF). Function ToF receives Degrees C as its argument, converts into Degrees F and returns to the main program. The temperature in degrees Centigrade is read from the keyboard. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *Centigrade to Fahrenheit*/ #include "clock_config.h" #include "board.h" #include "fsl_device_registers.h" #include "fsl_debug_console.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ /******************************************************************************* * Code ******************************************************************************/ // // Function to convert Degrees C to Degrees F // float ToF(float C) { return (1.8 * C + 32); } int main(void) { float F; int C, Fint;
● 35
Getting Started with NXP i.MX Development Board - UK.indd 35
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
/* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); while (1) { PRINTF("Enter temperature as Degreec C: "); SCANF("%d", &C); Fint = ToF(C);
// COnvert to F
PRINTF("%d Degrees C = %d Degrees F\n", C, Fint); while(1); } }
Figure 3.7 Program listing Figure 3.8 shows output from the program, where 100 degrees Centigrade is converted into Fahrenheit and displayed on the Serial Monitor.
Figure 3.8 Output from the program
3.5 Example 4 – Times table Write a program to read an integer number and then tabulate the times table from 1 to 12 for the given number. Solution Figure 3.9 shows the program listing (Timestable). An integer number is read from the keyboard and stored in variable N. Then a for loop is set up that runs from 1 to 12. The number N is multiplied by 1 to 12 inside this loop and is then displayed on the Serial Monitor. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause *
● 36
Getting Started with NXP i.MX Development Board - UK.indd 36
26-10-2023 09:34
Chapter 3 • Simple Program Examples and Debugging
*Times table*/ #include "clock_config.h" #include "board.h" #include "fsl_device_registers.h" #include "fsl_debug_console.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ /******************************************************************************* * Code ******************************************************************************/ int main(void) { int N, i; /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); while (1) { PRINTF("Enter the number: ");
// Prompt for a number
SCANF("%d", &N); PRINTF("\nTimes table for number %d\n", N);
// Heading
for(i = 1; i <=12; i++) { PRINTF("%d X %d = %d\n", i, N, i*N); } while(1); } }
Figure 3.9 Program listing Figure 3.10 shows output from the program, where the times table for number 5 is displayed.
● 37
Getting Started with NXP i.MX Development Board - UK.indd 37
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Figure 3.10 Output from the program
3.6 Example 5 – Table of trigonometric sine Write a program to tabulate the trigonometric sine between the angles of 0 to 90 degrees, in steps of 5 degrees. Solution Figure 3.11 shows the program listing (Trigonometric-sine). After displaying a heading, a for loop is set up which runs from 0 to 90 in steps of 5. Inside this loop, the sine of the angles are calculated. Notice that the angles for trigonometric functions must be entered in radians. Degrees are converted into radians by multiplying with Pi/180 /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *Trigonometric sine*/ #include "clock_config.h" #include "board.h" #include "fsl_device_registers.h" #include "fsl_debug_console.h" #include "math.h" /******************************************************************************* * Definitions ******************************************************************************/ #define pi 3.14159 /******************************************************************************* * Prototypes ******************************************************************************/ /*******************************************************************************
● 38
Getting Started with NXP i.MX Development Board - UK.indd 38
26-10-2023 09:34
Chapter 3 • Simple Program Examples and Debugging
* Variables ******************************************************************************/ /******************************************************************************* * Code ******************************************************************************/ int main(void) { int degree; float rad; /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); while (1) {
PRINTF("TRIGONOMETRIC SINE\n");
// Heading
PRINTF("==================\n"); PRINTF("Degree\t Sine\n"); for(degree = 0; degree <=90; degree += 5)
// 0 to 90
{ rad = degree * pi / 180.0; PRINTF("%d\t%6.3f\n", degree, sin(rad));
// Convert to radians // Display degree
} while(1); } }
Figure 3.11 Program listing Figure 3.12 shows the output from the program.
● 39
Getting Started with NXP i.MX Development Board - UK.indd 39
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Figure 3.12 Output from the program Note that by default floating-point variables are not displayed by the PRINTF() function and this feature must be enabled as follows: • Click Project followed by Properties • Click C/C++ Build followed by Settings • Click to expand Preprocessor • Click on CR_INTEGER_PRINTF and click on Delete to remove it
3.7 Example 6 – Table of trigonometric sine, cosine and tangent Write a program to tabulate the trigonometric sine between the angles of 0 to 45 degrees, in steps of 5 degrees. Solution The program (sine-cosine-tangent) is similar to the one given in Figure 3.11, but here cosine and tangent are also included. Figure 3.13 shows the program listing. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *Trigonometric sine,cosine,tangent*/ #include "clock_config.h"
● 40
Getting Started with NXP i.MX Development Board - UK.indd 40
26-10-2023 09:34
Chapter 3 • Simple Program Examples and Debugging
#include "board.h" #include "fsl_device_registers.h" #include "fsl_debug_console.h" #include "math.h" /******************************************************************************* * Definitions ******************************************************************************/ #define pi 3.14159 /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ /******************************************************************************* * Code ******************************************************************************/ int main(void) { int degree; float rad; /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); while (1) { PRINTF("TRIGONOMETRIC FUNCTIONS\n");
// Heading
PRINTF("=======================\n"); PRINTF("Degree\tSine\tCosine\tTangent\n"); for(degree = 0; degree <=45; degree += 5)
// 0 to 45
{ rad = degree * pi / 180.0;
// Convert to radians
PRINTF("%d\t%6.3f\t%6.3f\t%6.3f\n", degree, sin(rad), cos(rad), tan(rad)); } while(1); } }
Figure 3.13 Program listing
● 41
Getting Started with NXP i.MX Development Board - UK.indd 41
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Figure 3.14 shows the output from the program.
Figure 3.14 Output from the program
3.8 Example 7 – Integer calculator Write an integer calculator program. The program should receive two integer numbers from the keyboard and the operation to be performed. The result of the calculation should be displayed on the Serial Monitor. Only the four basic operations (+ - * /) should be used in the program. Solution: Figure 3.15 shows the program listing (integer-calculator). Two integer numbers (N1 and N2) and the required operation (oper as + - * /) are read from the keyboard, separated with spaces. A switch block is used to determine the type of operation to be performed. For example, if oper is equal to '+' then numbers N1 and N2 are added together. The result of the operation is stored in variable result, which is displayed at the end of the program. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *Integer calculator*/ #include "clock_config.h" #include "board.h" #include "fsl_device_registers.h" #include "fsl_debug_console.h" #include "math.h" /******************************************************************************* * Definitions ******************************************************************************/ /*******************************************************************************
● 42
Getting Started with NXP i.MX Development Board - UK.indd 42
26-10-2023 09:34
Chapter 3 • Simple Program Examples and Debugging
* Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ /******************************************************************************* * Code ******************************************************************************/ int main(void) { int N1, N2, result; char oper; /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); while (1) { PRINTF("Enter the numbers and operation: "); SCANF("%d %d %c", &N1, &N2, &oper); switch(oper) { case '+':
// Is it +
result = N1 + N2;
// Add
break; case '-':
// Is it -
result = N1 - N2;
// Subtract
break; case '*':
// Is it *
result = N1 * N2;
// Multiply
break; case '/':
// Is it /
result = N1 / N2;
// Divide
break; } PRINTF("\nResult = %d\n", result); while(1); } }
Figure 3.15 Program listing
● 43
Getting Started with NXP i.MX Development Board - UK.indd 43
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Figure 3.16 shows an example output from the program where numbers 23 and 3 are multiplied to give the result 69.
Figure 3.16 Example output from the program
3.9 Example 8 – Solution of a quadratic equation Write a program to calculate the roots of a quadratic equation of the following form and display the roots. Enter a, b, and c from the keyboard. ax2 + bx + c = 0 Solution The roots of a quadratic equation are calculated using the following formula:
x1,2 =
� b ± b 2 � 4ac 2a
Figure 3.17 shows the program listing (quadratic). /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *Quadratic equation*/ #include "clock_config.h" #include "board.h" #include "fsl_device_registers.h" #include "fsl_debug_console.h" #include "math.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/
● 44
Getting Started with NXP i.MX Development Board - UK.indd 44
26-10-2023 09:34
Chapter 3 • Simple Program Examples and Debugging
/******************************************************************************* * Variables ******************************************************************************/ float a, b, c, root1, root2, rootreal, rootimag; int real; /******************************************************************************* * Code ******************************************************************************/ // // This function calculates and returns the roots // void quadratic() { float det = b * b - 4.0 * a * c; if(det >= 0.0)
// If positive determinant
{ real = 1; det = sqrt(det);
// Claculate square root
root1 = (-b + det) / (2.0 * a);
// Root 1
root2 = (-b - det) / (2.0 * a);
// Root 2
} else
// Negative determinant
{ real = 0; det = -det;
// Negate the determinant
det = sqrt(det);
// Calculate square toot
rootreal = -b / (2.0 * a);
// Real part of root
rootimag = det / (2.0 * a);
// Imaginary part of root
} } int main(void) { /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); while (1) { PRINTF("Enter a b c: ");
// Enter a
SCANF("%f %f %f", &a, &b, &c); quadratic();
// Calculate roots
if(real == 1)
// If real roots
{ PRINTF("Root1 = %6.3f\n", root1);
● 45
Getting Started with NXP i.MX Development Board - UK.indd 45
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
PRINTF("Root2 = %6.3f\n", root2); } else
// If complex roots
{ PRINTF("Root1 = %6.3f+j%6.3f\n", rootreal, rootimag); PRINTF("Root2 = %6.3f-j%6.3f\n", rootreal, rootimag); } while(1); } }
Figure 3.17 Program listing An example output is shown in Figure 3.18 for the solution of the quadratic equation: −2x2 + 2x + 1 = 0
Figure 3.18 Solution of: −2x2 + 2x + 1 = 0 Figure 3.19 shows an example with complex roots for the equation: 2x2 +2x + 1 = 0
Figure 3.19 Solution of: 2x2 + 2x + 1 = 0
3.10 Example 9 – Squares and cubes of numbers Write a program to tabulate the squares and cubes of numbers from 1 to 10. Solution The program listing is shown in Figure 3.20 (squares-and-cubes). Example output from the program is shown in Figure 3.21.
● 46
Getting Started with NXP i.MX Development Board - UK.indd 46
26-10-2023 09:34
Chapter 3 • Simple Program Examples and Debugging
/* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *Squares and cubes*/ #include "clock_config.h" #include "board.h" #include "fsl_device_registers.h" #include "fsl_debug_console.h" #include "math.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ /******************************************************************************* * Code ******************************************************************************/
int main(void) { /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); while (1) { PRINTF("SQUARES AND CUBES OF NUMBERS\n"); PRINTF("============================\n"); PRINTF("N\tN*N\tN*N*N\n"); for(int i = 1; i < 11; i++) {
PRINTF("%d\t%d\t%d\n", i, i*i, i*i*i); }
● 47
Getting Started with NXP i.MX Development Board - UK.indd 47
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
while(1); } }
Figure 3.20 Program listing
Figure 3.21 Example output
3.11 Example 10 – Factorial of a number Write a program to calculate the factorial of a number entered from the keyboard. Solution The program listing is shown in Figure 3.22 (factorial). Figure 3.23 shows example output from the program. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *Factorial*/ #include "clock_config.h" #include "board.h" #include "fsl_device_registers.h" #include "fsl_debug_console.h" #include "math.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /*******************************************************************************
● 48
Getting Started with NXP i.MX Development Board - UK.indd 48
26-10-2023 09:34
Chapter 3 • Simple Program Examples and Debugging
* Variables ******************************************************************************/ /******************************************************************************* * Code ******************************************************************************/
int main(void) { int N; int32_t factorial; /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); while (1) { PRINTF("Enter a number: "); SCANF("%d", &N); factorial = 1; if(N == 0)PRINTF("The factorial of 0 is 1"); else {
for(int i = 1; i <= N; i++)
factorial = factorial*i;
} PRINTF("The factorial of %d is %d\n", N, factorial); while(1); } }
Figure 3.22 Program listing
Figure 3.23 Example output
● 49
Getting Started with NXP i.MX Development Board - UK.indd 49
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
3.12 Debugging a project The process of debugging is essential during program development. It is the process of finding and fixing errors or bugs in a program. When a program does not work as expected, it is common to use debugging tools to run the program in a controlled environment, usually step by step, to analyze and fix the errors. A debug operation requires a physical connection between the host computer and the target MCU via a debug probe. The debug probe translates the high-level commands provided by MCUXpresso IDE into the appropriate low-level operations supported on the target MCU. Fortunately, the MIMXRT 1010-EVK development board has on-board debug probes, which simplifies the debugging task greatly. To debug a program, simply highlight the program in the Project Explorer and then click Debug under MCUXpresso IDE → Quickstart Panel (Figure 3.24). Alternatively, click the blue bug icon to perform the same action (The green bug icon should not be used because this invokes the standard Eclipse debug operation and so skips certain essential MCUXpresso IDE debug steps). For a newly created project, a debug operation will perform a number of steps. By default, it will first build the selected project and (assuming there are no build errors) launch a debug probe discovery operation to allow the user to select the required debug probe. A launch configuration file will automatically be created with default options (per build configuration) and will be associated with the project. Like a project's build configuration, launch configuration files control what occurs each time a debug operation is performed.
Figure 3.24 Click Debug The first time you debug a project, the IDE will perform a probe discovery operation and display the discovered Debug Probes for selection. This will show a dialogue listing all supported probes that are attached to the host computer. For any future debug sessions, the stored probe selection will be automatically used to match the project being debugged with the previously used debug probe. This greatly simplifies the case where multiple debug probes are being used. However, if a debug operation is performed and the previously remembered debug probe cannot be found, then a debug probe discovery operation will be performed from within the same family e.g. LinkServer, PEmicro or SEGGER. When you have started a debug session, a default breakpoint is set on the first instruction in main(), the application is started (by simulating or performing a processor reset), and code is ex-
● 50
Getting Started with NXP i.MX Development Board - UK.indd 50
26-10-2023 09:34
Chapter 3 • Simple Program Examples and Debugging
ecuted until the default 'breakpoint is hit. Program execution can now be controlled using the common debug control buttons, as listed in Table 3.1, which are displayed on the global toolbar.
Table 3.1 Debug control buttons Clean up debug will kill all debug processes associated with LinkServer, PEmicro and SEGGER debug connections. This may be necessary if the IDE is restarted with a connected debug session or if a crash occurs – and will remove any failed or orphaned debug processes. Setting a breakpoint: To set a breakpoint, simply double-click on the left margin area of the line on which you wish to set the breakpoint (before the line number). Restarting the application: If you hit a breakpoint or pause execution and want to start execution of the application from the beginning again, you can do this using the Restart button. Stopping debugging: To stop debugging, just press the Terminate/Stop button. This action will disconnect MCUXpresso IDE from the target (board). The subsequent behaviour is controllable by the disconnect behaviour. Pause debugging: Typically, debugging is paused due to the action of a breakpoint or watch point since these will be set to observe the target when an event of interest has occurred. However, the pause button can be used to pause the target at an instant of time. The debugger has many configuration options. Interested readers can find detailed information on using the debugger in the following document: MCUXpresso IDE User Guide, Rev. 11.7.1 - 28 March, 2023 In the remainder parts of this chapter, we will be looking at an example program and learn how the debugger can be used with this program.
● 51
Getting Started with NXP i.MX Development Board - UK.indd 51
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
3.12.1 Example debug session In this example, we will use the simple program given in Example 1 (Sum of integer numbers) of this chapter, shown in Figure 3.1. The steps are as follows: • Load the program to your MCUXpresso • Connect your development kit to your PC • Click Quick Settings under tab Miscellaneous under the MCUXpresso IDE – Quickstart Panel • Click SDK Debug Console and select Semihost console • Click the Debug button under MCUXpresso IDE – Quickstart Panel • If this is the first time you are using the debugger, you will be presented with an option to choose a debug probe on your development kit (there are 3 debugger probes on the MIMXRT1010-EVK development kit); otherwise the debugger will start to compile the program and initiate the debug session. After a short delay, the debugger will highlight the first executable statement in green in the main() program as shown in Figure 3.25.
Figure 3.25 Debugger stopped at the first executable line in main() • Pressing Run followed by Resume will run the program at this stage. But here we will single step through the program. Press key F6 to step through the program. You should see the green highlight moving down the program statements as you press F6. Enter 5 to the prompt How many numbers are there ? • Place the cursor over Sum, you should see the value of this variable (in decimal, hexadecimal, binary and octal) as you step through the program (Figure 3.26)
● 52
Getting Started with NXP i.MX Development Board - UK.indd 52
26-10-2023 09:34
Chapter 3 • Simple Program Examples and Debugging
Figure 3.26 Place the cursor over Sum • Step through until the program comes out of the for loop. You should see the result displayed in the Console (Figure 3.27);
Figure 3.27 result is displayed • Click Run followed by Terminate to end the debug session. • We will now insert breakpoints in the program. The breakpoint will be inserted to a point just before displaying the result. The steps are: • Click Run followed by Debug Last Launched • Place the cursor on the left margin where the numbers are, on the line PRINTF and double-click the mouse. You should see a small mark on the left side of the number in the margin.
● 53
Getting Started with NXP i.MX Development Board - UK.indd 53
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
• This has placed a breakpoint marker at this statement. Now, click Run followed by Resume. The program will run up to the breakpoint and then stop there. Enter 10 as the numbers to be added (Figure 3.28)
Figure 3.28 Program stopped at the breakpoint • Now, press F6 to step through the program and display the result of PRINTF (Figure 3.29).
Figure 3.29 Press F6 to step to the result • Double-click on the breakpoint marker to remove the breakpoint. Click Run followed by Terminate to terminate debugging. We will now watch a variable as its value changes during single stepping. For this example, a simple program code is shown in Figure 3.30. Here, we will watch the variable Count, which is a global variable.
● 54
Getting Started with NXP i.MX Development Board - UK.indd 54
26-10-2023 09:34
Chapter 3 • Simple Program Examples and Debugging
Figure 3.30 Simple program The steps are: • Start the debugger. • Click Window followed by Show View. Then click Global Variables. A new window will appear at the right-hand side. • Click icon Add global variables and select Count from the shown list. You should see variable Count displayed as shown in Figure 3.31.
Figure 3.31 Displaying variable Count • Click F6 and single step until statement PRINTF. Observe that Count has the value 0. • Click F6 until the statement while() is highlighted. You should see the value of Count changed to 100 (Figure 3.32).
Figure 3.32 Value of Count changed
● 55
Getting Started with NXP i.MX Development Board - UK.indd 55
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Chapter 4 • LED Projects 4.1 Overview In this chapter you will be developing various projects using external LEDs. The aim of this chapter is to show how LEDs can be interfaced to the development kit and how they can be programmed using the MCUXpresso. Table 4.1 shows the pin configuration of the commonly used peripheral protocols (they must be routed to the required functions. Some functions are also available on different pads). Name
Function
Usage
GPIO_11
GPIO
On-board LED
GPIO_SD_05
GPIO
On-board SW4
GPIO_10
LPUART1_TX
UART Console
GPIO_09
LPUART1_RX
UART Console
GPIO_13
LPUART2_RX
UART 2 RX
GPIO_AD_00
LPUART2_TX
UART2 TX
GPIO_12
LPUART3_TX
UART3 TX
GPIO_07
LPUART3_RX
UART3 RX
GPIO_AD_02
LPUART4_TX
UART4 TX
GPIO_AD_01
LPUART4_RX
UART4 RX
GPIO_01
LPI2C1_SDA
I2C1 SDA
GPIO_02
LPI2C1_SCL
I2C1 SCL
GPIO_AD_08
LPI2C2_SCL
I2C2 SCL
GPIO_AD_07
LPI2C2_SDA
I2C2 SDA
GPIO_AD_03
LPSPI1_SDI
SPI1 MISO
GPIO_AD_04
LPSPI1_SDO
SPI1 MOSI
GPIO_AD_05
LPSPI1_PCS0
SPI1 CS
GPIO_AD_06
LPSPI1_SCK
SPI1 CLK
GPIO_00
LPSPI2_PCS3
SPI2 CS
GPIO_AD_12
LPSPI2_SCK
SPI2 CLK
GPIO_AD_09
LPSPI2_PCS0
SPI2 CS
GPIO_AD_10
LPSPI2_SDO
SPI2 MOSI
GPIO_AD_09
LPSPI2_SDI
SPI2 MISO
GPIO_AD_14
ADC
ADC channel 14
GPIO_AD_13
ADC
ADC channel 13
GPIO_AD_12
ADC
ADC channel 12
GPIO_AD_11
ADC
ADC channel 11
● 56
Getting Started with NXP i.MX Development Board - UK.indd 56
26-10-2023 09:34
Chapter 4 • LED Projects
GPIO_AD_10
ADC
ADC channel 10
GPIO_AD_09
ADC
ADC channel 9
GPIO_AD_08
ADC
ADC channel 8
GPIO_AD_07
ADC
ADC channel 7
GPIO_AD_06
ADC
ADC channel 6
GPIO_AD_05
ADC
ADC channel 5
GPIO_AD_04
ADC
ADC channel 4
GPIO_AD_03
ADC
ADC channel 3
GPIO_AD_02
ADC
ADC channel 2
GPIO_AD_01
ADC
ADC channel 1
GPIO_AD_00
ADC
ADC channel 0
GPIO_02
PWM1:A,0
PWM1
GPIO_08
PWM1:A,3
PWM1
GPIO_07
PWM1:B,3
PWM1
GPIO_06
PWM1:A,2
PWM1
GPIO_05
PWM1:B,2
PWM1
GPIO_04
PWM1:A,1
PWM1
GPIO_03
PWM1:B,1
PWM1
GPIO_01
PWM1:B,0
PWM1
GPIO_08
SA1:MCLK
SA1
GPIO_07
SA1:TX_SYNC
SA1
GPIO_06
SA1:TX_BCLK
SA1
GPIO_05
SA1:TX_RX_DATA1
SA1
GPIO_04
SA1:TX_DATA0
SA1
GPIO_03
SA1:RX_DATA0
SA1
GPIO_02
SA1:RX_SYNC
SA1
GPIO_01
SA1:RX_BCLK
SA1
GPIO_00
SA13:MCLK
SA13
GPIO_SD_04
SA13:RX_SYNC
SA13
GPIO_SD_03
SA13:RX_DATA
SA13
GPIO_SD_02
SA13:TX_DATA
SA13
GPIO_SD_01
SA13:TX_BCLK
SA13
GPIO_SD_00
SA13:TX_SYNC
Table 4.1 Peripheral protocol pin configurations
● 57
Getting Started with NXP i.MX Development Board - UK.indd 57
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
4.2 Project 1 – Flashing an external LED Description: This project flashes an external LED connected to Arduino header J57, pin 8 every 2 seconds. The aim of the project is to show how an external LED can be connected to the development board and how it can be configured and programmed. Block diagram: Figure 4.1 shows the block diagram of the project.
Figure 4.1 Block diagram of the project Circuit diagram: The circuit diagram is shown in Figure 4.2. The LED is connected in current sinking mode with a 1 kΩ current limiting resistor connected in series with the LED. Assuming a voltage drop of 2 V across the LED, the current drawn from the port pin will be approximately (3.3 V – 2 V) / 1 kΩ = 1.3 mA. A smaller resistor, e.g. 330 Ω can be used for higher brightness.
Figure 4.2 Circuit diagram of the project The GPIO pins of the development board can be configured for several different functions. The default I/O pin names of the Arduino header pins are shown in Table 4.2 to Table 4.5. The pin you will be using in this project is IO18.
● 58
Getting Started with NXP i.MX Development Board - UK.indd 58
26-10-2023 09:34
Chapter 4 • LED Projects
1. NC
2. UART1_RXD (IO9)
3. NC
4. UART1_TXD (IO10)
5. NC
6. GPIO_AD_05 (IO19)
7. NC
8. GPIO_AD_06 (IO20)
9. NC
10. NC
11. NC
12. NC
13. NC
14. GPIO_AD_01 (IO15)
15. NC
16. ADC12_2 (IO16)
Table 4.2 Arduino header J56 1. NC
2. GPIO_SD_02 (IO2)
3. NC
4. NC
5. NC
6. GPIO_AD_05 (IO19)
7. NC
8. GPIO_AD_04 (IO18)
9. NC
10. GPIO_AD_03 (IO17)
11. NC
12. GPIO_AD_06 (IO20)
13. NC
14. NC
15. NC
16. NC
16. NC
18. I2C1_SDA (IO1)
17. NC
20. I2C1_SCL (IO2)
Table 4.3 Arduino header J57 12. GPIO_AD_02 (IO16)
11. NC
10. GPIO_AD_01 (IO15)
9. NC
8. GPIO_AD_14 (IO28)
7. NC
6. GPIO_AD_10
5. NC
4. GPIO_AD_09
3. NC
2. GPIO_AD_07 (IO21)
1. NC
Table 4.4 Arduino header J26
● 59
Getting Started with NXP i.MX Development Board - UK.indd 59
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
16. +5 V
15. NC
14. GND
14. NC
12. GND
11. NC
10. +5 V
9. NC
8. +3.3 V
7. NC
6. RESET
5. NC
4. +3.3 V
3. GND
2. NC
1. GND
Table 4.5 Arduino header J60 Be careful with the pin numbering of headers J26 and J60. Program listing: The GPIO pin at header J57, pin 8 (GPIO_AD_04) must be configured before it can be used as an output port. This is easily done using the ConfigTools menu option of MCUXpresso IDE. The steps are: • Start the MCUXpresso with the same Workspace as in Figure 2.11 (i.e. project iled_blinky). • Click ConfigTools menu option, followed by Pins. You should see the configuration menu as in Figure 4.3. In the left-hand side window are the pin names and their functions, the middle part of the window shows the package, the bottom part of the window shows the pin routing details.
Figure 4.3 The configuration menu
● 60
Getting Started with NXP i.MX Development Board - UK.indd 60
26-10-2023 09:34
Chapter 4 • LED Projects
• Click to select Pin 56, GPIO_AD_04 and select GPIO1:gpio (Figure 4.4)
Figure 4.4 Select Pin 56 • You should see the selected pin routing details at the bottom of the screen. Set the Direction to Output (Figure 4.5). Notice that the port number is 18, and you can select various other options for the port.
Figure 4.5 Set the Direction to Output • Click menu option Update Code and then OK to exit the configuration mode • Port pin 18 is now configured as digital output. Files pin_mux.c and pin_mux.h have been modified automatically to reflect the changes. The following lines of code are added to file pin_mux,c: /* GPIO configuration of LPSPI1_SDO on GPIO_AD_04 (pin 56) */ gpio_pin_config_t LPSPI1_SDO_config = { .direction = kGPIO_DigitalOutput, .outputLogic = 0U, .interruptMode = kGPIO_NoIntmode }; /* Initialize GPIO functionality on GPIO_AD_04 (pin 56) */ GPIO_PinInit(GPIO1, 18U, &LPSPI1_SDO_config); IOMUXC_SetPinMux(IOMUXC_GPIO_AD_04_GPIOMUX_IO18, 0U);
● 61
Getting Started with NXP i.MX Development Board - UK.indd 61
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
The following lines of code are added to file pin_mux.h: /* GPIO_AD_04 (number 56), LPSPI1_SDO/J57[8]/U27[5] */ /* Routed pin properties */ #define BOARD_LPSPI1_SDO_PERIPHERAL GPIO1
/*!< Peripheral name */
#define BOARD_LPSPI1_SDO_SIGNAL gpiomux_io
/*!< Signal name */
#define BOARD_LPSPI1_SDO_CHANNEL 18U
/*!< Signal channel */
The new code in pin_mux.c configures the port as an output with no interrupt, and it then initializes it. The new code in pin_mux.h defines the signal and channel names for port 18. Figure 4.6 shows the complete program listing (ext-led). This program is the same as the one in Figure 2.11, but here port 18 is used. Figure 4.7 shows the project constructed using a breadboard. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *This program flashes an external LED every 2 seconds*/
#include "pin_mux.h" #include "clock_config.h" #include "board.h" /******************************************************************************* * Definitions ******************************************************************************/ #define LED_PIN (18U) /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; /******************************************************************************* * Code ******************************************************************************/ void SysTick_Handler(void)
● 62
Getting Started with NXP i.MX Development Board - UK.indd 62
26-10-2023 09:34
Chapter 4 • LED Projects
{ if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } } /*! * @brief Main function */ int main(void) { /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } while (1) { /* Delay 2000 ms */ SysTick_DelayTicks(2000U); GPIO_PortToggle(GPIO1, 1u << LED_PIN); } }
Figure 4.6 Program listing
● 63
Getting Started with NXP i.MX Development Board - UK.indd 63
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Figure 4.7 Construction of the project
4.3 Project 2 – LED flashing as Morse SOS signal Description: This project flashes the external LED connected to Arduino header J57, pin 8 as a Morse SOS signal (ON ON ON OFF OFF OFF ON ON ON i.e. … --- …). The block diagram and circuit diagram of the project are the same as in Figure 4.1 and Figure 4.2. Program listing: Figure 4.8 shows the program listing (SOS). The main program runs inside a while loop. Here, the LED is flashed three times, with 200 ms between each flash to represent three dots (letter 'S' in Morse code). After 500 ms the LED is flashed again, but this time 600 ms between each flash to represent the letter 'O' in Morse code. Then, the LED flashes again three times with 200 ms to represent three dots (letter 'S' in Morse code). This process is repeated after a two-second delay. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *This program flashes an external LED as a Morse SOS signal*/
#include "pin_mux.h" #include "clock_config.h" #include "board.h" /******************************************************************************* * Definitions ******************************************************************************/ #define LED_PIN (18U)
● 64
Getting Started with NXP i.MX Development Board - UK.indd 64
26-10-2023 09:34
Chapter 4 • LED Projects
/******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; /******************************************************************************* * Code ******************************************************************************/ void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } } /* Main function */ int main(void) { /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } }
● 65
Getting Started with NXP i.MX Development Board - UK.indd 65
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
while (1) { for(int i=0; i < 3; i++)
// Send S
{
GPIO_PinWrite(GPIO1, LED_PIN, 1U);
SysTick_DelayTicks(200U); GPIO_PinWrite(GPIO1, LED_PIN, 0U);
// ON // OFF
SysTick_DelayTicks(200); } SysTick_DelayTicks(500); for(int i=0; i < 3; i++)
// Send O
{ GPIO_PinWrite(GPIO1, LED_PIN, 1U);
// ON
SysTick_DelayTicks(600); GPIO_PinWrite(GPIO1, LED_PIN, 0U);
// OFF
SysTick_DelayTicks(600); } SysTick_DelayTicks(100); for(int i=0; i < 3; i++)
// Send S
{ GPIO_PinWrite(GPIO1, LED_PIN, 1U);
// ON
SysTick_DelayTicks(200); GPIO_PinWrite(GPIO1, LED_PIN, 0U);
// OFF
SysTick_DelayTicks(200); } SysTick_DelayTicks(2000);
// Wait 2 sec
} }
Figure 4.8 Program listing
4.4 Project 3 – Alternately flashing two LEDs Description: In this project, two LEDs are connected to the development kit. The LEDs flash alternately 250 ms each. The aim of this project is to show how more than one LED can be connected to the development kit and how these LEDs can be configured and programmed. Block diagram: Figure 4.9 shows the block diagram of the project.
● 66
Getting Started with NXP i.MX Development Board - UK.indd 66
26-10-2023 09:34
Chapter 4 • LED Projects
Figure 4.9 Block diagram of the project Circuit diagram: The circuit diagram of the project is shown in Figure 4.10. LED1 is connected to Arduino header J57, pin 8 (GPIO_AD_03, IO8). LED2 is connected to header J57, pin 10 (IO17). 1 kΩ current limiting resistors are used with each LED.
Figure 4.10 Circuit diagram of the project Program listing: You should configure port pin GPIO_AD_03 for output as described in the previous project (Figure 4.11). Figure 4.12 shows the program listing (alternate-leds). Inside the main program loop, the two LEDs are turned ON alternately for 250 ms each. Figure 4.12 shows the project constructed on a breadboard.
● 67
Getting Started with NXP i.MX Development Board - UK.indd 67
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Figure 4.11 Configure GPIO_AD_03 as output /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *This program flashes 2 LEDs alternately for 250 ms each*/
#include "pin_mux.h" #include "clock_config.h" #include "board.h" /******************************************************************************* * Definitions ******************************************************************************/ #define LED1 (18U)
// At port 18
#define LED2 (17U)
// At port 17
/******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter;
● 68
Getting Started with NXP i.MX Development Board - UK.indd 68
26-10-2023 09:34
Chapter 4 • LED Projects
/******************************************************************************* * Code ******************************************************************************/ void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } } /* Main function */ int main(void) { /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } while (1) { GPIO_PinWrite(GPIO1, LED1, 1U);
// LED1 ON
GPIO_PinWrite(GPIO1, LED2, 0U);
// LED2 OFF
SysTick_DelayTicks(250);
// 250 ms delay
GPIO_PinWrite(GPIO1, LED1, 0U);
// LED1 OFF
GPIO_PinWrite(GPIO1, LED2, 1U);
// LED2 ON
SysTick_DelayTicks(250);
// 250 ms delay
● 69
Getting Started with NXP i.MX Development Board - UK.indd 69
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
} }
Figure 4.12 Program listing
Figure 4.13 Project constructed on a breadboard
4.5 Project 4 – Chasing LEDs Description: In this project, 8 LEDs are connected to the Arduino headers of the development kit. The LEDs chase each other as shown in Figure 4.14, with a 250 ms delay between each output. The aim of this project is to show how multiple LEDs can be connected, configured, and programmed.
Figure 4.14 Chasing LEDs Block diagram: The block diagram of the project is shown in Figure 4.15.
● 70
Getting Started with NXP i.MX Development Board - UK.indd 70
26-10-2023 09:34
Chapter 4 • LED Projects
Figure 4.15 Block diagram of the project Circuit diagram: Figure 4.16 shows the circuit diagram. The 8 LEDs are connected to Arduino header J57 and J56 as follows using 1 kΩ current limiting resistors: LED1 LED2 LED3 LED4 LED5 LED6 LED7 LED8
J57, pin 6 J57, pin 8 J57, pin 10 J57, pin 12 J57, pin 18 J57, pin 20 J56, pin 14 J56, pin 16
GPIO_AD_05 GPIO_AD_04 GPIO_AD_03 GPIO_AD_06 I2C1_SDA I2C1_SCL GPIO_AD_01 ADC12_2
(IO19) (IO18) (IO17) (IO20) (IO1) (IO2) (IO15) (IO16)
Figure 4.16 Circuit diagram of the project Program listing: You should, first of all, configure all the used ports as outputs as described in the previous projects (Figure 4.17). Function ALLOFF() turns OFF all the LEDs. Function OnlyOne() receives the LED name as its parameter and turns ON the specified
● 71
Getting Started with NXP i.MX Development Board - UK.indd 71
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
LED for 250 ms. Inside the main program the function OnlyOne() is called with its parameters ranging from LED1 to LED8. As a result, the LEDs turn ON from right to left as if they are chasing each other. Figure 4.18 shows the program listing (chasing-1). The project built on a breadboard is shown in Figure 4.19.
Figure 4.17 Configure ports as outputs /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *Chasing 8 LEDs - 1*/
#include "pin_mux.h" #include "clock_config.h" #include "board.h" /******************************************************************************* * Definitions ******************************************************************************/ #define LED1 (19U)
// At port 19
#define LED2 (18U)
// At port 18
#define LED3 (17U)
// At port 17
#define LED4 (20U)
// At port 20
#define LED5 (1U)
// At port 1
#define LED6 (2U)
// At port 2
#define LED7 (15U)
// At port 15
● 72
Getting Started with NXP i.MX Development Board - UK.indd 72
26-10-2023 09:34
Chapter 4 • LED Projects
#define LED8 (16U)
// At port 16
/******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; /******************************************************************************* * Code ******************************************************************************/ void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } } // // Turn OFF all LEDs // void ALLOFF() { GPIO_PinWrite(GPIO1, LED1, 0U);
// LED1 OFF
GPIO_PinWrite(GPIO1, LED2, 0U);
// LED2 OFF
GPIO_PinWrite(GPIO1, LED3, 0U);
// LED3 OFF
GPIO_PinWrite(GPIO1, LED4, 0U);
// LED4 OFF
GPIO_PinWrite(GPIO1, LED5, 0U);
// LED5 OFF
GPIO_PinWrite(GPIO1, LED6, 0U);
// LED6 OFF
GPIO_PinWrite(GPIO1, LED7, 0U);
// LED7 OFF
GPIO_PinWrite(GPIO1, LED8, 0U);
// LED8 OFF
} //
● 73
Getting Started with NXP i.MX Development Board - UK.indd 73
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
// Only 1 LED ON for 250 ms // void OnlyOne(int ThisLED) { GPIO_PinWrite(GPIO1, ThisLED, 1U); SysTick_DelayTicks(250); GPIO_PinWrite(GPIO1, ThisLED, 0U); } /* Main function */ int main(void) { /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } ALLOFF();
// All LEDs OFF
while (1)
// DO FOREVER
{ OnlyOne(LED1);
// LED1 ON
OnlyOne(LED2);
// LED2 ON
OnlyOne(LED3);
// LED3 ON
OnlyOne(LED4);
// LED4 ON
OnlyOne(LED5);
// LED5 ON
OnlyOne(LED6);
// LED6 ON
OnlyOne(LED7);
// LED7 ON
OnlyOne(LED8);
// LED8 ON
} }
Figure 4.18 Program listing
● 74
Getting Started with NXP i.MX Development Board - UK.indd 74
26-10-2023 09:34
Chapter 4 • LED Projects
Figure 4.19 Project built on a breadboard
4.5.1 More efficient program The program given in Figure 4.18 can be made simpler and more efficient by using an array to store the port numbers, and then using this array to control the LEDs. In this modified version (chasing-2) of the program (Figure 4.20), the port numbers are stored in an array called LEDS[] as follows: static uint32_t LEDS[8] = {19U, 18U, 17U, 20U, 1U, 2U, 15U, 16U}; Function ALLOFF() turns OFF all LEDs and it is simplified as follows: void ALLOFF() { for(int i = 0; i < 8; i++) GPIO_PinWrite(GPIO1, LEDS[i], 0U); }
The main program that controls the LEDs is much simpler and efficient and is as follows: while (1) { for(int j = 0; j < 8; j++) {
GPIO_PinWrite(GPIO1, LEDS[j], 1U);
// LED[j] ON
SysTick_DelayTicks(500);
// 500 ms delay
GPIO_PinWrite(GPIO1, LEDS[j], 0U);
// LED[j] OFF
} }
/* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause *
● 75
Getting Started with NXP i.MX Development Board - UK.indd 75
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
*Chasing 8 LEDs - 2*/
#include "pin_mux.h" #include "clock_config.h" #include "board.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; static uint32_t LEDS[8] = {19U,18U,17U,20U,1U,2U,15U,16U}; /******************************************************************************* * Code ******************************************************************************/ void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } } // // Turn OFF all LEDs // void ALLOFF() {
● 76
Getting Started with NXP i.MX Development Board - UK.indd 76
26-10-2023 09:34
Chapter 4 • LED Projects
for(int i = 0; i < 8; i++) GPIO_PinWrite(GPIO1, LEDS[i], 0U); } /* Main function */ int main(void) { /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } ALLOFF();
// All LEDs OFF
while (1)
// DO FOREVER
{ for(int j = 0; j < 8; j++) {
GPIO_PinWrite(GPIO1, LEDS[j], 1U);
// LED[j] ON
SysTick_DelayTicks(500);
// 500 ms delay
GPIO_PinWrite(GPIO1, LEDS[j], 0U);
// LED[j] OFF
} } }
Figure 4.20 Modified program
4.5.2 Using PortClear and PortSet functions The program in Figure 4.20 can further be improved by using the PortClear and PortSet functions. These functions apply to multiple I/O pins. Function PortClear is used to clear more than one port in multiple I/Os. Similarly, PortSet is used to set more than one port in multiple I/Os. Figure 4.21 shows the modified program (chasing-3). These functions require the ports to be set or cleared to be specified in a 32-bit mask. Setting a bit in the mask clears or sets that I/O port. For example, to set ports 1, 2, and 3 the mask should be: 0000 0000 0000 0000 0000 0000 0000 1110 i.e. hexadecimal number 0x0E.
● 77
Getting Started with NXP i.MX Development Board - UK.indd 77
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
In the program in Figure 4.21, all LEDs are cleared in function ALLOFF() using the following function: void ALLOFF() { uint32_t k = pow(2, 0x1F8006); GPIO_PortClear(GPIO1, k); }
The mask 0x1F8006 corresponds to the bit pattern: 0000 0000 0001 1111 1000 0000 0000 0110 where the following bit positions are set to 1: 1, 2, 15, 16, 17, 18, 19, 20 The built-in function pow() calculates the power of 2. Here, 2k is calculated based on the bits to be set or cleared in the mask. Inside the main program, the LEDs are turned ON and OFF using the following statements: while (1) { for(int j = 0; j < 8; j++) {
uint32_t k = pow(2, LEDS[j]);
GPIO_PortSet(GPIO1, k);
SysTick_DelayTicks(250);
GPIO_PortClear(GPIO1, k);
} }
The for-loop runs from 0 to 8 and extracts the port numbers from array LEDS[]. The bit positions of these numbers are calculated in the mask using the pow() function. The LEDs are then turned ON and OFF as required to give the chasing effect. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *Chasing 8 LEDs - 3*/
#include "pin_mux.h" #include "clock_config.h" #include "board.h"
● 78
Getting Started with NXP i.MX Development Board - UK.indd 78
26-10-2023 09:34
Chapter 4 • LED Projects
#include "math.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; static uint32_t LEDS[8] = {19U,18U,17U,20U,1U,2U,15U,16U}; /******************************************************************************* * Code ******************************************************************************/ void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } } // // Turn OFF all LEDs // void ALLOFF() { uint32_t k = pow(2, 0x1F8006); GPIO_PortClear(GPIO1, k); } /* Main function */
● 79
Getting Started with NXP i.MX Development Board - UK.indd 79
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
int main(void) { /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } ALLOFF();
// All LEDs OFF
while (1)
// DO FOREVER
{ for(int j = 0; j < 8; j++) {
uint32_t k = pow(2, LEDS[j]);
GPIO_PortSet(GPIO1, k);
SysTick_DelayTicks(250);
GPIO_PortClear(GPIO1, k);
} } }
Figure 4.21 Program listing
4.6 Project 5 – Binary counting LEDs Description: In this project, 8 LEDs are connected to the development board as in the previous project. As shown in Figure 4.22, the LEDs count up in binary with a 500 ms delay between each count. The aim of this project is to show how a group of port pins can be combined into a port and accessed at the same time.
● 80
Getting Started with NXP i.MX Development Board - UK.indd 80
26-10-2023 09:34
Chapter 4 • LED Projects
Figure 4.22 Binary counting LEDs The block diagram and circuit diagram are as in Figure 4.15 and Figure 4.16. Program listing: Figure 4.23 shows the program listing (counting-leds). As in the previous projects, the GPIO ports used must be configured as outputs using the ConfigTools. At the beginning of the program, variable count is set to 0. Function Display() combines the 8 port pins 16, 15, 2, 1, 20, 17, 18, and 19 into a single port group and accesses them at the same time. This function has two arguments: the width of the data, and the data to be sent to the port. The data width is set to 8 since there are 8 LEDs. For example, setting the function to Display(8, 5) sends number 5 (bit pattern 0000 0101) to the ports, i.e. the LEDs at ports 17 and 19 turn ON. The variable count is incremented by one, and the function Display() is called to turn ON the appropriate LEDs to display the number stored in count. When count reaches 255, it is reset to 0. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *Binary counting LEDs*/
#include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "math.h" /*******************************************************************************
● 81
Getting Started with NXP i.MX Development Board - UK.indd 81
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
* Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; static uint32_t LEDS[8] = {16U,15U,2U,1U,20U,17U,18U,19U}; int count = 0; /******************************************************************************* * Code ******************************************************************************/ void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } } // // Group the port pins together. L is the number of bits (8 here),and No // is the data to be displayed // void Display(int No, int L) { int i, m, j; m = L - 1; for(i = 0; i < L; i++) { j = 1; for(int k = 0; k < m; k++)j = j * 2;
● 82
Getting Started with NXP i.MX Development Board - UK.indd 82
26-10-2023 09:34
Chapter 4 • LED Projects
if((No & j) != 0) GPIO_PinWrite(GPIO1, LEDS[i], 1U); else GPIO_PinWrite(GPIO1, LEDS[i], 0U); m--; } }
/* Main function */ int main(void) { /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } Display(0, 8);
// All LEDs OFF
while (1)
// DO FOREVER
{ count++;
// Increment count
if(count > 255) count = 0;
// If count>255, set to 0
Display(count, 8);
// Display result
SysTick_DelayTicks(500);
// 500 ms delay
} }
Figure 4.23 Program listing
4.7 Project 6 – Random flashing LEDs Description: In this project 8 LEDs are connected to the development board as in the previous project. The LEDs flash randomly as if they are Christmas lights. The block diagram and circuit diagram are as in Figure 4.15 and Figure 4.16.
● 83
Getting Started with NXP i.MX Development Board - UK.indd 83
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Program listing: Figure 4.24 shows the program listing (random-leds). As in the previous projects, the GPIO ports used must be configured as outputs using the ConfigTools. The program is very similar to Figure 4.23. Here, an integer random number is generated between 0 and 255 and this number is used to flash the LEDs randomly every 100 ms. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *Lucky day*/
#include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "math.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; static uint32_t LEDS[8] = {16U,15U,2U,1U,20U,17U,18U,19U}; /******************************************************************************* * Code ******************************************************************************/ void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) {
● 84
Getting Started with NXP i.MX Development Board - UK.indd 84
26-10-2023 09:34
Chapter 4 • LED Projects
g_systickCounter = n; while (g_systickCounter != 0U) { } } // // Group the port pins together. L is the number of bits (8 here),and No // is the data to be displayed // void Display(int No, int L) { int i, m, j; m = L - 1; for(i = 0; i < L; i++) { j = 1; for(int k = 0; k < m; k++)j = j * 2; if((No & j) != 0) GPIO_PinWrite(GPIO1, LEDS[i], 1U); else GPIO_PinWrite(GPIO1, LEDS[i], 0U); m--; } }
/* Main function */ int main(void) { /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } srand(0);
// Random seed
● 85
Getting Started with NXP i.MX Development Board - UK.indd 85
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
while (1)
// DO FOREVER
{ int rnd = rand();
// Random int number
rnd = rnd % 255;
// Between 0 and 255
Display(rnd, 8);
// Display the number
SysTick_DelayTicks(100);
// 100 ms delay
} }
Figure 4.24 Program listing
4.8 Project 7 – Lucky day of the week Description: In this project, 7 LEDs are positioned in the form of a circle and are connected to the development kit. Each LED is assumed to represent a day of the week. Pressing an external button generates a random number between 1 and 7 and lights up only one of the LEDs. The day name corresponding to this LED is assumed to be your lucky day of the week. The aim of this project is to show how an external button can be used in a project. Block diagram: Figure 4.25 shows the block diagram of the project.
Figure 4.25 Block diagram of the project Circuit diagram: The circuit diagram is shown in Figure 4.26. It is similar to Figure 4.15, but here 7 LEDs are used together with an external button (called MyButton) connected to Arduino header J56, pin 2 (i.e. UART1_RXD, IO9).
● 86
Getting Started with NXP i.MX Development Board - UK.indd 86
26-10-2023 09:34
Chapter 4 • LED Projects
Figure 4.26 Circuit diagram of the project Program listing: As before, the LED ports must be configured as outputs. Also, the button at UART1_RXD must be configured as digital input and pulled up with 22 kΩ pull-up resistor using the menu option ConfigTools (Figure 4.27).
Figure 4.27 Configure the button as digital input Figure 4.28 shows the program listing (lucky-day). At the beginning of the program, MyButton is defined as 9 (i.e. port 9), and the random number generator seed MySeed is cleared to 0. Function Display() is used as in the previous project to turn ON/OFF the required LEDs. Inside the main program, a while loop is formed. Inside this loop, the button state is checked and if the button is not pressed (i.e. MyButton = 1) then the random number generator seed MySeed is incremented by 1. This is so that the generated number is random, and the same number is not generated every time the program is started. The while loop terminates when the button is pressed, and the resulting number is used as the seed for the random number generator. The generated random number is set to be between 0 and 7 and is then used to turn ON only one of the LEDs, which represents the
● 87
Getting Started with NXP i.MX Development Board - UK.indd 87
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
lucky day of the user. The program then terminates and can be restarted after pressing the Reset button. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *Lucky day*/
#include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "math.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; static uint32_t LEDS[8] = {16U,15U,2U,1U,20U,17U,18U,19U}; #define MyButton 9U uint32_t MySeed = 0; /******************************************************************************* * Code ******************************************************************************/ void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) {
● 88
Getting Started with NXP i.MX Development Board - UK.indd 88
26-10-2023 09:34
Chapter 4 • LED Projects
g_systickCounter = n; while (g_systickCounter != 0U) { } } // // Group the port pins together. L is the number of bits (8 here),and No // is the data to be displayed // void Display(int No, int L) { int i, m, j; m = L - 1; for(i = 0; i < L; i++) { j = 1; for(int k = 0; k < m; k++)j = j * 2; if((No & j) != 0) GPIO_PinWrite(GPIO1, LEDS[i], 1U); else GPIO_PinWrite(GPIO1, LEDS[i], 0U); m--; } }
/* Main function */ int main(void) { /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } while(GPIO_PinRead(GPIO1, MyButton) == 1)
// Wait until button
● 89
Getting Started with NXP i.MX Development Board - UK.indd 89
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
pressed { MySeed++;
// Increment seed
} srand(MySeed);
// Random seed
int rnd = rand();
// Random int number
rnd = rnd % 8;
// Between 0 and 7
int k = pow(2, rnd);
// As power of 2
Display(k, 8);
// Display the number
while(1);
// Wait here forever
}
Figure 4.28 Program listing Figure 4.29 shows the project built on a breadboard.
Figure 4.29 Project built on a breadboard
4.9 Project 8 – Binary up/down counter with LEDs Description: This project is similar to Project 5, but here the counting can be up or down controlled with an external button. Normally, the counting is up. Pressing the button counts down. While counting up, when the count reaches 255, it is reset to 0. Similarly, while counting down, when the count reaches 0 it is reset to 255. The block diagram and circuit diagram of the project are the same as in Figure 4.25 and Figure 4.26 respectively. Program listing: As in the previous project, you should configure the 8 LEDs as outputs and the button as input using the menu option ConfigTools. Figure 4.30 shows the program listing (led-up-down). Variable count is initialized to 0 at the beginning of the program. Inside the main program loop, the state of the button is checked. If the button is not pressed (MyButton = 1) then counting continues upwards.
● 90
Getting Started with NXP i.MX Development Board - UK.indd 90
26-10-2023 09:34
Chapter 4 • LED Projects
If, on the other hand, the button is pressed (MyButton = 0), then the counting continues downwards while the button is kept pressed. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *LED up/down counter*/
#include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "math.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; static uint32_t LEDS[8] = {16U,15U,2U,1U,20U,17U,18U,19U}; #define MyButton 9U int count = 0; /******************************************************************************* * Code ******************************************************************************/ void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) {
● 91
Getting Started with NXP i.MX Development Board - UK.indd 91
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
g_systickCounter = n; while (g_systickCounter != 0U) { } } // // Group the port pins together. L is the number of bits (8 here),and No // is the data to be displayed // void Display(int No, int L) { int i, m, j; m = L - 1; for(i = 0; i < L; i++) { j = 1; for(int k = 0; k < m; k++)j = j * 2; if((No & j) != 0) GPIO_PinWrite(GPIO1, LEDS[i], 1U); else GPIO_PinWrite(GPIO1, LEDS[i], 0U); m--; } }
/* Main function */ int main(void) { /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } }
● 92
Getting Started with NXP i.MX Development Board - UK.indd 92
26-10-2023 09:34
Chapter 4 • LED Projects
while(1)
// Do Forever
{ if(GPIO_PinRead(GPIO1, MyButton) == 1)
// If button not pressed
{
count++;
// Increment count
if(count == 256)count = 0;
// Back to 0
} else
// Button pressed
{
count--;
// Decrement count
if(count < 0)count = 255;
// Back to 255
} Display(count, 8);
// Display
SysTick_DelayTicks(250);
// Wait 250 ms
} }
Figure 4.30 Program listing
4.10 Project 9 – Binary event counter with LEDs Description: In this project, 8 LEDs and a button are used as in the previous project. Here, the button simulates the occurrence of external events. The count is incremented by 1 and displayed on the LEDs when the button is pressed. The block diagram and circuit diagram of the project are the same as in Figure 4.25 and Figure 4.26 respectively. Program listing: As in the previous project, you should configure the 8 LEDs as outputs and the button as input using the menu option ConfigTools. Figure 4.31 shows the program listing (led-event-counter). Variable count is initialized to 0 at the beginning of the program. The program checks the button MyButton and when it is pressed, the variable count is incremented by 1 and is displayed on the LEDs. The program waits until the button is released and also a 50 ms delay is introduced to eliminate any contact bouncing problems. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *LED event counter*/
#include "pin_mux.h"
● 93
Getting Started with NXP i.MX Development Board - UK.indd 93
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
#include "clock_config.h" #include "board.h" #include "math.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; static uint32_t LEDS[8] = {16U,15U,2U,1U,20U,17U,18U,19U}; #define MyButton 9U int count = 0; /******************************************************************************* * Code ******************************************************************************/ void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } } // // Group the port pins together. L is the number of bits (8 here),and No // is the data to be displayed // void Display(int No, int L) { int i, m, j;
● 94
Getting Started with NXP i.MX Development Board - UK.indd 94
26-10-2023 09:34
Chapter 4 • LED Projects
m = L - 1; for(i = 0; i < L; i++) { j = 1; for(int k = 0; k < m; k++)j = j * 2; if((No & j) != 0) GPIO_PinWrite(GPIO1, LEDS[i], 1U); else GPIO_PinWrite(GPIO1, LEDS[i], 0U); m--; } }
/* Main function */ int main(void) { /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } while(1)
// Do Forever
{ while(GPIO_PinRead(GPIO1, MyButton) == 1);
// Wait for press
count++;
// Increment count
Display(count, 8); while(GPIO_PinRead(GPIO1, MyButton) == 0);
// Wait for release
SysTick_DelayTicks(50);
// Contact debounce
} }
Figure 4.31 Program listing
● 95
Getting Started with NXP i.MX Development Board - UK.indd 95
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Chapter 5 • 7-Segment LED Displays 5.1 Overview In the previous chapter, you learned how to develop projects using LEDs only. In this chapter you will be using 7-segment LED displays in projects.
5.2 7-Segment LED display structure 7-segment LED displays are frequently used in electronic circuits to show numeric or alphanumeric values. As shown in Figure 5.1, a 7-segment LED display basically consists of 7 LEDs connected such that numbers from 0 to 9 and some letters can be displayed. Segments are identified by letters from a to g and Figure 5.2 shows the segment names of a typical 7-segment LED display.
Figure 5.1 Some 7-segment LED displays
Figure 5.2 Segment names of a 7-segment display Figure 5.3 shows how numbers from 0 to 9 can be obtained by turning ON different segments of the display.
● 96
Getting Started with NXP i.MX Development Board - UK.indd 96
26-10-2023 09:34
Chapter 5 • 7-Segment LED Displays
Figure 5.3 Displaying numbers 0–9 (courtesy of electronics-fun.com) 7-segment displays are available in two different configurations: common cathode and common anode. As shown in Figure 5.4, in common cathode configuration, all the cathodes of all segment LEDs are connected to ground. The segments are turned ON by applying a logic 1 to the required segment LED via current limiting resistors. In common cathode configuration, the 7-segment LED is connected to the microcontroller in current sourcing mode.
Figure 5.4 Common cathode 7-segment display In a common anode configuration, the anode terminals of all the LEDs are connected as shown in Figure 5.5. This common point is then normally connected to the supply voltage. A segment is turned ON by connecting its cathode terminal to logic 0 via a current limiting resistor. In common anode configuration, the 7-segment LED is connected to the microcontroller in current sinking mode.
Figure 5.5 Common cathode 7-segment display
● 97
Getting Started with NXP i.MX Development Board - UK.indd 97
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
5.3 Project 1 – 7-Segment 1-digit LED counter Description: In this project, you will use a 1-digit display to count from 0 to 9. Block diagram: Figure 5.6 shows the project block diagram.
Figure 5.6 Block diagram of the project Circuit diagram: The 7-segment display used in this project is type 5161AH, red common cathode type, with a digit height of 0.56 inches. Figure 5.7 shows the display structure with its connection diagram. This is a 10-pin display with pin 1 located as shown in the figure. Pin numbering is 1 to 5 going right from pin 1. Pins 6 to 10 are located at the top part of the display, with pin 6 at the top right-hand side. Notice where pin 1 is located.
Figure 5.7 The supplied display Figure 5.8 shows the display connected to the development board through 1 kΩ current limiting resistors. The common pins of the display are connected to GND.
● 98
Getting Started with NXP i.MX Development Board - UK.indd 98
26-10-2023 09:34
Chapter 5 • 7-Segment LED Displays
Figure 5.8 Circuit diagram of the project The connections between the ports and the 7-segment LED pins are as follows: 7-segment LED pin a (pin 7) b (pin 6) c (pin 4) d (pin 2) e (pin 1) f (pin 9) g (pin 10) common (pins 3,8)
Port pin J57, pin 6 57, pin 8 J57 pin 10 J57 pin 12 J57, pin 18 J57, pin 20 J56, pin 14 J60, pin 12
Before using a 7-segment display, you have to map the pins to the MCU ports. Table 5.1 shows the number to be displayed and the data to be sent to the port pins. Notice that the MSB bit (shown as x in the table) is not used and is set to 0. Number
x g f e d c b a
Port data in Hex
0
0 0 1 1 1 1 1 1
0x3F
1
0 0 0 0 0 1 1 0
0x06
2
0 1 0 1 1 0 1 1
0x5B
3
0 1 0 0 1 1 1 1
0x4F
4
0 1 1 0 0 1 1 0
0x66
5
0 1 1 0 1 1 0 1
0x6D
6
0 1 1 1 1 1 0 1
0x7D
7
0 0 0 0 0 1 1 1
0x07
8
0 1 1 1 1 1 1 1
0x7F
9
0 1 1 0 1 1 1 1
0x6F
Table 5.1 Number to be displayed and port data
● 99
Getting Started with NXP i.MX Development Board - UK.indd 99
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Program listing: Figure 5.9 shows the program listing (sevenseg-1). Array LEDS[] stores the pin numbers corresponding to the connections between the display and the development board ports. Array SEG stores the mapping between the numbers and the data to be sent to display a number, as shown in Table 5.1. As in the previous LED-based projects, you should configure the LED ports as outputs using the menu option ConfigTools. Function Display() groups a number of port pins and sends data to the group as described in earlier multiple-LED projects. Here, 8 port pins are grouped together (MSB bit is not used) and hexadecimal byte data is sent to the group as shown in Table 5.1. Variable Count stores the number to be sent to the display. The program displays numbers 0 to 9 continuously with one-second delay between each display. Notice that Count is rest to 0 when it reaches 10. Figure 5.10 shows the project built on a breadboard. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *7-segment 1 digit*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; static uint32_t LEDS[8] = {0,15U,2U,1U,20U,17U,18U,19U};
// Only 7 used
static unsigned char SEG[] = {0x3F,0x06,0x5B,0x4F,0x66,
// See Table 5.1
0x6D, 0x7D, 0x07, 0x7F, 0x6F}; int Count = 0; /******************************************************************************* * Code ******************************************************************************/ void SysTick_Handler(void) {
● 100
Getting Started with NXP i.MX Development Board - UK.indd 100
26-10-2023 09:34
Chapter 5 • 7-Segment LED Displays
if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } }
// // Group the port pins together. L is the number of bits (8 here),and No // is the data to be displayed // void Display(int No, int L) { int i, m, j; m = L - 1; for(i = 0; i < L; i++) { j = 1; for(int k = 0; k < m; k++)j = j * 2; if((No & j) != 0) GPIO_PinWrite(GPIO1,LEDS[i], 1U); else GPIO_PinWrite(GPIO1,LEDS[i], 0U); m--; } }
/* Main function */ int main(void) { BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */
● 101
Getting Started with NXP i.MX Development Board - UK.indd 101
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } while(1)
// Do forever
{ unsigned char Pattern = SEG[Count];
// Get number to send to
Display(Pattern, 8);
// Display the number
Count++;
// Increment Count
if(Count == 10) Count = 0;
// If Count=10, set to 0
SysTick_DelayTicks(1000);
// 1 second delay
} }
Figure 5.9 Program listing
Figure 5.10 Project built on a breadboard Suggestion Note that the program can be made more readable if you create a function to display the required number and then call this function from the main program.
5.4 Project 2 – 7-Segment 4-digit multiplexed LED display Project Description This project is similar to the previous one, but here a multiplexed four-digit 7-segment display is used instead of one digit. The program displays the number 5346 on the 7-segment display. Block diagram: Figure 5.11 shows the project block diagram.
● 102
Getting Started with NXP i.MX Development Board - UK.indd 102
26-10-2023 09:34
Chapter 5 • 7-Segment LED Displays
Figure 5.11 Block diagram of the project Circuit diagram: The 7-segment 4-digit display used in this project is red color common cathode type, having digit heights of 0.36 inches. Figure 5.12 shows the display structure with its connection diagram. The display features one decimal point per digit. The LEDs have a forward voltage of 1.8 VDC and a max forward current of 30 mA. The hardware interface is twelve (two rows of six) through-hole pins. Pin 1 is located as shown in the figure. Pins 1 to 6 are at the bottom, and pins 7 to 12 are at the top, with pin 7 being at the top right-hand side. Corresponding a-g pins of each digit are connected. Digit 1 is the leftmost digit.
Figure 5.12 The supplied display The individual digits of multiplexed 7-segment displays are normally enabled using transistors, as shown in Figure 5.13. This is because the current sourcing/sinking capabilities of the MCU may not be enough to turn ON the required segments. In general, any type NPN
● 103
Getting Started with NXP i.MX Development Board - UK.indd 103
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
transistor can be used, but in his project BC337 type small NPN transistors were used.
Figure 5.13 Enabling digits using transistors Figure 5.14 shows the 7-segment display connected to the GPIO ports of the development kit. A display segment is turned on when logic 1 is applied to it and when its digit is enabled by setting the port pin LOW.
Figure 5.14 Circuit diagram of the project The connections between the ports and the 7-segment LED pins are as follows (see also Table 3.1 to Table 3.4): 7-segment LED pin a (pin 11) b (pin 7) c (pin 4) d (pin 2) e (pin 1) f (pin 10) g (pin 5)
Port pin J57, pin 6 J57, pin 8 J57, pin 10 J57, pin 12 J57, pin 18 J57, pin 20 J56, pin 14
Port number 19 18 17 20 1 2 15
● 104
Getting Started with NXP i.MX Development Board - UK.indd 104
26-10-2023 09:34
Chapter 5 • 7-Segment LED Displays
DIG1 DIG2 DIG3 DIG4
(pin 12) (pin 9) (pin 8) (pin 6)
J56, pin 16 J56, pin 2 J56, pin 4 J26, pin 8
16 9 10 28
Program Listing: By displaying each digit for several milliseconds, the eye cannot differentiate that the digits are not ON all the time. This way, you can multiplex any number of 7-segment displays together. For example, to display number 5346, you have to send 5 to the first digit and enable its common pin. After a few milliseconds, number 3 is sent to the second digit and the common point of the second digit is enabled, and so on. When this process is repeated continuously the user sees as if both displays are ON continuously. As in the previous projects using LEDs, the port pins used must be configured as outputs using menu option ConfigTools (Figure 5.15). Figure 5.16 shows the program listing (sevenseg-4digit). Array LEDS[] stores the LED segment port assignments, array SEG[] stores the bit pattern to turn on a digit, and array DIGITS[] stores the digit port assignments where transistors are used. The program is similar to the one with one digit. Here, number 5 is sent to digit 1 (MSD) and DIG1 (DIGITS[0]) is enabled for about 5 milliseconds. Then, DIG1 is disabled and number 3 is sent to digit 2 and DIG2 (DIGITS[1]) is enabled for about 5 milliseconds. Then, DIG2 is disabled and number 4 is sent to the third digit and DIG3 (DIGITS[2]) is enabled for about 5 seconds. Finally, DIG3 is disabled and number 6 is sent to the last digit (LSD) and DIG4 (DIGITS[3]) is enabled for about 5 seconds. This process is repeated after disabling DIG4, thus displaying number 5346 on the 7-segment display as shown in Figure 5.17. Note: You might have to increase the Drive Strength of some of the digit ports in ConfigTools to have a display with equal brightness digits.
Figure 5.15 Configure ports as outputs /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *7-segment 4 digit - display 5346*/
● 105
Getting Started with NXP i.MX Development Board - UK.indd 105
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
#include "pin_mux.h" #include "clock_config.h" #include "board.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; static uint32_t LEDS[8] = {0,15U,2U,1U,20U,17U,18U,19U};
// Only 7 used
static unsigned char SEG[] = {0x3F,0x06,0x5B,0x4F,0x66,
// See Table 5.1
0x6D, 0x7D, 0x07, 0x7F, 0x6F}; static unsigned char DIGITS[] = {16U, 9U, 10U, 28U};
// DIGIT ports
static unsigned int Cnt = 5346;
// Number to
display int D1, D2, D3, D4, D5, D6; unsigned char Pattern; /******************************************************************************* * Code ******************************************************************************/ void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } }
● 106
Getting Started with NXP i.MX Development Board - UK.indd 106
26-10-2023 09:34
Chapter 5 • 7-Segment LED Displays
// // Group the port pins together. L is the number of bits (8 here),and No // is the data to be displayed // void Display(int No, int L) { int i, m, j; m = L - 1; for(i = 0; i < L; i++) { j = 1; for(int k = 0; k < m; k++)j = j * 2; if((No & j) != 0) GPIO_PinWrite(GPIO1,LEDS[i], 1U); else GPIO_PinWrite(GPIO1,LEDS[i], 0U); m--; } }
/* Main function */ int main(void) { BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } while(1) { D1 = Cnt / 1000;
// 1000s
Pattern = SEG[D1];
// Get the bit pattern
digit
Display(Pattern, 8);
// Send to display
GPIO_PinWrite(GPIO1, DIGITS[0], 1U);
// Enable DIG1
SysTick_DelayTicks(5);
// Wait a while
● 107
Getting Started with NXP i.MX Development Board - UK.indd 107
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
GPIO_PinWrite(GPIO1, DIGITS[0], 0U);
// Disable DIG1
D2 = Cnt % 1000; D3 = D2 / 100;
// 100s digit
Pattern =
// Get the bit pattern
SEG[D3];
Display(Pattern, 8);
// Send to display
GPIO_PinWrite(GPIO1, DIGITS[1], 1U);
// Enable DIG2
SysTick_DelayTicks(5);
// Wait a while
GPIO_PinWrite(GPIO1, DIGITS[1], 0U);
// Disable DiG2
D4 = D2 % 100; D5 = D4/10;
// 10s digit
Pattern = SEG[D5];
// Get the bit pattern
Display(Pattern, 8);
// Send to display
GPIO_PinWrite(GPIO1, DIGITS[2], 1U);
// Enable DIG3
SysTick_DelayTicks(5);
// Wait a while
GPIO_PinWrite(GPIO1, DIGITS[2], 0U);
// Disable DIG3
D6 = D4 % 10;
// 1s digit
Pattern = SEG[D6];
// Get the bit pattern
Display(Pattern, 8);
// Send to display
GPIO_PinWrite(GPIO1, DIGITS[3], 1U);
// Enable DIG4
SysTick_DelayTicks(5);
// Wait a while
GPIO_PinWrite(GPIO1, DIGITS[3], 0U);
// Disable DIG4
} }
Figure 5.16 Program listing
Figure 5.17 Project constructed on a breadboard
● 108
Getting Started with NXP i.MX Development Board - UK.indd 108
26-10-2023 09:34
Chapter 5 • 7-Segment LED Displays
5.5 Project 3 – 7-Segment 4-digit multiplexed LED display counter – timer interrupts Why timer interrupts? In the previous example, you displayed a fixed number on the 7-segment display. This was not a real application, since we almost always want to display different numbers. For example, during counting, different numbers are displayed. The problem here is that the 7-segment display has to be refreshed nearly every a few milliseconds and the CPU cannot refresh the display and at the same time execute other user code as it requires multitasking. The solution is to refresh the display in a timer interrupt routine and carry out the normal user tasks in the main program. In this program, you will refresh the display in the timer interrupt service routine and then send data to the display in the main program. Description: In this project, the display will count up every second and display on the 7-segment display. A timer will be used to refresh the display every 5 milliseconds inside the timer interrupt service routine. The display counts up by 1 every second to up to 9999. It is then reset back to 0 where the counting continues. Block diagram and circuit diagram of the project are as in Figure 5.11 and 5.14 respectively. Program listing: As in the previous projects using LEDs, the port pins used must be configured as outputs using menu option ConfigTools (Figure 5.15). Figure 5.18 shows the program listing (sevenseg-counter). The main program loop is elementary. It simply increments variable cnt and waits for one second. The value of cnt is displayed inside the interrupt service routine. Inside the timer interrupt service routine, variable ms_counter is used to introduce the required delays to the main program code. This variable is incremented by one at every millisecond (i.e. at every timer interrupt). Function delay_ms() checks the value of ms_counter and waits until it equals the required delay in milliseconds. For example, calling the function as delay_ms(1000) will delay the main program loop by a second. Variable g_sysTickCounter is set to 5 at the beginning of the main program loop. It decrements by one every time a timer interrupt occurs (i.e. at every millisecond) and this variable is used to refresh the display every 5 milliseconds. The variable called flag is used inside the timer interrupt service routine, and it is used to determine which digit to enable. Initially, the flag is set to 0 and first digit is enabled. On the next entry to the interrupt service routine, flag is incremented by 1 and the second digit is enabled. While the first digit is disabled. This is done for all the four digits. Flag is cleared to zero at the end of the cycle.
● 109
Getting Started with NXP i.MX Development Board - UK.indd 109
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
/* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *7-segment 4 digit - Counter with timer interrupts*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; volatile uint32_t ms_counter = 0; static uint32_t LEDS[8] = {0,15U,2U,1U,20U,17U,18U,19U};
// Only 7 used
static unsigned char SEG[] = {0x3F,0x06,0x5B,0x4F,0x66,
// See Table 5.1
0x6D, 0x7D, 0x07, 0x7F, 0x6F}; static unsigned char DIGITS[] = {16U, 9U, 10U, 28U};
// DIGIT ports
unsigned int MSD, m, MID2, n, MID1, LSD; volatile int flag =
0, cnt = 0;
unsigned char Pattern; /******************************************************************************* * Code ******************************************************************************/ // // Group the port pins together. L is the number of bits (8 here),and No // is the data to be displayed // void Display(int No, int L) { int i, m, j; m = L - 1;
● 110
Getting Started with NXP i.MX Development Board - UK.indd 110
26-10-2023 09:34
Chapter 5 • 7-Segment LED Displays
for(i = 0; i < L; i++) { j = 1; for(int k = 0; k < m; k++)j = j * 2; if((No & j) != 0) GPIO_PinWrite(GPIO1,LEDS[i], 1U); else GPIO_PinWrite(GPIO1,LEDS[i], 0U); m--; } } // // Timer interrupt service routine. Refresh the display every 5 ms. // Also, increment the millisecond counter // void SysTick_Handler(void) { if (g_systickCounter != 0U) { ms_counter++;
// Increment ms
g_systickCounter--;
// Decrement 5 ms
} else {
g_systickCounter = 5; MSD = cnt / 1000;
// Every 5 ms // Get MSD
m = cnt % 1000; MID2 = m / 100;
// Get MID2
n = m % 100; MID1 = n / 10;
// Get MID1
LSD = n % 10;
// Get LSD
if(flag == 0) { GPIO_PinWrite(GPIO1, DIGITS[3], 0U);
// Disable DIG4
Pattern = SEG[MSD];
// Get pattern
Display(Pattern, 8);
// Display number
GPIO_PinWrite(GPIO1, DIGITS[0], 1U);
// Enable DIG1
flag++; } else if(flag == 1) { GPIO_PinWrite(GPIO1, DIGITS[0], 0U);
// Disable DIG1
Pattern = SEG[MID2];
// Get pattern
Display(Pattern, 8);
// Display number
GPIO_PinWrite(GPIO1, DIGITS[1], 1U);
// Enable DIG2
● 111
Getting Started with NXP i.MX Development Board - UK.indd 111
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
flag++; } else if(flag == 2) { GPIO_PinWrite(GPIO1, DIGITS[1], 0U);
// DIsable DIG2;
Pattern = SEG[MID1];
// Get pattern
Display(Pattern, 8);
// Display number
GPIO_PinWrite(GPIO1, DIGITS[2], 1U);
// Enable DIG3
flag++; } else if(flag == 3) { GPIO_PinWrite(GPIO1, DIGITS[2], 0U);
// Disable DIG3
Pattern = SEG[LSD];
// Get pattern
Display(Pattern, 8);
// Display number
GPIO_PinWrite(GPIO1, DIGITS[3], 1U);
// Enable DIG4
flag = 0; } } }
// // This function waits for the specified number of milliseconds // void delay_ms(uint32_t ms) { while(ms_counter != ms); ms_counter = 0; }
/* Main function */ int main(void) { BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { }
● 112
Getting Started with NXP i.MX Development Board - UK.indd 112
26-10-2023 09:34
Chapter 5 • 7-Segment LED Displays
} g_systickCounter = 5;
// Set to 5 ms
while(1) { cnt++;
// Increment count
if(cnt > 9999) cnt = 0;
// If 9999, reset to 0
delay_ms(1000);
// Wait 1 second
} }
Figure 5.18 Program listing
5.6 Project 4 – 7-Segment 4-digit multiplexed LED display counter – blanking leading zeroes Description: In the program in Figure 5.18 the leading digits are shown as 0 when the number is lower than these digits. For example, number 12 is displayed as 0012 and not as 12. This is not desirable in many applications. In this project, we disable the leading zeroes. Program listing: Figure 5.19 shows the modified program (sevenseg-counter-blank) where the leading 0s are disabled by disabling their digits. For example, digit 1 is enabled if the number to be displayed is greater than 999, digit 2 is enabled if the number is greater than 99 and so on. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *7-segment 4 digit - Counter with timer interrupts*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /*******************************************************************************
● 113
Getting Started with NXP i.MX Development Board - UK.indd 113
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
* Variables ******************************************************************************/ volatile uint32_t g_systickCounter; volatile uint32_t ms_counter = 0; static uint32_t LEDS[8] = {0,15U,2U,1U,20U,17U,18U,19U};
// Only 7 used
static unsigned char SEG[] = {0x3F,0x06,0x5B,0x4F,0x66,
// See Table 5.1
0x6D, 0x7D, 0x07, 0x7F, 0x6F}; static unsigned char DIGITS[] = {16U, 9U, 10U, 28U};
// DIGIT ports
unsigned int MSD, m, MID2, n, MID1, LSD; volatile int flag =
0, cnt = 0;
unsigned char Pattern; /******************************************************************************* * Code ******************************************************************************/ // // Group the port pins together. L is the number of bits (8 here),and No // is the data to be displayed // void Display(int No, int L) { int i, m, j; m = L - 1; for(i = 0; i < L; i++) { j = 1; for(int k = 0; k < m; k++)j = j * 2; if((No & j) != 0) GPIO_PinWrite(GPIO1,LEDS[i], 1U); else GPIO_PinWrite(GPIO1,LEDS[i], 0U); m--; } } // // Timer interrupt service routine. Refresh the display every 5 ms. // Also, increment the millisecond counter // void SysTick_Handler(void) { if (g_systickCounter != 0U) { ms_counter++;
// Increment ms
● 114
Getting Started with NXP i.MX Development Board - UK.indd 114
26-10-2023 09:34
Chapter 5 • 7-Segment LED Displays
g_systickCounter--;
// Decrement 5 ms
} else {
g_systickCounter = 5; MSD = cnt / 1000;
// Every 5 ms // Get MSD
m = cnt % 1000; MID2 = m / 100;
// Get MID2
n = m % 100; MID1 = n / 10;
// Get MID1
LSD = n % 10;
// Get LSD
if(flag == 0) { GPIO_PinWrite(GPIO1, DIGITS[3], 0U);
// Disable DIG4
Pattern = SEG[MSD];
// Get pattern
Display(Pattern, 8);
// Display number
if(cnt > 999)GPIO_PinWrite(GPIO1, DIGITS[0], 1U); flag++; } else if(flag == 1) { GPIO_PinWrite(GPIO1, DIGITS[0], 0U);
// Disable DIG1
Pattern = SEG[MID2];
// Get pattern
Display(Pattern, 8);
// Display number
if(cnt > 99)GPIO_PinWrite(GPIO1, DIGITS[1], 1U); flag++; } else if(flag == 2) { GPIO_PinWrite(GPIO1, DIGITS[1], 0U);
// DIsable DIG2;
Pattern = SEG[MID1];
// Get pattern
Display(Pattern, 8);
// Display number
if(cnt > 9)GPIO_PinWrite(GPIO1, DIGITS[2], 1U); flag++; } else if(flag == 3) { GPIO_PinWrite(GPIO1, DIGITS[2], 0U);
// Disable DIG3
Pattern = SEG[LSD];
// Get pattern
Display(Pattern, 8);
// Display number
GPIO_PinWrite(GPIO1, DIGITS[3], 1U);
// Enable DIG4
flag = 0; } } }
● 115
Getting Started with NXP i.MX Development Board - UK.indd 115
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
// // This function waits for the specified number of milliseconds // void delay_ms(uint32_t ms) { while(ms_counter != ms); ms_counter = 0; }
/* Main function */ int main(void) { BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } g_systickCounter = 5;
// Set to 5 ms
while(1) { cnt++;
// Increment count
if(cnt > 9999) cnt = 0;
// If 9999, reset to 0
delay_ms(1000);
// Wait 1 second
} }
Figure 5.19 Program listing
● 116
Getting Started with NXP i.MX Development Board - UK.indd 116
26-10-2023 09:34
Chapter 6 • Using Serial Communication
Chapter 6 • Using Serial Communication 6.1 Overview Serial communication is a simple means of sending data to long distances quickly and reliably. Serial communication can be done either in software or by using a UART chip. Using UART chip has the advantage that the communication can be very high speed. Error detection is also easily handled in UART-based systems. The most commonly used serial communication method is based on the RS-232 standard. In this standard, data is sent over a single line from a transmitting device to a receiving device in bit serial format at a pre-specified speed, also known as the baud rate, or the number of bits sent each second. Typical Baud rates are 4800, 9600, 19200, 38400, etc. RS-232 serial communication is a form of asynchronous data transmission where data is sent character by character using dedicated hardware. Each character is preceded with a Start bit, seven or eight data bits, an optional parity bit, and one or more stop bits. The most commonly used format is eight data bits, no parity bit and one stop bit. Therefore, a data frame consists of 10 bits. With a baud rate of 9600, we can transmit and receive 960 characters every second. The least significant data bit is transmitted first, and the most significant bit is transmitted last. In standard RS-232 communication, a logic high is defined to be at -12 V, and a logic 0 is at +12 V. Figure 11.1 shows how character 'A' (ASCII binary pattern 0010 0001) is transmitted over a serial line. The line is normally idle at -12 V. The start bit is first sent by the line going from high to low. Then eight data bits are sent starting from the least significant bit. Finally, the stop bit is sent by raising the line from low to high.
Figure 6.1 Sending character 'A' in serial format In serial connection, a minimum of three lines are used for communication: transmit (TX), receive (RX), and ground (GND). Some high-speed serial communication systems use additional control signals for synchronization, such as CTS, DTR, and so on. Some systems use software synchronization techniques where a special character (XOFF) is used to tell the sender to stop sending, and another character (XON) is used to tell the sender to restart transmission. RS-232 devices are connected to each other using two types of connectors: 9-way connectors, and 25-way connectors. Table 6.1 shows the TX, RX, and GND pins of each types of connectors. The connectors used in RS-232 serial communication are shown in Figure 6.2.
● 117
Getting Started with NXP i.MX Development Board - UK.indd 117
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
9-pin connector Pin
Function
2
Transmit (TX)
3
Receive (RX)
5
Ground (GND)
25-pin connector Pin
Function
2
Transmit (TX)
3
Receive (RX)
7
Ground (GND)
Table 6.1 Minimum pins required for RS-232 serial communication
Figure 6.2 RS-232 connectors As described above, RS-232 voltage levels are at ±12 V. On the other hand, microcontroller input-output ports operate at 0 to +5V voltage levels. It is therefore necessary to translate the voltage levels before a microcontroller can be connected to a RS-232 compatible device. Thus, the output signal from the microcontroller has to be converted into ±12 V, and the input from an RS-232 device must be converted into 0 to +5V before it can be connected to a microcontroller. This voltage translation is normally done using special RS232 voltage converter chips. One such popular chip is the MAX232. This is a dual converter chip having the pin configuration as shown in Figure 6.3. This particular device requires four external 1 μF capacitors for its operation.
● 118
Getting Started with NXP i.MX Development Board - UK.indd 118
26-10-2023 09:34
Chapter 6 • Using Serial Communication
Figure 6.3 MAX232 pin configuration Nowadays, serial communication is done using standard TTL logic levels instead of ±12 V, where logic 1 is +5 V (or greater than +3 V) and logic 0 is 0 V. A serial line is idle when the voltage is at +5 V. The start bit is identified on the high-to-low transition of the line, i.e. the transition from +5 V to 0 V.
6.2 Project 1 – Serial communication between the MIMXRT1010-EVK Development Kit and an Arduino UNO Description: In this project, a MIMXRT1010-EVK development kit (named the GENERATOR) and an Arduino UNO (named the SQUARER) are connected to each other via serial communication lines. The GENERATOR sends numbers to the SQUARER. The square of these numbers is taken by SQUARER and then sent back to the GENERATOR where they are displayed on the Console. Block diagram: Figure 6.4 shows the block diagram of the project.
Figure 6.4 Block diagram of the project
● 119
Getting Started with NXP i.MX Development Board - UK.indd 119
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Circuit diagram: The circuit diagram of the project is shown in Figure 6.5. In this project, serial output LPUART4 (TX, GPIO_AD_02) and serial input (RX, GPIO_AD_01) of the GENERATOR are connected to inputs 2 and 3 of the Arduino UNO (SQUARER). Pin 3 and pin 2 of the Arduino UNO are software configured as serial TX and RX pins, respectively. Additionally, the GND pins of both development kits are connected. Notice that a resistive voltage divider circuit is used to lower the Arduino UNO output voltage to +3.3 V.
Figure 6.5 Circuit diagram of the project Program listing: Before writing the program, the serial lines TX and RX of LPUART4 on the MIMXRT1010-EVK development kit must be configured as shown in Figure 6.6
Figure 6.6 Configure the LPUART4 Figure 6.7 shows the Arduino UNO program listing (Program: UART). This is a basic serial communication program. Inside the setup() function, the software serial port baud rate is set to 115200. Inside the main program loop the program reads numbers from the serial port, calculates the squares of these numbers and then sends them to UART to the MIMXRT1010-EVK development board. /************************************************************************ *
UART COMMUNICATIONS
*
===================
* * This program receives numbers from the development kit, takes * the square of these numbers and sends them back to the development kit
● 120
Getting Started with NXP i.MX Development Board - UK.indd 120
26-10-2023 09:34
Chapter 6 • Using Serial Communication
* * Program: UART * Date
: September, 2023
* Author : Dogan Ibrahim ************************************************************************/ #include "SoftwareSerial.h" SoftwareSerial MySerial(2, 3); void setup() { MySerial.begin(115200); } void loop() { while(MySerial.available() > 0)
// If data available
{ unsigned int dat = MySerial.read();
// Read data
dat = dat * dat; MySerial.print(dat); } }
Figure 6.7 Arduino UNO program Make sure that lpuart is included in the project using the SDK Manager. Figure 6.8 shows the MIMXRT1010-EVK program listing (GENERATOR). At the beginning of the program, UART LPUART4 is defined. Inside the main program loop 3 numbers are generated (1, 2, 3) and sent to the Arduino UNO over the serial link. The received numbers are displayed on the Console as shown in Figure 6.9. Note that the program must run in the Debug mode so that the data can be displayed on the Console. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *GENERATOR*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "fsl_debug_console.h" #include "fsl_lpuart.h"
● 121
Getting Started with NXP i.MX Development Board - UK.indd 121
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
/******************************************************************************* * Definitions ******************************************************************************/ #define MyUart
LPUART4
#define MyUart_CLK_FREQ BOARD_DebugConsoleSrcFreq() /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ /******************************************************************************* * Code ******************************************************************************/ int main(void) { PRINTF("BEGINNING\n\r"); uint8_t wr, rd; lpuart_config_t config; BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole();
/*config.baudRate_Bps = 115200U; config.parityMode = kLPUART_ParityDisabled; config.stopBitCount = kLPUART_OneStopBit; config.txFifoWatermark = 0; config.rxFifoWatermark = 0; config.enableTx = false; config.enableRx = false; */ LPUART_GetDefaultConfig(&config); config.baudRate_Bps = BOARD_DEBUG_UART_BAUDRATE; config.enableTx
= true;
config.enableRx
= true;
LPUART_Init(MyUart, &config, MyUart_CLK_FREQ); for(wr = 1; wr < 4; wr++) { PRINTF("Sent: %d
", wr);
// Display Sent:
LPUART_WriteBlocking(MyUart, &wr, 1);
// Send a number
● 122
Getting Started with NXP i.MX Development Board - UK.indd 122
26-10-2023 09:34
Chapter 6 • Using Serial Communication
LPUART_ReadBlocking(MyUart, &rd, 1);
// Receive its square
PRINTF("Received: %c\n\r",rd);
// Display the number
} while(1); }
Figure 6.8 Program listing
Figure 6.9 Data displayed on the Console
● 123
Getting Started with NXP i.MX Development Board - UK.indd 123
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Chapter 7 • I²C Bus Interface 7.1 Overview The I²C (or I2C, or Inter Integrated Circuit) bus is commonly used in microcontroller-based projects. This is a hardware specification and protocol developed by the semiconductor division of Philips back in 1982. The aim of this chapter is to make the reader familiar with the I²C bus library functions and to show how they can be used in a real project. Before looking at the details of the project, it is worthwhile to look at the basic principles of the I²C bus.
7.2 The I²C Bus I²C bus is one of the most commonly used microcontroller communication protocols for communicating with external devices such as sensors and actuators. The I²C bus is a single master, multiple slave, half-duplex, single-ended,8-bit bus, and it can operate at standard mode: 100 Kbit/s, full speed: 400 Kbit/s, fast mode: 1 Mbit/s, and high speed: 3.2 Mbit/s. The bus consists of two open-drain signals, pulled up with resistors. The values of these resistors depend on the bus capacitance and the transmission speed. SDA: data line SCL: clock line Figure 7.1 shows the structure of an I²C bus with one master and three slaves.
Figure 7.1 I²C bus with one master and three slaves Because the I²C bus is based on just two wires, there should be a way to address an individual slave device on the same bus. For this reason, the protocol defines that each slave device provides a unique slave address for the given bus. This address is usually 7-bits wide. When the bus is free, both lines are HIGH. All communication on the bus is initiated and completed by the master, which initially sends a START bit, and completes a transaction by sending a STOP bit. This alerts all the slaves that some data is coming on the bus, and all the slaves listen on the bus. After the start bit, a 7-bit slave address is sent. Each slave device on the bus has its own address, and this ensures that only the addressed slave communicates on the bus at any time to avoid any collisions. The last sent bit is a read/ write bit such that if this bit is 0, it means that the master wishes to write to the bus (e.g. to a register of a slave), if this bit is a 1, it means that the master wishes to read from the bus (e.g. from the register of a slave). The data is sent on the bus with the MSB bit first.
● 124
Getting Started with NXP i.MX Development Board - UK.indd 124
26-10-2023 09:34
Chapter 7 • I²C Bus Interface
An acknowledgement (ACK) bit takes place after every byte. This bit allows the receiver to signal the transmitter that the byte was received successfully and that another byte may be sent. ACK bit is sent at the 9th clock pulse. The communication over the I²C bus is simply as follows: • The master sends on the bus the address of the slave it wants to communicate with • The LSB is the R/W bit which establishes the direction of data transmission, i.e. from mater to slave (R/W = 0), or from slave to master (R/W = 1) • Required bytes are sent, each interleaved with an ACK bit, until a stop condition occurs Depending on the type of slave device used, some operations may require more than one transaction. For example, the steps to read data from an I²C compatible memory device are: • Master starts the transaction in write mode (R/W = 0) by sending the slave address on the bus • The memory location to be retrieved is then sent as two bytes (assuming 64 Kbit memory) • The master sends a STOP condition to end the transaction • The master starts a new transaction in read mode (R/W = 1) by sending the slave address on the bus • The master reads the data from the memory. If reading the memory in sequential format then more than one byte will be read. • The master sets a stop condition on the bus
7.3 Project 1 – Port expander Description: A simple project is given in this section to show how the I²C functions can be used in a program. In this project, the I²C bus compatible Port Expander chip (MCP23017) is used to add an additional 16 I/O ports to your development kit. This is useful in some applications where many I/O ports may be required. In this simple project, an LED is connected to MCP23017 port pin GPA0 (pin 21) and the LED is flashed ON and OFF every second. A 1K current limiting resistor is used in series with the LED. Block diagram: The block diagram of the project is shown in Figure 7.2.
● 125
Getting Started with NXP i.MX Development Board - UK.indd 125
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Figure 7.2 Block diagram of the project The MCP23017 The MCP23017 is a 28-pin chip with the following features. The pin configuration is shown in Figure 7.3: • 16 bidirectional I/O ports, up to 1.7 MHz operation on I²C bus. • Interrupt capability • External reset input • Low standby current • +1.8 to +5.5 V operation • 3 address pins so that up to 8 devices can be used on the I²C bus • 28-pin DIL package
Figure 7.3 Pin configuration of the MCP23017
● 126
Getting Started with NXP i.MX Development Board - UK.indd 126
26-10-2023 09:34
Chapter 7 • I²C Bus Interface
The pin descriptions are shown in Table 7.1. Pin
Description
GPA0-GPA7
Port A pins
GPB0-GPB7
Port B pins
VDD
Power supply
VSS
Ground
SDA
I²C data pin
SCL
I²C clock pin
RESET
Reset pin
A0-A2
I²C address pins
Table 7.1 MCP23017 pin descriptions The MCP23017 is addressed using pins A0 to A2. Table 7.2 shows the address selection. In this project, the address pins are connected to ground, thus the address of the chip is 0x20. The chip address is 7 bits wide, with the low bit is set or cleared depending on whether we wish to read data from the chip or write data to the chip respectively. Since in this project, you will be writing to the MCP23017, the low bit should be 0, making the chip byte address (also called the device opcode) as 0x40. A2
A1
A0
Address
0
0
0
0x40
0
0
1
0x21
0
1
0
0x22
0
1
1
0x23
1
0
0
0x24
1
0
1
0x25
1
1
0
0x26
1
1
1
0x27
Table 7.2 Address selection of the MCP23017 The MCP23017 chip has 8 internal registers that can be configured for its operation. The device can either be operated in one 16-bit bank mode or in two 8-bit banks mode by configuring bit IOCON.BANK. On power-up, this bit is cleared which chooses the 8-bit bank mode by default. The I/O direction of the port pins are controlled with registers IODIRA (at address 0x00) and IODIRB (at address 0x01). Clearing a bit to 0 in these registers makes the corresponding port pin(s) as output(s). Similarly, setting a bit to 1 in these registers makes the corresponding port pin(s) input(s). GPIOA and GPIOB register addresses are 0x12 and 0x13, respectively. This is shown in Figure 7.4.
● 127
Getting Started with NXP i.MX Development Board - UK.indd 127
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Figure 7.4 Configuring the I/O ports Circuit diagram: Figure 7.5 shows the circuit diagram of the project. Notice that the I²C SDA and SCL pins of the port expander are connected to Arduino header pins J57 (pin 18) and J57 (pin 20) of the development kit. These pins are internally pulled up by 4.7 kΩ resistors on the development kit, and therefore there is no need to use any external pull-up resistors on the I²C bus. The LED is connected to port pin GPA0 of the MCP23017 (pin 21). The address select bits of the MCP23017 are all connected to ground.
Figure 7.5 Circuit diagram of the project Figure 7.6 shows the circuit built on a breadboard. Connections to the development kit were made using jumper wires.
Figure 7.6 Circuit built on a breadboard
● 128
Getting Started with NXP i.MX Development Board - UK.indd 128
26-10-2023 09:34
Chapter 7 • I²C Bus Interface
More information on the MCP23017 chip can be obtained from the data sheet: http://docs-europe.electrocomponents.com/webdocs/137e/0900766b8137eed4.pdf Configuring the MCUXpresso: Before writing the program, the MCUXpresso IDE must be configured correctly. The steps are given below. • Configure Arduino header J57 (pin 18, GPIO1) and J57 (pin 20, GPIO2) as SDA and SCL ports respectively. Click ConfigTools followed by Pins • Click chip pin number 11 at the left (Figure 7.7) and then click to select I²C_ SCL, click Done • Click chip pin number 12 at the left and then click to select I²C_SDA, click Done
Figure 7.7 Configure chip pins 11 and 12 • In window Routing Details, set (Figure 7.8): Software Input On: Enabled Drive strength: R0/6 Pull/Keeper select: Keeper Pull/Keeper enable: Enabled Pull up/Down Config: No init
Figure 7.8 Configure routing details • Click ConfigTools and click Peripherals • Enable Peripherals (Figure 7.9)
● 129
Getting Started with NXP i.MX Development Board - UK.indd 129
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Figure 7.9 Enable Peripherals • Click at the left Peripheral drivers (Device specific) at the left (Figure 7.10)
Figure 7.10 Select Peripherals • Click to select LPI² C1. You should see a window similar to Figure 7.11. Make sure that LPI² C1 is selected as the Master and the clock field is not blank. Leave the other fields as they are
Figure 7.11 Peripheral drivers
● 130
Getting Started with NXP i.MX Development Board - UK.indd 130
26-10-2023 09:34
Chapter 7 • I²C Bus Interface
• Click ConfigTools followed by Clocks and make sure that clock is assigned to port LPI²C_CLK_ROOT • Click Update Code followed by OK The MCUXpresso is now configured correctly, and you are ready to write the program. Program listing: The program listing is shown in Figure 7.12 (PortExpander). You must include the I²C from Manage SDK Components before compiling your project (see Chapter 7 which describes how to use the Manage SDK Components tool). At the beginning of the program, the used library header files are included. MCP2307 direction register (IODIRA) and GPIO register (MCP_GPIOA) are assigned to 0x0 and 0x12 respectively. Then, various I²C based definitions and the MCP2307 slave address (shifted right and given as 0x20) are given. The system timer is set to generate interrupts every millisecond so that 1-second delays can be used in the program. Function SEND () sends data to the MCP2307 device. This function has two arguments: register address and data. Function LPI²C_MasterStart () sends a Start bit and slave address on the I²C bus to the slave device (here the MCP2307). Functions LPI²C_MasterSend () send data bytes to the slave device whose address was sent on the bus by function LPI²C_MasterStart (). Arrays buff [0] and buff [1] store the MCP2307 register address and the data to be sent to this register, respectively. Here, only one byte of register address is specified with one byte of data. Function LPI²C_MasterStop () is finally called in function SEND () to terminate the transaction on the I²C bus. Inside the main program, various I²C parameters are configured. Notice that instead of defining each parameter, you could have set the default parameters by calling function LPI²C_MasterGetDefaultConfig (&masterConfig). Before entering the program loop, function SEND (IODIRA, 0xFE) configures bit 0 of PORT A as output. Then the program loop is formed using a while statement. Inside this loop, function SEND () is called to set bit 0 of the port to 0 and to 1 with one-second delay between each output. The result is that the LED is flashing every second. If you wish to run the program in debug mode, click Debug under Quickstart Panel, and then click Run → Resume to run the program. Alternatively, select to run in Release mode (Project → Build Configurations → Set Active → Release) and load the .axf file (or right click on .axf file followed by Binary Utilities → Create bin file to create the .bin file) to the target development kit. You should see the LED flashing every second. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *MCP23017 Port Expander*/
● 131
Getting Started with NXP i.MX Development Board - UK.indd 131
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
#include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "stdio.h" #include "fsl_debug_console.h" #include "fsl_lpi2c.h" #include "string.h"
/******************************************************************************* * Definitions ******************************************************************************/ #define IODIRA 0x00 #define MCP_GPIOA 0x12 #define EXAMPLE_I2C_MASTER_BASE (LPI2C1_BASE) /* Select USB1 PLL (480 MHz) as master lpi2c clock source */ #define LPI2C_CLOCK_SOURCE_SELECT (0U) /* Clock divider for master lpi2c clock source */ #define LPI2C_CLOCK_SOURCE_DIVIDER (5U) /* Get frequency of lpi2c clock */ #define LPI2C_CLOCK_FREQUENCY ((CLOCK_GetFreq(kCLOCK_Usb1PllClk)/8)/ (LPI2C_CLOCK_SOURCE_DIVIDER+1U)) #define LPI2C_MASTER_CLOCK_FREQUENCY LPI2C_CLOCK_FREQUENCY #define WAIT_TIME
10U
#define EXAMPLE_I2C_MASTER ((LPI2C_Type *)EXAMPLE_I2C_MASTER_BASE) #define LPI2C_MASTER_SLAVE_ADDR_7BIT 0x20U #define LPI2C_BAUDRATE
100000U
/******************************************************************************* * Prototypes ******************************************************************************/ void SysTick_DelayTicks(uint32_t); /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; /******************************************************************************* * Code
● 132
Getting Started with NXP i.MX Development Board - UK.indd 132
26-10-2023 09:34
Chapter 7 • I²C Bus Interface
******************************************************************************/ // // Timer interrupt service routine. Refresh the display every 5 ms. // Also, increment the millisecond counter // void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } }
// // This function sends register address and register data to MCP23017 chip // void SEND(uint32_t port, uint32_t data) { uint32_t buff[2]; status_t reVal = kStatus_Fail; buff[0]=port; // Address buff[1]=data; // Data if (kStatus_Success == LPI2C_MasterStart(EXAMPLE_I2C_MASTER, LPI2C_MASTER_ SLAVE_ADDR_7BIT, kLPI2C_Write)) { PRINTF("STARTED\n\r"); } reVal = LPI2C_MasterSend(EXAMPLE_I2C_MASTER, &buff[0], 1); if (reVal != kStatus_Success) {
PRINTF("ERROR IN SEND\n\r");
}
● 133
Getting Started with NXP i.MX Development Board - UK.indd 133
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
reVal = LPI2C_MasterSend(EXAMPLE_I2C_MASTER, &buff[1], 1); if (reVal != kStatus_Success) {
PRINTF("ERROR IN SEND\n\r");
} LPI2C_MasterStop(EXAMPLE_I2C_MASTER); }
/* Main function */ int main(void) { PRINTF("BEGINNING\n\r"); BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); lpi2c_master_config_t masterConfig; /*Clock setting for LPI2C*/ CLOCK_SetMux(kCLOCK_Lpi2cMux, LPI2C_CLOCK_SOURCE_SELECT); CLOCK_SetDiv(kCLOCK_Lpi2cDiv, LPI2C_CLOCK_SOURCE_DIVIDER); // // I2C configuration // masterConfig.debugEnable = false; masterConfig.ignoreAck = false; masterConfig.pinConfig = kLPI2C_2PinOpenDrain; masterConfig.baudRate_Hz = 100000U; masterConfig.busIdleTimeout_ns = 0; masterConfig.pinLowTimeout_ns = 0; masterConfig.sdaGlitchFilterWidth_ns = 0; masterConfig.sclGlitchFilterWidth_ns = 0; //
LPI2C_MasterGetDefaultConfig(&masterConfig);
/* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } }
● 134
Getting Started with NXP i.MX Development Board - UK.indd 134
26-10-2023 09:34
Chapter 7 • I²C Bus Interface
/* Change the default baudrate configuration */ masterConfig.baudRate_Hz = LPI2C_BAUDRATE; /* Initialize the LPI2C master peripheral */ LPI2C_MasterInit(EXAMPLE_I2C_MASTER, &masterConfig, LPI2C_MASTER_CLOCK_FREQUENCY); SEND(IODIRA, 0xFEU);
// Configure as outputs
while(1) {
SEND(MCP_GPIOA, 0U);
// LED OFF
SysTick_DelayTicks(1000);
// One sec delay
SEND(MCP_GPIOA, 1U);
// LED ON
SysTick_DelayTicks(1000);
// One sec delay
} }
Figure 7.12 Program listing
7.4 Project 2 – TMP102 temperature sensor chip Description: In this project the I²C compatible TMP102 temperature sensor chip is used. The ambient temperature is read every second and is displayed on the debug Console. The aim of this project is to show how the temperature sensor chip TMP102 can be used in a program. The TMP102 The TMP102 is an I²C compatible temperature sensor chip having the following basic features: Supply voltage: 1.4 V to 3.6 V Supply current: 10 μA Accuracy: 2ºC Resolution: 12 bits (0.0625ºC) Accuracy: ±0.5ºC TMP102 is a 6-pin chip as shown in Figure 7.13. The pin descriptions are: Pin 1 2 3 4 5 6
Name Description SCL I²C line GND power supply ground ALERT Over temperature alert. Open-drain output, requires a pull-up resistor ADD0 Address select V+ power supply SDA I²C line
● 135
Getting Started with NXP i.MX Development Board - UK.indd 135
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Figure 7.13 TMP102 pin layout The default operating mode of TMP102 is continuous conversion where the internal ADC converts the temperature into digital with the default rate of 4 Hz, with a conversion time of 26 ms. The temperature register is a 16-bit register where bits 0, 1, 2, and 3 are set to 0, and bits 4 to 15 store the 12-bit temperature data. TMP102 has the following operational modes: • Continuous conversion: by default, an internal ADC converts the temperature into digital format with the default conversion rate of 4 Hz, with a conversion time of 26 ms. The conversion rate can be selected using bits CR1 and CR0 of the configuration register as 0.25Hz, 1Hz, 4 Hz (default), and 8Hz. In this project, the default 4 Hz is used. • Extended mode: Bit EM of the configuration register selects normal mode (EM = 0), or extended mode (EM = 1). In normal mode (default mode) the converted data is 12 bits. Extended mode is used if the temperature is above 128ºC and the converted data is 13 bits. In this project, the normal mode is used. • Shutdown mode: This mode is used to save power where the current consumption is reduced to less than 0.5 μA. The shutdown mode is entered when configuration register bit SD = 1. The default mode is normal operation (SD = 0). • One-shot conversion: Setting configuration register bit OS to 1 selects the one-shot mode, which is a single conversion mode. The default mode is continuous conversion (OS = 0). • Thermostat mode: This mode indicates whether to operate in comparator mode (TM = 0) or in interrupt mode (TM = 1). The default is the comparator mode. In comparator mode, the Alert pin is activated when the temperature equals or exceeds the value in the THIGH register and remains active until the temperature drops below TLOW. In interrupt mode, the Alert pin is activated when the temperature exceeds THIGH or goes below TLOW registers. The Alert pin is cleared when the host controller reads the temperature register. A Pointer Register selects various registers in the chip as shown in Table 7.3. The upper 6 bits of this register are 0s.
● 136
Getting Started with NXP i.MX Development Board - UK.indd 136
26-10-2023 09:34
Chapter 7 • I²C Bus Interface
P1
P0
REGISTER SELECTED
0
0
Temperature register (read only)
0
1
Configuration register
1
0
TLOW register
1
1
THIGH register
Table 7.3 Pointer register bits Table 7.4 shows the temperature register bits in normal mode (EM = 0). BYTE 1: D7
D6
D5
D4
D3
D2
D1
D0
T11
T10
T9
T8
T7
T7
T5
T4
D7
D6
D5
D4
D3
D2
D1
D0
T3
T2
T1
T0
0
0
0
0
BYTE 2:
Table 7.4 Temperature register bits Table 7.5 shows the configuration register bits. The power-up default bit configuration is shown in the Table. BYTE 1: D7
D6
D5
D4
D3
D2
D1
D0
OS
R1
R0
F1
F0
POL
TM
SD
0
1
1
0
0
0
0
0
D6
D5
D4
D3
D2
D1
D0
BYTE 2: D7 CR1
CR0
AL
EM
0
0
0
0
1
0
1
0
0
0
0
0
Table 7.5 Configuration register bits The Polarity bit (POL) allows the user to adjust the polarity of the Alert pin output. If set to 0 (default), the Alert pin becomes active low. When set to 1, the Alert pin becomes active high. TMP102 is available as a module (breakout module) as shown in Figure 7.14. The default device address is 0x48. The temperature register address is 0x00, and this should be sent after sending the device address. This is then followed with a read command where 2 bytes are read from the TMP102. These 2 bytes contain the temperature data.
● 137
Getting Started with NXP i.MX Development Board - UK.indd 137
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
The temperature read sequence is as follows: • Master sends the device address 0x48 with the R/W set to 0 • Device responds with ACK • Master sends the temperature register address 0x00 • Device responds with ACK • Master re-sends device address 0x48 with the R/W bit set to 1 • Master reads upper byte of temperature data • Device sends ACK • Master reads lower byte of temperature data • Device sends ACK • Master sends stop condition on the bus
Figure 7.14 TMP102 as a module Block diagram: Figure 7.15 shows the block diagram of the project.
Figure 7.15 Block diagram of the project Circuit diagram: The circuit diagram of the project is shown in Figure 7.16. On-chip pullup resistors are available on the development kit and are not needed in this project.
● 138
Getting Started with NXP i.MX Development Board - UK.indd 138
26-10-2023 09:34
Chapter 7 • I²C Bus Interface
Figure 7.16 Circuit diagram of the project Program listing: Before writing the program, the SDA and SCL ports must be configured as described in the previous project. You must include the I²C from Manage SDK Components before compiling your project. Figure 7.17 shows the program listing (TMP102). At the beginning of the program, various definitions used by the I²C library are defined. The TMP102 address is set to 0x48. The function GET_TEMPERATURE () operates as follows: • Call MasterStart() with the LSB set to 0 (i.e. write) • Call MasterSend() to send the pointer register address as 0 (temperature register) • Call MasterRepeatedStart() with the LSB set to 1 (i.e. read) • Call MasterReceive() to receive two bytes of data from the TMP102 The received two bytes are then converted into temperature in Degrees Centigrade. The main program loop calls the function GET_TEMPERATURE () and displays the temperature every 3 seconds. Example output from the program on the Console is shown in Figure 7.18. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *TMP102*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "stdio.h" #include "fsl_debug_console.h" #include "fsl_lpi2c.h" #include "string.h"
● 139
Getting Started with NXP i.MX Development Board - UK.indd 139
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
/******************************************************************************* * Definitions ******************************************************************************/ uint8_t Temp_Reg = 0x00; #define EXAMPLE_I2C_MASTER_BASE (LPI2C1_BASE) /* Select USB1 PLL (480 MHz) as master lpi2c clock source */ #define LPI2C_CLOCK_SOURCE_SELECT (0U) /* Clock divider for master lpi2c clock source */ #define LPI2C_CLOCK_SOURCE_DIVIDER (5U) /* Get frequency of lpi2c clock */ #define LPI2C_CLOCK_FREQUENCY ((CLOCK_GetFreq(kCLOCK_Usb1PllClk) / 8) / (LPI2C_ CLOCK_SOURCE_DIVIDER + 1U)) #define LPI2C_MASTER_CLOCK_FREQUENCY LPI2C_CLOCK_FREQUENCY #define WAIT_TIME
10U
#define EXAMPLE_I2C_MASTER ((LPI2C_Type *)EXAMPLE_I2C_MASTER_BASE) #define LPI2C_MASTER_SLAVE_ADDR_7BIT 0x48U #define LPI2C_BAUDRATE
100000U
/******************************************************************************* * Prototypes ******************************************************************************/ void SysTick_DelayTicks(uint32_t); /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; /******************************************************************************* * Code ******************************************************************************/ // // Timer interrupt service routine. Refresh the display every 5 ms. // Also, increment the millisecond counter // void SysTick_Handler(void) { if (g_systickCounter != 0U) {
● 140
Getting Started with NXP i.MX Development Board - UK.indd 140
26-10-2023 09:34
Chapter 7 • I²C Bus Interface
g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } }
// // This function sends register address and register data to MCP23017 chip // float GET_TEMPERATURE() { uint8_t buff[10]; int16_t comb; float temperature, LSB = 0.0625; status_t reVal = kStatus_Fail; buff[0]=Temp_Reg; if (!kStatus_Success == LPI2C_MasterStart(EXAMPLE_I2C_MASTER, LPI2C_MASTER_ SLAVE_ADDR_7BIT, kLPI2C_Write)) {
PRINTF("WRITE FAILED..\n\r");
} reVal = LPI2C_MasterSend(EXAMPLE_I2C_MASTER, &buff[0], 1); if (reVal != kStatus_Success) {
PRINTF("SEND FAILED\n\r");
} if (!kStatus_Success == LPI2C_MasterRepeatedStart(EXAMPLE_I2C_MASTER, LPI2C_MASTER_SLAVE_ADDR_7BIT, kLPI2C_Read)) {
PRINTF("READ FAILED..\n\r");
} reVal = LPI2C_MasterReceive(EXAMPLE_I2C_MASTER, buff, 2); if (reVal != kStatus_Success)
● 141
Getting Started with NXP i.MX Development Board - UK.indd 141
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
{
PRINTF("ERROR IN RECEIVE\n\r");
} LPI2C_MasterStop(EXAMPLE_I2C_MASTER); comb = ((int16_t)buff[0] << 4) | (buff[1] >> 4);
// Combine the
bytes if (comb > 0x7FF )
// If negative
{ comb = (~comb) & 0xFFF; comb = comb + 1; temperature = -comb * LSB; } else temperature = comb * LSB; return temperature;
// Return temperature
}
/* Main function */ int main(void) { PRINTF("BEGINNING\n\r"); float T; char buff[7]; BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); lpi2c_master_config_t masterConfig; /*Clock setting for LPI2C*/ CLOCK_SetMux(kCLOCK_Lpi2cMux, LPI2C_CLOCK_SOURCE_SELECT); CLOCK_SetDiv(kCLOCK_Lpi2cDiv, LPI2C_CLOCK_SOURCE_DIVIDER); // // I2C configuration // /*
masterConfig.debugEnable = false; masterConfig.ignoreAck = false; masterConfig.pinConfig = kLPI2C_2PinOpenDrain; masterConfig.baudRate_Hz = 100000U; masterConfig.busIdleTimeout_ns = 0; masterConfig.pinLowTimeout_ns = 0; masterConfig.sdaGlitchFilterWidth_ns = 0;
● 142
Getting Started with NXP i.MX Development Board - UK.indd 142
26-10-2023 09:34
Chapter 7 • I²C Bus Interface
masterConfig.sclGlitchFilterWidth_ns = 0; */ LPI2C_MasterGetDefaultConfig(&masterConfig);
/* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } SysTick_DelayTicks(50); /* Change the default baudrate configuration */ masterConfig.baudRate_Hz = LPI2C_BAUDRATE; /* Initialize the LPI2C master peripheral */ LPI2C_MasterInit(EXAMPLE_I2C_MASTER, &masterConfig, LPI2C_MASTER_CLOCK_FREQUENCY);
while(1) { T = GET_TEMPERATURE(); PRINTF("Temperature = %d\n", (int)T);
SysTick_DelayTicks(3000);
// Read temperature // Display it // One second delay
} }
Figure 7.17 Program listing Table 7.6 shows the data output format of the temperature. Let us look at two examples: Example 1: Measured value = 0011 00100000
= 0x320
= 800 decimal
This is positive temperature, so the temperature is 800 x 0.0625 = +50ºC Example 2: Measured value = 1110 01110000
= 0xE70
This is negative temperature, complement is 0001 10001111 adding 1 gives 0001 10010000 = 400 decimal. The temperature is 400 x 0.0625 = 25 or, -25ºC
● 143
Getting Started with NXP i.MX Development Board - UK.indd 143
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Figure 7.18 Example output from the program Temperature
Digital Output (Binary)
Digital Output (Hex)
128
011111111111
7FF
100
011001000000
640
50
001100100000
320
0.25
000000000100
004
-0.25
111111111100
FFC
-25
111001110000
E70
-55
110010010000
C90
Table 7.6 Data output for some temperature readings. To run the program, click the Debug button under the Quickstart Panel. Then, click Run → Resume. Make sure that the Quick Settings → SDK Debug Console is set to Semihost console. Stop the program by clicking Run → Terminate. Figure 7.19 shows the project built on a breadboard.
Figure 7.19 Project built on a breadboard
● 144
Getting Started with NXP i.MX Development Board - UK.indd 144
26-10-2023 09:34
Chapter 8 • SPI Bus Interface
Chapter 8 • SPI Bus Interface 8.1 Overview In the previous chapter, you have learned how to interface I²C devices to your development kit. In this chapter you will be developing projects using the SPI bus (Serial Peripheral Interface). The SPI bus is one of the commonly used protocols to connect sensors and many other devices to microcontrollers. The SPI bus is a master-slave type bus protocol. In this protocol, one device (the microcontroller) is designated as the master, and one or more other devices (usually sensors) are designated as slaves. In a minimum bus configuration, there is one master and only one slave. The master establishes communication with the slaves and controls all the activity on the bus. Figure 8.1 shows an SPI bus example with one master and 3 slaves. The SPI bus uses 3 signals: clock (SCK), data in (SDI), and data out (SDO). SDO of the master is connected to the SDIs of the slaves, and SDOs of the slaves are connected to the SDI of the master. The master generates the SCK signals to enable data to be transferred on the bus. In every clock pulse, one bit of data is moved from master to slave, or from slave to master. The communication is only between a master and a slave, and the slaves cannot communicate with each other. It is important to note that only one slave can be active at any time, since there is no mechanism to identify the slaves. Thus, slave devices have enable lines (e.g. CS or CE) which are normally controlled by the master. A typical communication between a master and several slaves is as follows: • Master enables slave 1 • Master sends SCK signals to read or write data to slave 1 • Master disables slave 1 and enables slave 2 • Master sends SCK signals to read or write data to slave 2 • The above process continues as required
Figure 8.1 SPI bus with one master and 3 slaves The SPI signal names are also called MISO (Master in, Slave out), and MOSI (Master out, Slave in). Clock signal SCK is also called SCLK and the CS is also called SSEL. In the SPI projects in this chapter the development kit is the master, and one or more slaves can be connected to the bus. Transactions over the SPI bus are started by enabling the SCK line. The master then asserts the SSEL line LOW so that data transmission can begin. The data transmission involves two registers, one in the master and one in the slave device. Data
● 145
Getting Started with NXP i.MX Development Board - UK.indd 145
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
is shifted out from the master into the slave, with the MSB bit first. If more data is to be transferred, then the process is repeated. Data exchange is complete when the master stops sending clock pulses and deselects the slave device. Both the master and the slave must agree on the clock polarity and phase on the line, which are known as the SPI bus modes. These two settings are named as Clock Polarity (CPOL) and Clock Phase (CPHA) respectively. CPOL and CPHA can have the following values: CPOL 1 1
Clock active state Clock active HIGH Clock active LOW
CPHA 1 2
Clock phase Clock out of phase with data Clock in phase with data
The four SPI modes are: Mode 0 1 2 3
CPOL 0 0 1 1
CPHA 0 1 0 1
When CPOL = 0, the active state of the clock is 1, and its idle state is 0. For CPHA = 0, data is captured on the rising clock, and data is shifted out on the falling clock. For CPHA = 1, data is captured on the falling edge of the clock, and is shifted out on the rising edge of the clock. When CPOL = 1, the active state of the clock is 0, and its idle state is 1. For CPHA = 0, data is captured on the falling edge of the clock and is output on the rising edge. For CPHA = 1, data is captured on the rising edge of the clock and is shifted out on the falling edge.
8.2 Project 1 – Port expander Description: A simple project is given in this section to show how the SPI functions can be used in a program. This project is similar to the port expander project from the previous chapter where an LED is connected to one of the port expander pins and the LED is flashed every second. In this project, the MCP23S17 port expander chip is used. This is the SPI version of the MCP23017 chip.
● 146
Getting Started with NXP i.MX Development Board - UK.indd 146
26-10-2023 09:34
Chapter 8 • SPI Bus Interface
Block diagram: Figure 8.2 shows the block diagram of the project.
Figure 8.2 Block diagram of the project The MCP23S17 port expander chip The MCP23S17 is a 28-pin chip with the following features. The pin configuration is shown in Figure 8.3, which is the same as the pin configuration of MCP23017, but SPI pins are used instead of I²C pins: • 16 bidirectional I/O ports • Up to 1.7 MHz operation on I²C bus • Interrupt capability • External reset input • Low standby current • +1.8 V to +5.5 V operation • 3 address pins so that up to 8 devices can be used on the SPI bus • 28-pin DIL package
Figure 8.3 Pin configuration of the MCP23S17
● 147
Getting Started with NXP i.MX Development Board - UK.indd 147
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
The pin descriptions are given in Table 8.1. Pin
Description
GPA0-GPA7
Port A pins
GPB0-GPB7
Port B pins
VDD
Power supply
VSS
Ground
SI
SPI MOSI data pin
SCK
SPI clock pin
SO
SPI MISO data pin
CS
SPI SSEL chip enable pin
A0-A2
I²C address pins
RESET
Reset pin
INTA
Interrupt pin
INTB
Interrupt pin
Table 8.1 MCP23S17 pin descriptions The MCP23S17 is a slave SPI device. The slave address contains four upper fixed bits (0100) and three user-defined hardware address bits (pins A2, A1 and A0) with the read/ write bit filling out the control byte. These address bits are enabled/disabled by control register IOCON.HAEN. By default, the user address bits are disabled at power-up (i.e. IOCON.HAEN = 0) and A2 = A1 = A0 = 0 and the chip is addressed with 0x40. As such, we can use two MCP23S17 chips on SPI0 by connecting one CS bit to CE0, and the other one to CE1 and addressing both chips with 0x40. By setting bit HAEN to 1, you can change the addresses of the devices in multiple MCP23S17 based applications (e.g. more than 2) by connecting the A2, A1, and A0 accordingly. Sixteen of such chips can be connected (8 to CE0 and 8 to CE1), corresponding to 16x16 = 256 I/O ports. Figure 8.4 and Figure 8.5 show the addressing format. The address pins should be externally biased even if disabled.
Figure 8.4 MCP23S17 control byte format
● 148
Getting Started with NXP i.MX Development Board - UK.indd 148
26-10-2023 09:34
Chapter 8 • SPI Bus Interface
Figure 8.5 MCP23S17 addressing registers Like the MCP23017, the MCP23S17 chip has 8 internal registers that can be configured for its operation. The device can either be operated in 16-bit-bank mode or in two 8-bit-bank mode by configuring bit IOCON.BANK. On power-up, this bit is cleared which chooses the two 8-bit-bank mode by default. The I/O direction of the port pins are controlled with registers IODIRA (at address 0x00) and IODIRB (at address 0x01). Clearing a bit to 0 in these registers makes the corresponding port pin(s) as output(s). Similarly, setting a bit to 1 in these registers makes the corresponding port pin(s) input(s). GPIOA and GPIOB register addresses are 0x12 and 0x13, respectively. This is shown in Figure 8.6.
Figure 8.6 Configuring the I/O ports Further information on the MCP23S17 chip can be obtained from the Microchip Inc data sheet at the following website: http://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf Circuit diagram: Figure 8.7 shows the circuit diagram of the project. The LED is connected to port pin 21 (GPIAO bit 0) of the MCP23S17 through a current limiting resistor. The interface between the development kit and the MCP23S17 chip is as follows: MCP23S17 Development Kit Arduino header Pin name SI J57, pin 8 GPIO_AD_04 (IO18) SCK J57, pin 12 GPIO_AD_06 (IO20) CS J57, pin 6 GPIO_AD_05 (IO19) GND J60, pin 12 GND +3.3 V J60, pin 8 +3.3 V
● 149
Getting Started with NXP i.MX Development Board - UK.indd 149
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Figure 8.7 Circuit diagram of the project Program listing: Before writing the program, you should configure the SPI ports as described below: • Click ConfigTools followed by Pins and configure GPIO_AD_04 as LPSPI_SO, GPIO_AD_05 as LPSPI_PCS0 and GPIO_AD_06 as LPSPI_SCK as shown in Figure 8.8
Figure 8.8 Configure the SPI ports • Click ConfigTools followed by Peripherals and then click Peripheral drivers (Device specific). Click on LPSPI (Figure 8.9)
● 150
Getting Started with NXP i.MX Development Board - UK.indd 150
26-10-2023 09:34
Chapter 8 • SPI Bus Interface
Figure 8.9 Click on Peripheral drivers (Device specific) • You should see the LPSPI window as shown in Figure 8.10 (If there are any errors or warnings, right-click on the displayed error/warning on the right-hand side window called Problems and accept to correct the error/warning)
Figure 8.10 LPSPI window • Click ConfigTools followed by Clocks and make sure that clock is assigned to LPSPI_CLK_ROOT • Click Update Code followed by OK to exit the configuration tool You must include lpspi in your project using the Manage SDK Components, as described in the previous chapter on how to use the SDK manager. You are now ready to write the program. Figure 8.11 shows the program listing (MCP23S17). At the beginning of the program, the MCP23S17 device address and used register addresses are defined. Also, various SPI bus parameters are defined here. Function SEND () sends data to the MCP23S17 over
● 151
Getting Started with NXP i.MX Development Board - UK.indd 151
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
the SPI bus. A buffer is created and the MCP23S17 device address, the register address to be accessed, and the data to be sent to the device are all stored in a buffer called buff. Data in this buffer is then sent over the SPI bus using call LPSPI_MasterTransferBlocking (). Inside the main program loop, the MCP23S17 port GPIOA bit 0 is configured as output. Then a while loop is formed where 1 and 0 are sent to the LED to flash it every second. If you want to receive data over the SPI bus, you can use the same program but use the receive data option by specifying masterXfer. rxData = masterRxData. Figure 8.12 shows the project built on a breadboard. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *MCP23S17*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "fsl_debug_console.h" #include "fsl_lpspi.h" /******************************************************************************* * Definitions ******************************************************************************/ #define SPI_Address 0x40
// Device addr
#define MCP_GPIOA 0x12 // GPIOA #define MCP_IODIRA 0 // IODIRA #define EXAMPLE_LPSPI_MASTER_BASEADDR
(LPSPI1)
#define EXAMPLE_LPSPI_MASTER_PCS_FOR_INIT
(kLPSPI_Pcs0)
#define EXAMPLE_LPSPI_MASTER_PCS_FOR_TRANSFER (kLPSPI_MasterPcs0) /* Select USB1 PLL PFD0 (720 MHz) as lpspi clock source */ #define EXAMPLE_LPSPI_CLOCK_SOURCE_SELECT (1U) /* Clock divider for master lpspi clock source */ #define EXAMPLE_LPSPI_CLOCK_SOURCE_DIVIDER (7U) #define LPSPI_MASTER_CLK_FREQ (CLOCK_GetFreq(kCLOCK_Usb1PllPfd0Clk) / (EXAMPLE_ LPSPI_CLOCK_SOURCE_DIVIDER + 1U)) #define TRANSFER_BAUDRATE 500000U /*! Transfer baudrate - 500k */ /******************************************************************************* * Prototypes
● 152
Getting Started with NXP i.MX Development Board - UK.indd 152
26-10-2023 09:34
Chapter 8 • SPI Bus Interface
******************************************************************************/ void SysTick_DelayTicks(uint32_t); /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; volatile uint32_t g_systickCounter
= 20U;
lpspi_master_config_t masterConfig; lpspi_transfer_t masterXfer; /******************************************************************************* * Code ******************************************************************************/ // // Timer interrupt service routine. Refresh the display every 5 ms. // Also, increment the millisecond counter // void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } } // // This function sends register address and register data to MCP23S17 chip // float SEND(uint8_t port, uint8_t data) { uint8_t buff[3]; buff[0] = SPI_Address; buff[1] = port; buff[2] = data; /*Start master transfer, transfer data to slave.*/
● 153
Getting Started with NXP i.MX Development Board - UK.indd 153
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
masterXfer.txData
= buff;
masterXfer.rxData
= NULL;
masterXfer.dataSize = 3; masterXfer.configFlags = EXAMPLE_LPSPI_MASTER_PCS_FOR_TRANSFER | kLPSPI_MasterPcsContinuous | kLPSPI_MasterByteSwap; LPSPI_MasterTransferBlocking(EXAMPLE_LPSPI_MASTER_BASEADDR, &masterXfer); /* Delay 20 ms to wait slave is ready */ SysTick_DelayTicks(20); }
/* Main function */ int main(void) { PRINTF("BEGINNING\n\r"); BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } /*Set clock source for LPSPI*/ CLOCK_SetMux(kCLOCK_LpspiMux, EXAMPLE_LPSPI_CLOCK_SOURCE_SELECT); CLOCK_SetDiv(kCLOCK_LpspiDiv, EXAMPLE_LPSPI_CLOCK_SOURCE_DIVIDER); uint32_t srcClock_Hz;
LPSPI_MasterGetDefaultConfig(&masterConfig); masterConfig.baudRate = TRANSFER_BAUDRATE; masterConfig.whichPcs = EXAMPLE_LPSPI_MASTER_PCS_FOR_INIT; masterConfig.pcsToSckDelayInNanoSec
= 1000000000U / (masterConfig.
baudRate * 2U); masterConfig.lastSckToPcsDelayInNanoSec
= 1000000000U / (masterConfig.
baudRate * 2U); masterConfig.betweenTransferDelayInNanoSec = 1000000000U / (masterConfig.
● 154
Getting Started with NXP i.MX Development Board - UK.indd 154
26-10-2023 09:34
Chapter 8 • SPI Bus Interface
baudRate * 2U); srcClock_Hz = LPSPI_MASTER_CLK_FREQ; LPSPI_MasterInit(EXAMPLE_LPSPI_MASTER_BASEADDR, &masterConfig, srcClock_Hz); SEND(MCP_IODIRA, 0xFE);
// Configure GPIOA bit 0 as output
while(1) { SEND(MCP_GPIOA, 1);
// GPIOA bit 0 is 1
SysTick_DelayTicks(1000);
// One second delay
SEND(MCP_GPIOA, 0);
// GPIOA bit 0 is 0
SysTick_DelayTicks(1000);
// One second delay
} }
Figure 8.11 Program listing
Figure 8.12 Project built on a breadboard
● 155
Getting Started with NXP i.MX Development Board - UK.indd 155
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Chapter 9 • Using LCDs 9.1 Overview In microcontroller systems, the output of a measured variable is usually displayed using LEDs, 7-segment displays, or LCD-type displays. LCDs have the advantages that they can be used to display alphanumeric or graphical data. Some LCDs have 40 or more character lengths with the capability to display several lines. Some other LCDs can be used to display graphic images. Some modules offer color displays, while some others incorporate back lighting so that they can be viewed in dimly lit conditions. There are basically two types of LCDs as far as the interface technique is concerned: parallel LCDs and serial LCDs. Parallel LCDs (e.g. Hitachi HD44780) are connected to a microcontroller using more than one data line and the data is transferred in parallel form. It is common to use either 4 or 8 data lines. Using a 4-wire connection saves I/O pins, but it is slower since the data is transferred in two stages. Serial LCDs are connected to the microcontroller using only one data line, and data is usually sent to the LCD using the standard RS-232 asynchronous data communication protocol. Serial LCDs are much easier to use, but they cost more than the parallel ones. The programming of a parallel LCD is usually a complex task and requires a good understanding of the internal operation of the LCD controllers, including the timing diagrams. Fortunately, most high-level languages provide special library commands for displaying data on alphanumeric as well as on graphical LCDs. All the user has to do is connect the LCD to the microcontroller, define the LCD connection in the software, and then send special commands to display data on the LCD.
9.2 The HD44780 LCD module HD44780 is one of the most popular alphanumeric LCD modules used in industry and also by hobbyists. This module is monochrome and comes in different sizes. Modules with 8, 16, 20, 24, 32, and 40 columns are available. Depending on the model chosen, the number of rows varies between 1, 2 or 4. The display provides a 14-pin (or 16-pin) connector to a microcontroller. Table 9.1 gives the pin configuration and pin functions of a 14-pin LCD module. Below is a summary of the pin functions: Pin no
Name
Function
1
VSS
Ground
2
VDD
+ ve supply
3
VEE
Contrast
4
RS
Register select
5
R/W
Read/write
6
E
Enable
7
D0
Data bit 0
8
D1
Data bit 1
● 156
Getting Started with NXP i.MX Development Board - UK.indd 156
26-10-2023 09:34
Chapter 9 • Using LCDs 9
D2
Data bit 2
10
D3
Data bit 3
11
D4
Data bit 4
12
D5
Data bit 5
13
D6
Data bit 6
14
D7
Data bit 7
Table 9.1 Pin configuration of HD44780 LCD module VSS is the 0 V supply or ground. The VDD pin should be connected to the positive supply. Although the manufacturers specify a 5 V DC supply, the modules will usually work with as low as 3 V or as high as 6 V. Pin 3 is named VEE and this is the contrast control pin. This pin is used to adjust the contrast of the display, and it should be connected to a variable voltage supply. A potentiometer is normally connected between the power supply lines, with its wiper connected to this pin so that the contrast can be adjusted. Pin 4 is the Register Select (RS) and when this pin is LOW, data transferred to the display is treated as commands. When RS is HIGH, character data can be transferred to and from the module. Pin 5 is the Read/Write (R/W) line. This pin is pulled LOW to write commands or character data to the LCD module. When this pin is HIGH, character data or status information can be read from the module. Pin 6 is the Enable (E) pin, which is used to initiate the transfer of commands or data between the module and the microcontroller. When writing to the display, data is transferred only on the HIGH to LOW transition of this line. When reading from the display, data becomes available after the LOW to HIGH transition of the enable pin, and this data remains valid as long as the enable pin is at logic HIGH. Pins 7 to 14 are the eight data bus lines (D0 to D7). Data can be transferred between the microcontroller and the LCD module using either a single 8-bit byte, or as two 4-bit nibbles. In the latter case, only the upper four data lines (D4 to D7) are used. 4-bit mode has the advantage that four less I/O lines are required to communicate with the LCD. In this book, we shall be using alphanumeric-based LCD only and look at the 4-bit interface only. Figure 9.1 shows a typical HD44780 controller based 2 × 16 (2 rows and 16 columns) character LCD. In this chapter you will learn how to interface and program an LCD to the development Kit.
● 157
Getting Started with NXP i.MX Development Board - UK.indd 157
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Figure 9.1 2 × 16 character LCD Connecting the LCD to the development board The following pins are used in 4-bit mode: D4:D7 E R/S It is important to connect the R/W pin to ground pin of the power supply before applying power to the LCD. The following connections will be used between the LCD and the development kit in LCD-based projects in this book: LCD Pin Development Kit Arduino header pin D4 J57, pin6 IO19 D5 J57, pin 8 IO18 D6 J57, pin 10 IO17 D7 J57, pin 12 IO20 R/S J57, pin 18 IO1 E J57, pin 20 IO2 R/W J60, pin 12 GND
9.3 Project 1 – Displaying text on LCD Description: In this project, text is displayed on the LCD. The aim of this simple project is to show how an LCD can be programmed using the MCUXpresso IDE with your development kit. The text TESTING LCD is displayed on the LCD. Block diagram: The block diagram of the project is shown in Figure 9.2.
● 158
Getting Started with NXP i.MX Development Board - UK.indd 158
26-10-2023 09:34
Chapter 9 • Using LCDs
Figure 9.2 Block diagram of the project Circuit diagram: Figure 9.3 shows the connections between the LCD and the development kit. The LCD contrast is adjusted using a 10 kΩ potentiometer.
Figure 9.3 Circuit diagram of the project Program listing: The used port pins must be configured as outputs, as in the previous projects. In this project, the HD44780 compatible LCD is programmed from the first principles without using a library. Programming an LCD requires good knowledge of its pins and timing details, which are available in the LCD data sheets. Although this is beyond the scope of this book, some details are given here. Initializing the LCD: The HD44780 LCD has 8 data pins D0 to D7. It can either be programmed in 8-bit mode where data is sent to all 8 bits in parallel and at the same time, or 4-bit programming mode can be selected where the 8-bit data is divided into two nibbles (4-bits) and first the higher nibble is sent followed by the lower nibble on data pins D4 to D7 (i.e. the lower bits D0 to D3 are not used). The LCD should be initialized before it can be used. The initialization sequence is described in detail in the data sheet, and it basically consists of sending nibble 0x03 three times followed by sending nibble 0x02. The enable input E must be clocked after sending each nibble of data. Clocking the E input simply consists of sending logic HIGH followed by logic LOW with some delay in between. This is also called applying STROBE to the LCD. At the end of sending the four nibbles, the LCD is initialized to operate in 4-bit mode. The remaining parts of the initialization sequence involve configuring the function set, which sets the number of lines and the character set (5 × 10 or 5 × 8). Usually, command 0x28 is sent to the LCD, which selects two lines with a 5 × 8 dot character set. The initialization sequence is terminated by setting the display on/off control, clearing the display, and configuring the entry mode set. The LCD operates either in command mode or in data mode. The command mode is identified with pin R/S set to LOW. During the data mode, R/S is set HIGH. After the initialization sequence, various LCD functions are defined, such as homing the cursor, moving the cursor to a specified position, displaying a character or text, etc.
● 159
Getting Started with NXP i.MX Development Board - UK.indd 159
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Figure 9.4 shows the program listing (lcd1). The LCD initialization and the definition of various LCD functions are done in the early parts of the code, starting with the comment LCD CODES, and terminating with the comment END OF LCD CODES. Inside the main program, the text TESTING LCD is displayed and the program terminates. Figure 9.5 shows the project constructed on a breadboard. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *LCD-1*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" /******************************************************************************* * Definitions ******************************************************************************/ // // LCD connections // #define LCD_EN
2U
#define LCD_RS
1U
#define LCD_D4
19U
#define LCD_D5
18U
#define LCD_D6
17U
#define LCD_D7
20U
/******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; static int PORTS[] = {20U, 17U, 18U, 19U}; /******************************************************************************* * Code ******************************************************************************/ //
● 160
Getting Started with NXP i.MX Development Board - UK.indd 160
26-10-2023 09:34
Chapter 9 • Using LCDs
// Timer interrupt service routine. Refresh the display every 5 ms. // Also, increment the millisecond counter // void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } } //============================= LCD CODES ============================== // Group the port pins together. L is the number of bits (8 here),and No // is the data to be displayed // void Display(int No, int L) { int i, m, j; m = L - 1; for(i = 0; i < L; i++) { j = 1; for(int k = 0; k < m; k++)j = j * 2; if((No & j) != 0) GPIO_PinWrite(GPIO1,PORTS[i], 1U); else GPIO_PinWrite(GPIO1,PORTS[i], 0U); m--; } } // // LCD_STROBE // void LCD_STROBE() { GPIO_PinWrite(GPIO1, LCD_EN, 1U);
● 161
Getting Started with NXP i.MX Development Board - UK.indd 161
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
SysTick_DelayTicks(1); GPIO_PinWrite(GPIO1, LCD_EN, 0U); SysTick_DelayTicks(1); }
// // Send a command to the LCD // void lcd_write_cmd(unsigned char c) { unsigned int d; d = c; d = (d >> 4);
// Extract upper nibble
Display(d, 4); GPIO_PinWrite(GPIO1, LCD_RS, 0U); LCD_STROBE();
// Clock enable bit
d = c; d = d & 0x0F;
// Extract lower nibble
Display(d, 4); GPIO_PinWrite(GPIO1, LCD_RS, 0U); LCD_STROBE();
// Clock enable bit
SysTick_DelayTicks(1); } // // Send data to the LCD // void lcd_write_data(unsigned char c) { unsigned int d; d = c; d = (d >> 4);
// Extract upper nibble
Display(d, 4); GPIO_PinWrite(GPIO1, LCD_RS, 1U); LCD_STROBE();
// Clock enable bit
d = c; d = d & 0x0F;
// Extract lower nibble
Display(d, 4); GPIO_PinWrite(GPIO1, LCD_RS, 1U); LCD_STROBE(); } // // Clear LCD //
● 162
Getting Started with NXP i.MX Development Board - UK.indd 162
26-10-2023 09:34
Chapter 9 • Using LCDs
void lcd_clear(void) { lcd_write_cmd(0x1); SysTick_DelayTicks(5); } // // Home cursor // void lcd_home_cursor(void) { lcd_write_cmd(0x2); SysTick_DelayTicks(5); } // // Cursor blinking // void lcd_cursor_blinking(void) { lcd_write_cmd(0x0F); SysTick_DelayTicks(3); } // // Cursor non blinking // void lcd_cursor_nonblinking(void) { lcd_write_cmd(0x0E); SysTick_DelayTicks(3); } // // Shift right // void lcd_shift_all_right(void) { lcd_write_cmd(0x1C); SysTick_DelayTicks(5); } // // Shift left // void lcd_shift_all_left(void)
● 163
Getting Started with NXP i.MX Development Board - UK.indd 163
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
{ lcd_write_cmd(0x18); SysTick_DelayTicks(5); } // // Move cursor left // void lcd_cursor_left(void) { lcd_write_cmd(0x10); SysTick_DelayTicks(3); } // // Move cursor right // void lcd_cursor_right(void) { lcd_write_cmd(0x14); SysTick_DelayTicks(3); } // // Display text message on LCD // void lcd_print(const char * s) { while(*s) lcd_write_data(*s++); } // // Display single character on LCD // void lcd_putch(char c) { unsigned int d; d = c; d = (d >> 4); Display(d, 4); GPIO_PinWrite(GPIO1, LCD_RS, 1U); LCD_STROBE(); d = c; d = (d & 0x0F); Display(d, 4);
● 164
Getting Started with NXP i.MX Development Board - UK.indd 164
26-10-2023 09:34
Chapter 9 • Using LCDs
GPIO_PinWrite(GPIO1, LCD_RS, 1U); LCD_STROBE(); } // // Position the cursor at column,row. (0,0) is left corner // void lcd_goto(int col, int row) { char address; if(row == 0)address = 0; if(row == 1)address = 0x40; address += col - 1; lcd_write_cmd(0x80 | address); } // // Initialize the LCD // void lcd_init(void) { SysTick_DelayTicks(100); GPIO_PinWrite(GPIO1, LCD_RS, 0U); GPIO_PinWrite(GPIO1, LCD_EN, 0U); Display(0x03,4); SysTick_DelayTicks(5); LCD_STROBE(); SysTick_DelayTicks(30); LCD_STROBE(); SysTick_DelayTicks(20); LCD_STROBE(); SysTick_DelayTicks(20); Display(0x02,4); SysTick_DelayTicks(1); LCD_STROBE(); SysTick_DelayTicks(5); lcd_write_cmd(0x28); SysTick_DelayTicks(5); lcd_write_cmd(0x0F); SysTick_DelayTicks(5); lcd_write_cmd(0x01); SysTick_DelayTicks(5); lcd_write_cmd(0x06); SysTick_DelayTicks(5); GPIO_PinWrite(GPIO1, LCD_RS, 1U); }
● 165
Getting Started with NXP i.MX Development Board - UK.indd 165
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
//======================= END OF LCD CODES =================================
/* Main function */ int main(void) { BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } lcd_init();
// Initialize LCD
lcd_clear();
// Clear LCD
lcd_print("TESTING LCD");
// Display text
while(1);
// Stop here
}
Figure 9.4 Program listing
Figure 9.5 Construction of the project
● 166
Getting Started with NXP i.MX Development Board - UK.indd 166
26-10-2023 09:34
Chapter 9 • Using LCDs
The following functions are supported by the LCD: lcd_init() this must be the first function to be called lcd_clear() clear LCD lcd_home_cursor() home cursor lcd_cursor_blinking() set blinking cursor ON lcd_cursor_nonblinking() set blinking cursor OFF lcd_shift_all_right() shift all (next) characters right by one place lcd_shift_all_left() shift all (next) characters left by one position lcd_cursor_left() move cursor left by one position lcd_cursor_right() move cursor right by one position lcd_print() display string of data lcd_putch() display single character lcd_goto(col, row) move cursor to specified (col, row)
9.4 Project 2 – Using LCDs – simple up counter Description: In this project an up counter is developed which counts up every second and the result is displayed on the LCD in the following format: Count: nn Where the text Count is displayed at the beginning of row 0, and the number is displayed starting from row 1. The aim of this project is to show how both text and numbers can be displayed on the LCD. The block diagram and circuit diagram of the project are the same as in Figure 9.2 and Figure 9.3 respectively. Program listing: The used port pins must be configured as outputs, as in the previous projects. The program listing is shown in Figure 9.6 (lcd-counter). A variable called Count is initialized to 0 at the beginning of the program. An array called Txt is defined to store the count in string format. Count is incremented by one every second. It is then converted into a character array and stored in Txt using the standard function itoa. String Txt is then displayed on the LCD in the required format every second. Figure 9.7 shows the LCD. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *LCD-counter*/ #include "pin_mux.h" #include "clock_config.h"
● 167
Getting Started with NXP i.MX Development Board - UK.indd 167
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
#include "board.h" /******************************************************************************* * Definitions ******************************************************************************/ // // LCD connections // #define LCD_EN
2U
#define LCD_RS
1U
#define LCD_D4
19U
#define LCD_D5
18U
#define LCD_D6
17U
#define LCD_D7
20U
/******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; static int PORTS[] = {20U, 17U, 18U, 19U}; /******************************************************************************* * Code ******************************************************************************/ // // Timer interrupt service routine. Refresh the display every 5 ms. // Also, increment the millisecond counter // void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) {
● 168
Getting Started with NXP i.MX Development Board - UK.indd 168
26-10-2023 09:34
Chapter 9 • Using LCDs
} } //============================= LCD CODES ============================== // Group the port pins together. L is the number of bits (8 here),and No // is the data to be displayed // void Display(int No, int L) { int i, m, j; m = L - 1; for(i = 0; i < L; i++) { j = 1; for(int k = 0; k < m; k++)j = j * 2; if((No & j) != 0) GPIO_PinWrite(GPIO1,PORTS[i], 1U); else GPIO_PinWrite(GPIO1,PORTS[i], 0U); m--; } } // // LCD_STROBE // void LCD_STROBE() { GPIO_PinWrite(GPIO1, LCD_EN, 1U); SysTick_DelayTicks(1); GPIO_PinWrite(GPIO1, LCD_EN, 0U); SysTick_DelayTicks(1); }
// // Send a command to the LCD // void lcd_write_cmd(unsigned char c) { unsigned char d; d = c; d = (d >> 4);
// Extract upper nibble
Display(d, 4); GPIO_PinWrite(GPIO1, LCD_RS, 0U);
● 169
Getting Started with NXP i.MX Development Board - UK.indd 169
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
LCD_STROBE();
// Clock enable bit
d = c; d = d & 0x0F;
// Extract lower nibble
Display(d, 4); GPIO_PinWrite(GPIO1, LCD_RS, 0U); LCD_STROBE();
// Clock enable bit
SysTick_DelayTicks(1); } // // Send data to the LCD // void lcd_write_data(unsigned char c) { unsigned char d; d = c; d = (d >> 4);
// Extract upper nibble
Display(d, 4); GPIO_PinWrite(GPIO1, LCD_RS, 1U); LCD_STROBE();
// Clock enable bit
d = c; d = d & 0x0F;
// Extract lower nibble
Display(d, 4); GPIO_PinWrite(GPIO1, LCD_RS, 1U); LCD_STROBE(); } // // Clear LCD // void lcd_clear(void) { lcd_write_cmd(0x1); SysTick_DelayTicks(5); } // // Home cursor // void lcd_home_cursor(void) { lcd_write_cmd(0x2); SysTick_DelayTicks(5); } //
● 170
Getting Started with NXP i.MX Development Board - UK.indd 170
26-10-2023 09:34
Chapter 9 • Using LCDs
// Cursor blinking // void lcd_cursor_blinking(void) { lcd_write_cmd(0x0F); SysTick_DelayTicks(3); } // // Cursor non blinking // void lcd_cursor_nonblinking(void) { lcd_write_cmd(0x0E); SysTick_DelayTicks(3); } // // Shift right // void lcd_shift_all_right(void) { lcd_write_cmd(0x1C); SysTick_DelayTicks(5); } // // Shift left // void lcd_shift_all_left(void) { lcd_write_cmd(0x18); SysTick_DelayTicks(5); } // // Move cursor left // void lcd_cursor_left(void) { lcd_write_cmd(0x10); SysTick_DelayTicks(3); } // // Move cursor right
● 171
Getting Started with NXP i.MX Development Board - UK.indd 171
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
// void lcd_cursor_right(void) { lcd_write_cmd(0x14); SysTick_DelayTicks(3); } // // Display text message on LCD // void lcd_print(const char * s) { while(*s) lcd_write_data(*s++); } // // Display single character on LCD // void lcd_putch(char c) { unsigned int d; d = c; d = (d >> 4); Display(d, 4); GPIO_PinWrite(GPIO1, LCD_RS, 1U); LCD_STROBE(); d = c; d = (d & 0x0F); Display(d, 4); GPIO_PinWrite(GPIO1, LCD_RS, 1U); LCD_STROBE(); } // // Position the cursor at column,row. (0,0) is left corner // void lcd_goto(int col, int row) { char address; if(row == 0)address = 0; if(row == 1)address = 0x40; address += col - 1; lcd_write_cmd(0x80 | address); }
● 172
Getting Started with NXP i.MX Development Board - UK.indd 172
26-10-2023 09:34
Chapter 9 • Using LCDs
// // Initialize the LCD // void lcd_init(void) { SysTick_DelayTicks(100); GPIO_PinWrite(GPIO1, LCD_RS, 0U); GPIO_PinWrite(GPIO1, LCD_EN, 0U); Display(0x03,4); SysTick_DelayTicks(5); LCD_STROBE(); SysTick_DelayTicks(30); LCD_STROBE(); SysTick_DelayTicks(20); LCD_STROBE(); SysTick_DelayTicks(20); Display(0x02,4); SysTick_DelayTicks(1); LCD_STROBE(); SysTick_DelayTicks(5); lcd_write_cmd(0x28); SysTick_DelayTicks(5); lcd_write_cmd(0x0F); SysTick_DelayTicks(5); lcd_write_cmd(0x01); SysTick_DelayTicks(5); lcd_write_cmd(0x06); SysTick_DelayTicks(5); GPIO_PinWrite(GPIO1, LCD_RS, 1U); } //======================= END OF LCD CODES =================================
/* Main function */ int main(void) { char Txt[10]; int Count = 0; BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U))
● 173
Getting Started with NXP i.MX Development Board - UK.indd 173
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
{ while (1) { } } lcd_init();
// Initialize LCD
lcd_print("Count:");
// Display Count:
while(1) { lcd_goto(1,1); itoa(Count, Txt, 10);
// Convert to string
lcd_print(Txt);
// Display Count
Count++;
// Increment Count
SysTick_DelayTicks(1000);
// Wait 1 second
} }
Figure 9.6 Program listing
Figure 9.7 LCD
9.5 Including the LCD codes in a program In Figure 9.6 all the LCD functions were included at the beginning of the program. This is not desirable in many applications. In this section, you will see how to include the LCD files LCD.c and LCD.h in the project sources directory and then include them at the beginning of your programs. The steps are as follows: • Create a new file with the name LCD.h, whose contents are shown in Figure 9.8 #include "pin_mux.h" #include "clock_config.h" #include "board.h" void SysTick_DelayTicks(uint32_t); void LCD_STROBE(void); void Display(int,int);
● 174
Getting Started with NXP i.MX Development Board - UK.indd 174
26-10-2023 09:34
Chapter 9 • Using LCDs
void lcd_write_cmd(unsigned char); void lcd_write_data(unsigned char); void lcd_clear(void); void lcd_home_cursor(void); void lcd_cursor_blinking(void); void lcd_cursor_nonblinking(void); void lcd_shift_all_right(); void lcd_shift_all_left(); void lcd_cursor_left(); void lcd_cursor_right(); void lcd_print(const char * s); void lcd_putch(char c); void lcd_goto(int,int); void lcd_init(void);
Figure 9.8 File: LCD.h • Create a new file with the name LCD.c, whose contents are shown in Figure 9.9 include "board.h" // // LCD connections // #define LCD_EN
2U
#define LCD_RS
1U
#define LCD_D4
19U
#define LCD_D5
18U
#define LCD_D6
17U
#define LCD_D7
20U
static int PORTS[] = {20U, 17U, 18U, 19U}; extern void SysTick_DelayTicks(uint32_t); //============================= LCD CODES ============================== // Group the port pins together. L is the number of bits (8 here),and No // is the data to be displayed // void Display(int No, int L) { int i, m, j; m = L - 1; for(i = 0; i < L; i++) { j = 1; for(int k = 0; k < m; k++)j = j * 2; if((No & j) != 0)
● 175
Getting Started with NXP i.MX Development Board - UK.indd 175
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
GPIO_PinWrite(GPIO1,PORTS[i], 1U); else GPIO_PinWrite(GPIO1,PORTS[i], 0U); m--; } } // // LCD_STROBE // void LCD_STROBE() { GPIO_PinWrite(GPIO1, LCD_EN, 1U); SysTick_DelayTicks(1); GPIO_PinWrite(GPIO1, LCD_EN, 0U); SysTick_DelayTicks(1); }
// // Send a command to the LCD // void lcd_write_cmd(unsigned char c) { unsigned char d; d = c; d = (d >> 4);
// Extract upper nibble
Display(d, 4); GPIO_PinWrite(GPIO1, LCD_RS, 0U); LCD_STROBE();
// Clock enable bit
d = c; d = d & 0x0F;
// Extract lower nibble
Display(d, 4); GPIO_PinWrite(GPIO1, LCD_RS, 0U); LCD_STROBE();
// Clock enable bit
SysTick_DelayTicks(1); } // // Send data to the LCD // void lcd_write_data(unsigned char c) { unsigned char d; d = c; d = (d >> 4);
// Extract upper nibble
● 176
Getting Started with NXP i.MX Development Board - UK.indd 176
26-10-2023 09:34
Chapter 9 • Using LCDs
Display(d, 4); GPIO_PinWrite(GPIO1, LCD_RS, 1U); LCD_STROBE(); // Clock enable bit d = c; d = d & 0x0F;
// Extract lower nibble
Display(d, 4); GPIO_PinWrite(GPIO1, LCD_RS, 1U); LCD_STROBE(); } // // Clear LCD // void lcd_clear(void) { lcd_write_cmd(0x1); SysTick_DelayTicks(5); } // // Home cursor // void lcd_home_cursor(void) { lcd_write_cmd(0x2); SysTick_DelayTicks(5); } // // Cursor blinking // void lcd_cursor_blinking(void) { lcd_write_cmd(0x0F); SysTick_DelayTicks(3); } // // Cursor non blinking // void lcd_cursor_nonblinking(void) { lcd_write_cmd(0x0E); SysTick_DelayTicks(3); }
● 177
Getting Started with NXP i.MX Development Board - UK.indd 177
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
// // Shift right // void lcd_shift_all_right(void) { lcd_write_cmd(0x1C); SysTick_DelayTicks(5); } // // Shift left // void lcd_shift_all_left(void) { lcd_write_cmd(0x18); SysTick_DelayTicks(5); } // // Move cursor left // void lcd_cursor_left(void) { lcd_write_cmd(0x10); SysTick_DelayTicks(3); } // // Move cursor right // void lcd_cursor_right(void) { lcd_write_cmd(0x14); SysTick_DelayTicks(3); } // // Display text message on LCD // void lcd_print(const char * s) { while(*s) lcd_write_data(*s++); }
● 178
Getting Started with NXP i.MX Development Board - UK.indd 178
26-10-2023 09:34
Chapter 9 • Using LCDs
// // Display single character on LCD // void lcd_putch(char c) { unsigned int d; d = c; d = (d >> 4); Display(d, 4); GPIO_PinWrite(GPIO1, LCD_RS, 1U); LCD_STROBE(); d = c; d = (d & 0x0F); Display(d, 4); GPIO_PinWrite(GPIO1, LCD_RS, 1U); LCD_STROBE(); } // // Position the cursor at column,row. (0,0) is left corner // void lcd_goto(int col, int row) { char address; if(row == 0)address = 0; if(row == 1)address = 0x40; address += col - 1; lcd_write_cmd(0x80 | address); } // // Initialize the LCD // void lcd_init(void) { SysTick_DelayTicks(100); GPIO_PinWrite(GPIO1, LCD_RS, 0U); GPIO_PinWrite(GPIO1, LCD_EN, 0U); Display(0x03,4); SysTick_DelayTicks(5); LCD_STROBE(); SysTick_DelayTicks(30); LCD_STROBE(); SysTick_DelayTicks(20); LCD_STROBE(); SysTick_DelayTicks(20);
● 179
Getting Started with NXP i.MX Development Board - UK.indd 179
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Display(0x02,4); SysTick_DelayTicks(1); LCD_STROBE(); SysTick_DelayTicks(5); lcd_write_cmd(0x28); SysTick_DelayTicks(5); lcd_write_cmd(0x0F); SysTick_DelayTicks(5); lcd_write_cmd(0x01); SysTick_DelayTicks(5); lcd_write_cmd(0x06); SysTick_DelayTicks(5); GPIO_PinWrite(GPIO1, LCD_RS, 1U); } //======================= END OF LCD CODES =================================
Figure 9.9 File: LCD.c • Save both LCD.h and LCD.c in the source directory of your workspace. Assuming your workspace is evkmimxrt1010_iled_blinky: C:\users\username\Documents\MCUXpressoIDE_11.7.1_9221\ workspace\evkmimxrt1010_iled_blinky\source • The username and workspace name will be different on your computer. Notice that you could also save the include files in the compiler Build directory. • The following statement should be added to the beginning of the program: #include "LCD.h" An example project of using the new files is given below.
9.6 Project 3 – Using LCDs – simple up counter – including LCD header file Description: In this project an up counter is developed as in the previous project. Here, the LCD header file is included at the beginning of the program. The block diagram and circuit diagram of the project are the same as in Figure 9.2 and Figure 9.3 respectively. Program listing: The used port pins must be configured as outputs, as in the previous projects. The program is much simpler, and its listing is shown in Figure 9.10 (lcd-counter-header-included).
● 180
Getting Started with NXP i.MX Development Board - UK.indd 180
26-10-2023 09:34
Chapter 9 • Using LCDs
/* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *LCD-counter - header included*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "LCD.h" void SysTick_DelayTicks(uint32_t); /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; /******************************************************************************* * Code ******************************************************************************/ // // Timer interrupt service routine. Refresh the display every 5 ms. // Also, increment the millisecond counter // void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U)
● 181
Getting Started with NXP i.MX Development Board - UK.indd 181
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
{ } }
/* Main function */ int main(void) { char Txt[10]; int Count = 0; BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } lcd_init();
// Initialize LCD
lcd_print("Count:");
// Display Count:
while(1) { lcd_goto(1,1); itoa(Count, Txt, 10);
// Convert to string
lcd_print(Txt);
// Display Count
Count++;
// Increment Count
SysTick_DelayTicks(1000);
// Wait 1 second
} }
Figure 9.10 Program listing
9.7 Project 4 – LCD-based conveyor belt goods counter Description: In this project we count the number of items (e.g. bottles) passing on a conveyor belt and display the result continuously on the LCD. Block diagram: Figure 9.11 shows the project block diagram. A light beam is directed at a point to the passing bottles. At the other side of the beam, a light dependent resistor (LDR) is used to detect when the light beam is interrupted. When this happens, a signal is
● 182
Getting Started with NXP i.MX Development Board - UK.indd 182
26-10-2023 09:34
Chapter 9 • Using LCDs
sent to the development kit, which then increments a counter and displays the total count on the LCD.
Figure 9.11 Block diagram of the project Light dependent resistor A light dependent resistor is simply a resistor whose resistance changes with the application of light to its surface (Figure 9.12). The resistance of an LDR increases as the light intensity falling on the device is reduced (see Figure 9.12).
Figure 9.12 LDR and its typical characteristics LDRs are usually used in series with resistors in a circuit to form a resistive potential divider circuit. The voltage at the output of the potential divider circuit is used to send a trigger signal when the light intensity is below (or above) a set level. The trigger point can be detected by an MCU using both analog or digital inputs. Circuit diagram: Figure 9.13 shows the circuit diagram of the project. A 10 kΩ potentiometer is used in series with the LDR. The potentiometer is adjusted such that the output voltage goes over 2 V when the light falling on the LDR is reduced by a passing bottle. The output of the potential divider circuit is fed to digital port IO15 (J56, pin 14) of the development kit.
● 183
Getting Started with NXP i.MX Development Board - UK.indd 183
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
Figure 9.13 Circuit diagram of the project It was measured by the author that the resistance of the used LDR was about 0.5 kΩ in light and increased to about 100 kΩ when in dark. Assuming the potentiometer is set to its middle position, i.e. 5 kΩ: The voltage at the output when light falls on the LDR is Vo = 3.3 × 0.5/(5 + 0.5) = 0.3 V which is a logic 0. Similarly, the voltage at the output when it is dark is Vo = 3.3 × 100/ (5 + 100) = 3.14 V which is seen as a logic 1. We will set the potentiometer arm to just below its mid-point so that the output voltage is even lower than 0.3 V when it is light. The exact point can easily be determined by experimentation. Program listing: Figure 9.14 shows the program listing (lcd-conveyor-belt). The LCD connections must be configured as output ports. Similarly, the LDR must be configured as an input port (Figure 9.15). The LCD is initialized and the heading Count: is sent to the top row of the LCD. Inside the main program loop, the cursor is set to (1, 1), and the program waits while there is light on the LDR (i.e. no bottle detected). When a bottle is detected, the program comes out of the while statement and increment the total count (variable Count). This variable is converted into a character array in Txt and displayed on the LCD. The program then waits again until a bottle passes in front of the LDR. This cycle is repeated after a small delay. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *LCD- conveyor belt*/ #include "pin_mux.h"
● 184
Getting Started with NXP i.MX Development Board - UK.indd 184
26-10-2023 09:34
Chapter 9 • Using LCDs
#include "clock_config.h" #include "board.h" #include "LCD.h" void SysTick_DelayTicks(uint32_t); /******************************************************************************* * Definitions ******************************************************************************/ #define LDR 15U /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; /******************************************************************************* * Code ******************************************************************************/ // // Timer interrupt service routine. Refresh the display every 5 ms. // Also, increment the millisecond counter // void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } }
/* Main function */ int main(void) { char Txt[10];
● 185
Getting Started with NXP i.MX Development Board - UK.indd 185
26-10-2023 09:34
Get Started with the NXP i.MX RT1010 Development Kit
int Count = 0; BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } lcd_init();
// Initialize LCD
lcd_print("Count:");
// Display Count:
while(1) { lcd_goto(1,1); while(GPIO_PinRead(GPIO1, LDR) == 0);
// Wait if light (no bottle)
Count++; itoa(Count, Txt, 10);
// Convert to string
lcd_print(Txt);
// Display Count
while(GPIO_PinRead(GPIO1, LDR) == 1);
// Wait if dark
SysTick_DelayTicks(100); } }
Figure 9.14 Program listing
Figure 9.15 Configure GPIO ports
● 186
Getting Started with NXP i.MX Development Board - UK.indd 186
26-10-2023 09:35
Chapter 9 • Using LCDs
9.8 Project 5 – Event counter – using external interrupts Description: In this project a button is connected to the development board where pressing the button simulates the occurrence of external events. When the button is pressed, an external interrupt is generated and this causes a count to be incremented by one. The total count is displayed on LCD. Block diagram: Figure 9.16 shows the block diagram of the project.
Figure 9.16 Block diagram of the project Circuit diagram: The circuit diagram is shown in Figure 9.17. One pin of the button (called the EventButton) is connected to +3.3 V where the other pin is connected to port pin J56, pin 14 (IO15). The button state is pulled down in software. Therefore, normally, the state of the button is at logic LOW and goes to logic HIGH when the button is pressed (i.e. on the low-to-high transition of the button output).
Figure 9.17 Circuit diagram of the project Program listing: before developing the program, the GPIO pins must be configured as follows using the Pins option of menu item ConfigTools:
● 187
Getting Started with NXP i.MX Development Board - UK.indd 187
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
J57, pin 6 (IO19) Direction: output J57, pin 8 (IO18) Direction: output J57, pin 10 (IO17) Direction: output J57, pin 12 (IO20) Direction: output J57, pin 18 (IO1) Direction: output J57, pin 20 (IO2) Direction: output J56, pin 14 (IO15) Direction: input, GPIO interrupt: Rising Edge, Pull Up/Down: 100 kΩ Pull Down Figure 9.18 shows the program listing (lcd-interrupt). At the beginning of the program, EventButton is assigned to 15. GPIO_IRQHandler and GPIO_IRQ are defined, and the variable flag is cleared to 0. The interrupt service routine is the function named GPIO_ IRQHandler(). This function is called whenever an external interrupt occurs by pressing the button. Here, the interrupt flag is cleared so that new interrupts can be detected. Also, the variable flag is set to 1 to indicate that an external interrupt has occurred. Inside the main program loop, interrupts are enabled on button EventButton. The program displays text COUNT: on the top row of the LCD and waits until an external interrupt occurs. When variable flag is set (i.e. EventButton is pressed) then the variable Count is incremented, converted into a character array and displayed on the bottom row of the LCD. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *LCD- event counter - external interrupt*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "LCD.h" void SysTick_DelayTicks(uint32_t); /******************************************************************************* * Definitions ******************************************************************************/ #define EventButton 15U #define GPIO_IRQHandler GPIO1_Combined_0_15_IRQHandler #define GPIO_IRQ GPIO1_Combined_0_15_IRQn /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables
● 188
Getting Started with NXP i.MX Development Board - UK.indd 188
26-10-2023 09:35
Chapter 9 • Using LCDs
******************************************************************************/ volatile uint32_t g_systickCounter; volatile int flag = 0; /******************************************************************************* * Code ******************************************************************************/ // // Timer interrupt service routine. Refresh the display every 5 ms. // Also, increment the millisecond counter // void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } } // // External interrupt service routine. Clear the interrupts and set flag // void GPIO_IRQHandler(void) { GPIO_PortClearInterruptFlags(GPIO1, 1U << EventButton); flag = 1; SDK_ISR_EXIT_BARRIER; }
/* Main function */ int main(void) { char Txt[10]; int Count = 0; BOARD_ConfigMPU(); BOARD_InitBootPins();
● 189
Getting Started with NXP i.MX Development Board - UK.indd 189
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } /* Enable GPIO pin interrupt */ GPIO_PortEnableInterrupts(GPIO1, 1U << EventButton);
// Enable pin 15 int
EnableIRQ(GPIO_IRQ);
// Enable interrupts
lcd_init();
// Initialize LCD
lcd_print("Count:");
// Display Count:
while(1) { lcd_goto(1,1); if(flag == 1) {
flag = 0;
Count++; itoa(Count, Txt, 10);
// Convert to chars
lcd_print(Txt);
// Display Count
} } }
Figure 9.18 Program listing
● 190
Getting Started with NXP i.MX Development Board - UK.indd 190
26-10-2023 09:35
Chapter 10 • Analog-To-Digital Converter (Adc)
Chapter 10 • Analog-To-Digital Converter (ADC) 10.1 Overview Most sensors in real life are analog and give analog output voltages or currents which are proportional to the measured variable. Such sensors cannot be directly connected to digital computers without using ADCs. In this chapter you will learn how to use the ADC channels of the development kit. Most ADCs for general-purpose applications are 8-bits or 10-bits wide, although some higher-grade professional ones are 16 or even 32-bit wide. The conversion time of an ADC is one of its important specifications. This is the time taken for the ADC to convert an analog input into digital. The shorter the conversion time, the better. Some cheaper ADCs give the converted digital data in serial format, while some more expensive professional ones provide parallel digital outputs. The ADC of the i.MXRT 1010 processor has a resolution of 12 bits, thus converting an analog input voltage into 4096 (0 to 4095) levels. There are 15 channels from input 0 (GPIO_AD_00) to input 14 (GPIO_AD_14). The ADC can be configured for 8, 10, or 12 bits of resolution. ADC conversion can be done either by polling or it can be interrupt-driven. When using polling, the program waits until the conversion is ready. In interrupt-driven applications, an interrupt is generated when the conversion is complete.
10.2 Project 1 – Voltmeter Description: This is a voltmeter project. The voltage to be measured is applied to an analog input and its value in millivolts is displayed on the LCD. The maximum allowable input voltage is +3.3 V. It is shown later in the project how to use a resistive voltage divider circuit to extend the range of the voltmeter. Circuit diagram: As shown in Figure 10.1, in this project the voltage to be measured is applied to Arduino header J56, pin14 (GPIO_AD_01, i.e. ADC channel 1). The LCD is connected to the development kit as in the previous LCD-based projects.
Figure 10.1 Circuit diagram of the project
● 191
Getting Started with NXP i.MX Development Board - UK.indd 191
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
Program listing: You should configure the port pins connected to the LCD as outputs, and the analog pin at header J56, pin 14 (GPIO_AD_01) as an analog input (Figure 10.2).
Figure 10.2 Configure the used GPIO ports At the beginning of the program the ADC_BASE, ADC_USER_CHANNEL, and ADC_ CHANNEL_GROUP are set to ADC1, 1U, and 0U respectively. The ADC channel is configured as follows: // // Configure ADC channel // adcConfigStruct.enableAsynchronousClockOutput = true; adcConfigStruct.enableOverWrite =
false;
adcConfigStruct.enableContinuousConversion =
false;
adcConfigStruct.enableHighSpeed =
false;
adcConfigStruct.enableLowPower =
false;
adcConfigStruct.enableLongSample =
false;
adcConfigStruct.referenceVoltageSource =
kADC_ReferenceVoltageSourceAlt0;
adcConfigStruct.samplePeriodMode =
kADC_SamplePeriod2or12Clocks;
adcConfigStruct.clockSource =
kADC_ClockSourceAD;
adcConfigStruct.clockDriver =
kADC_ClockDriver1;
adcConfigStruct.resolution =
kADC_Resolution12Bit;
Files fsl_adc.h and fsl_adc.c must be in the drivers folder before the project is compiled. They can be included in the project as follows: • Click the orange icon to open Manage SDK Components • Click tab Drivers • Click to expand Device • Click to expand SDK Drivers • Click to include ADC driver adc_12b1msps.sar • Click OK to exit • Click drivers under Project Explorer and make sure that files fsl_adc.h and
● 192
Getting Started with NXP i.MX Development Board - UK.indd 192
26-10-2023 09:35
Chapter 10 • Analog-To-Digital Converter (Adc)
fsl_adc.c are included in the project Note that these files could also be included in the project during the project creation under Configure the project tab and by selecting tab Drivers and following the above steps Figure 10.2 shows the program listing (voltmeter). Note that calling function: ADC_GetDefaultConfig(&adcConfigStruct) sets the default channel configurations as above. Therefore, instead of the above configuration settings, you can just call this function. Interrupts on conversion are then disabled. Inside the main program loop, the conversion is triggered by calling the function ADC_SetChannelConfig(). The ADC is then calibrated by calling the function ADC_DoAutoCalibration(). The program then waits in a while loop until the conversion is complete. After this, the function ADC_GetChannelConversionValue() is called to read the converted digital data. This data is the raw reading from the channel in the range 0 to 4095. It is converted into millivolts by multiplying by 3300 and diving by 4096. The resulting voltage is then displayed on the second row of the LCD as shown in Figure 10.3. In this figure, the ADC input was connected to +3.3 V of the development kit. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *ADC - Voltmeter*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "LCD.h" #include "fsl_adc.h" /******************************************************************************* * Definitions ******************************************************************************/ #define ADC_BASE
ADC1
// ADC Base
#define ADC_USER_CHANNEL
1U
// ADC used
#define ADC_CHANNEL_GROUP 0U
// Channel group
/******************************************************************************* * Prototypes ******************************************************************************/ void SysTick_DelayTicks(uint32_t); /******************************************************************************* * Variables
● 193
Getting Started with NXP i.MX Development Board - UK.indd 193
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
******************************************************************************/ volatile uint32_t g_systickCounter; const uint32_t g_Adc_12bitFullRange = 4096U;
// 12-bits
int adcreading = 0; /******************************************************************************* * Code ******************************************************************************/ // // Timer interrupt service routine. Refresh the display every 5 ms. // Also, increment the millisecond counter // void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } }
/* Main function */ int main(void) { char Txt[10]; int Count = 0; BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) {
● 194
Getting Started with NXP i.MX Development Board - UK.indd 194
26-10-2023 09:35
Chapter 10 • Analog-To-Digital Converter (Adc)
} } adc_config_t adcConfigStruct; adc_channel_config_t adcChannelConfigStruct; // // Configure ADC channel // adcConfigStruct.enableAsynchronousClockOutput = true; adcConfigStruct.enableOverWrite =
false;
adcConfigStruct.enableContinuousConversion =
false;
adcConfigStruct.enableHighSpeed =
false;
adcConfigStruct.enableLowPower =
false;
adcConfigStruct.enableLongSample =
false;
adcConfigStruct.referenceVoltageSource = kADC_ReferenceVoltageSourceAlt0; adcConfigStruct.samplePeriodMode =
kADC_SamplePeriod2or12Clocks;
adcConfigStruct.clockSource =
kADC_ClockSourceAD;
adcConfigStruct.clockDriver =
kADC_ClockDriver1;
adcConfigStruct.resolution =
kADC_Resolution12Bit;
//
ADC_GetDefaultConfig(&adcConfigStruct); ADC_Init(ADC_BASE, &adcConfigStruct);
// Initialize
kStatus_Success == ADC_DoAutoCalibration(ADC_BASE);
// Auto calibrate
adcChannelConfigStruct.channelNumber = ADC_USER_CHANNEL;
// ADC channel
adcChannelConfigStruct.enableInterruptOnConversionCompleted = false; lcd_init();
// Initialize LCD
lcd_print("Voltmeter (mV):");
// Display heading
while(1) { lcd_goto(1,1); ADC_SetChannelConfig(ADC_BASE, ADC_CHANNEL_GROUP, &adcChannelConfigStruct); while (0U == ADC_GetChannelStatusFlags(ADC_BASE, ADC_CHANNEL_GROUP)) { } adcreading = ADC_GetChannelConversionValue(ADC_BASE, ADC_CHANNEL_GROUP); adcreading = adcreading * 3300 / g_Adc_12bitFullRange; itoa(adcreading, Txt, 10);
// Convert to chars
lcd_print(Txt);
// Display in mV
● 195
Getting Started with NXP i.MX Development Board - UK.indd 195
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
SysTick_DelayTicks(1000);
// Wait 1 second
} }
Figure 10.3 Program listing
Figure 10.4 Example display Extending the voltmeter range The useful range of our voltmeter can easily be extended by using resistive potential divider circuits. For example, the circuit shown in Figure 10.5 attenuates the input voltage by a factor of 4 so that input voltages up to 13 V can be measured. You should, of course, have to multiply the readings by 4.
Figure 10.5 Resistive potential divider circuit
10.3 Project 2 – Analog temperature sensor Description: In this project, you will be using the LM35 analog temperature sensor chip to measure and display the ambient temperature on the LCD every second. Block diagram: Figure 10.6 shows the block diagram of the project which consists of the LM35 sensor chip, the development kit, and the LCD.
Figure 10.6 Block diagram of the project
● 196
Getting Started with NXP i.MX Development Board - UK.indd 196
26-10-2023 09:35
Chapter 10 • Analog-To-Digital Converter (Adc)
Circuit diagram: LM35 is a 3-pin temperature sensor chip (Figure 10.7) with the pins: +V, GND, and OUT. The OUT pin is connected to analog input GPIO_AD_01 (J56, pin 14) of the development kit.
Figure 10.7 LM35 temperature sensor chip The LM35 temperature sensor has the following basic specifications: • Calibrated in Celsius • Linear 10 mV/°C output • 0.5ºC accuracy • -55ºC to +125ºC operation • Operation from +4 V to +30 V • Less than 60 μA current drain • Low self-heating (0.08ºC in still air) The output voltage of LM35 is linearly proportional to the measured temperature and is given by: T = Vo / 10 Where T is the measured temperature in degrees C, and Vo is the sensor output voltage in millivolts. For example, 250 mV output corresponds to 25ºC, 300 mV corresponds to 30ºC and so on. For simplicity, to find the measured temperature, divide the analog voltage read in millivolts by 10. Figure 10.8 shows the circuit diagram of the project.
● 197
Getting Started with NXP i.MX Development Board - UK.indd 197
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
Figure 10.8 Circuit diagram of the project Program listing: The program listing is shown in Figure 10.9 (LM35). Make sure that files fsl_adc.h and fsl_adc.h are included in the project, as described in the previous project. The main program reads the raw data output of the LM35. The temperature in Degrees C is then obtained by multiplying this value by 330 (i.e. 3300 mV / 10) and diving by 4096. Variable mV stores the temperature as a floating-point number. The decimal part (TDec) and fractional parts (TFrac) of the temperature are extracted and displayed on the LCD as shown in Figure 10.9. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *ADC - LM35*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "LCD.h" #include "fsl_adc.h" #include "stdio.h" /******************************************************************************* * Definitions ******************************************************************************/ #define ADC_BASE
ADC1
// ADC Base
#define ADC_USER_CHANNEL
1U
// ADC used
#define ADC_CHANNEL_GROUP 0U
// Channel group
/*******************************************************************************
● 198
Getting Started with NXP i.MX Development Board - UK.indd 198
26-10-2023 09:35
Chapter 10 • Analog-To-Digital Converter (Adc)
* Prototypes ******************************************************************************/ void SysTick_DelayTicks(uint32_t); /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; const uint32_t g_Adc_12bitFullRange = 4096U;
// 12-bits
/******************************************************************************* * Code ******************************************************************************/ // // Timer interrupt service routine. Refresh the display every 5 ms. // Also, increment the millisecond counter // void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } }
/* Main function */ int main(void) { char Txt[20]; float mV; int TDec, TFrac, adcreading;
BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole();
● 199
Getting Started with NXP i.MX Development Board - UK.indd 199
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
/* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } adc_config_t adcConfigStruct; adc_channel_config_t adcChannelConfigStruct; // // Configure ADC channel // adcConfigStruct.enableAsynchronousClockOutput = true; adcConfigStruct.enableOverWrite =
false;
adcConfigStruct.enableContinuousConversion =
false;
adcConfigStruct.enableHighSpeed =
false;
adcConfigStruct.enableLowPower =
false;
adcConfigStruct.enableLongSample =
false;
adcConfigStruct.referenceVoltageSource = kADC_ReferenceVoltageSourceAlt0; adcConfigStruct.samplePeriodMode =
kADC_SamplePeriod2or12Clocks;
adcConfigStruct.clockSource =
kADC_ClockSourceAD;
adcConfigStruct.clockDriver =
kADC_ClockDriver1;
adcConfigStruct.resolution =
kADC_Resolution12Bit;
//
ADC_GetDefaultConfig(&adcConfigStruct); ADC_Init(ADC_BASE, &adcConfigStruct);
// Initialize
kStatus_Success == ADC_DoAutoCalibration(ADC_BASE);
// Auto calibrate
adcChannelConfigStruct.channelNumber = ADC_USER_CHANNEL;
// ADC channel
adcChannelConfigStruct.enableInterruptOnConversionCompleted = false; lcd_init();
// Initialize LCD
lcd_print("Temperature (C)");
// Display heading
while(1) { lcd_goto(1,1); ADC_SetChannelConfig(ADC_BASE, ADC_CHANNEL_GROUP, &adcChannelConfigStruct); while (0U == ADC_GetChannelStatusFlags(ADC_BASE, ADC_CHANNEL_GROUP)) { }
● 200
Getting Started with NXP i.MX Development Board - UK.indd 200
26-10-2023 09:35
Chapter 10 • Analog-To-Digital Converter (Adc)
adcreading = ADC_GetChannelConversionValue(ADC_BASE, ADC_CHANNEL_GROUP); mV = adcreading * 330.0 / g_Adc_12bitFullRange; TDec = mV;
// Decimal part
mV = 10.0*(mV - TDec); TFrac = mV;
// Fractional part
itoa(TDec, Txt, 10); lcd_print(Txt);
// Display decimal
lcd_print(".");
// Display "."
itoa(TFrac, Txt, 10);
// Display fraction
lcd_print(Txt); SysTick_DelayTicks(1000);
// Wait 1 second
} }
Figure 10.9 Program listing
Figure 10.10 The display
10.4 Project 3 – ON/OFF temperature controller Description: Temperature control is important in many chemical industrial, commercial, and in domestic applications as well as in many other applications. A temperature control system basically consists of a temperature sensor, a heater, a fan (optional), and a controller. Negative feedback is used to control the heater so that the temperature is kept at the desired set-point value. Accurate temperature control systems are based on the PID (Proportional+Integral+Derivative) algorithms. In this project, an ON/OFF type simple control system is designed. ON/OFF temperature control systems commonly use relays to turn the heater ON or OFF, depending on the set-point temperature and the measured temperature. If the measured temperature is below the set-point value, then the relay is activated, which turns the heater ON so that the temperature reaches the set-point value. If, on the other hand, the measured temperature is above the set-point value, then the relay is deactivated to turn OFF the heater so that the temperature is lowered and reaches the set-point value. In this project, a LM35 type temperature sensor chip is used together with a heater, a relay, and an LED to control the temperature of a small room. The heater is turned ON by the relay if the measured room temperature (RoomTemp) is below the set-point temperature (SetTemp), and it is turned OFF if it is above the set-point value. The LED turns ON if the room temperature is below the set point value to indicate that the heater is ON. This process is repeated every 3 seconds. The aim of this project is to show how an ON/OFF type
● 201
Getting Started with NXP i.MX Development Board - UK.indd 201
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
temperature controller system can be designed using a low-cost temperature sensor chip with the development kit. Block diagram: Figure 10.11 shows the block diagram of the project.
Figure 10.11 Block diagram of the project Circuit diagram: The circuit diagram of the project is shown in Figure 10.12. The LM35 sensor chip is connected to analog channel GPIO_AD_0 (Arduino header J56, pin14, IN1) as in the previous project. An external LED is connected to pin ADC12_2 (Arduino header J56, pin 16, IO16) through a current limiting 1 kΩ resistor. A 3.3 V relay is connected to pin UART1_RXD (Arduino header J56, pin 2, IO9). The connections between the development kit and various components are as follows: Development kit J56, pin 14 J56, pin 16 J56, pin 2
Component TMP36 out LED Relay
Configuration configure as analog input configure as output configure as output
Figure 10.12 Circuit diagram of the project Operation of the project The operation of the project is described in Figure 10.13 as a PDL (Program Description Language).
● 202
Getting Started with NXP i.MX Development Board - UK.indd 202
26-10-2023 09:35
Chapter 10 • Analog-To-Digital Converter (Adc)
BEGIN Read the set temperature (SetTemp) Read the maximum temperature (MaxTemp) DO FOREVER
Read the room temperature (RoomTemp)
IF SetTemp > RoomTemp THEN Activate relay LED ON ELSE Deactivate relay LED OFF ENDIF
Wait 3 seconds
ENDDO END
Figure 10.13 PDL of the project Program listing: Configure the LED and Relay as outputs and the LM35 as analog input as in the previous project. Figure 10.14 shows the program listing (LM35-on-off-control). Make sure that files fsl_adc.h and fsl_adc.h are included in the project, as described in the previous projects. The desired temperature is set to 24ºC and is stored in variable SetTemp. Inside the program loop, the room temperature (RoomTemp) is read and compared with the desired temperature (SetTemp). If the room temperature is below the desired value, then both the relay (heater) and the LED are turned ON; otherwise they are both turned OFF. This process is repeated after a delay of three seconds. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *ADC - LM35 - ON-OFF Control*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "fsl_adc.h" #include "stdio.h" /******************************************************************************* * Definitions ******************************************************************************/ #define ADC_BASE
ADC1
// ADC Base
#define ADC_USER_CHANNEL
1U
// ADC used
● 203
Getting Started with NXP i.MX Development Board - UK.indd 203
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
#define ADC_CHANNEL_GROUP 0U
// Channel group
#define LED 16U // LED #define RELAY 9U // Relay /******************************************************************************* * Prototypes ******************************************************************************/ void SysTick_DelayTicks(uint32_t); /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; const uint32_t g_Adc_12bitFullRange = 4096U;
// 12-bits
/******************************************************************************* * Code ******************************************************************************/ // // Timer interrupt service routine. Refresh the display every 5 ms. // Also, increment the millisecond counter // void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } }
/* Main function */ int main(void) { char Txt[20]; int adcreading; int RoomTemp, SetTemp = 24;
● 204
Getting Started with NXP i.MX Development Board - UK.indd 204
26-10-2023 09:35
Chapter 10 • Analog-To-Digital Converter (Adc)
BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } adc_config_t adcConfigStruct; adc_channel_config_t adcChannelConfigStruct; // // Configure ADC channel // adcConfigStruct.enableAsynchronousClockOutput = true; adcConfigStruct.enableOverWrite =
false;
adcConfigStruct.enableContinuousConversion =
false;
adcConfigStruct.enableHighSpeed =
false;
adcConfigStruct.enableLowPower =
false;
adcConfigStruct.enableLongSample =
false;
adcConfigStruct.referenceVoltageSource = kADC_ReferenceVoltageSourceAlt0; adcConfigStruct.samplePeriodMode =
kADC_SamplePeriod2or12Clocks;
adcConfigStruct.clockSource =
kADC_ClockSourceAD;
adcConfigStruct.clockDriver =
kADC_ClockDriver1;
adcConfigStruct.resolution =
kADC_Resolution12Bit;
//
ADC_GetDefaultConfig(&adcConfigStruct); ADC_Init(ADC_BASE, &adcConfigStruct);
// Initialize
kStatus_Success == ADC_DoAutoCalibration(ADC_BASE);
// Auto calibrate
adcChannelConfigStruct.channelNumber = ADC_USER_CHANNEL;
// ADC channel
adcChannelConfigStruct.enableInterruptOnConversionCompleted = false; GPIO_PinWrite(GPIO1, RELAY, 0U);
// Relay OFF
GPIO_PinWrite(GPIO1, LED,0U);
// LED OFF
while(1) { ADC_SetChannelConfig(ADC_BASE, ADC_CHANNEL_GROUP, &adcChannelConfigStruct); while (0U == ADC_GetChannelStatusFlags(ADC_BASE, ADC_CHANNEL_GROUP))
● 205
Getting Started with NXP i.MX Development Board - UK.indd 205
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
{ } adcreading = ADC_GetChannelConversionValue(ADC_BASE, ADC_CHANNEL_GROUP); RoomTemp = adcreading * 330 / g_Adc_12bitFullRange; if(RoomTemp < SetTemp) { GPIO_PinWrite(GPIO1, RELAY, 1U);
// Relay ON
GPIO_PinWrite(GPIO1, LED, 1U);
// LED ON
} else { GPIO_PinWrite(GPIO1, RELAY, 0U);
// Relay OFF
GPIO_PinWrite(GPIO1, LED, 0U);
// LED OFF
} SysTick_DelayTicks(3000);
// Wait 3 secs
} }
Figure 10.14 Program listing
10.5 Project 4 – ON/OFF temperature controller – using LCD Description: This project is similar to the previous one, but here an LCD is used as a display. The display shows the SetTemp and the RoomTemp every time the display is refreshed (every 3 seconds). Block diagram: Figure 10.15 shows the block diagram of the project.
Figure 10.15 Block diagram of the project Circuit diagram; The circuit diagram is shown in Figure 10.16. The LCD is connected as in the previous LCD-based projects. Also, the relay and the LED are connected as in the previous project.
● 206
Getting Started with NXP i.MX Development Board - UK.indd 206
26-10-2023 09:35
Chapter 10 • Analog-To-Digital Converter (Adc)
Figure 10.16 Circuit diagram Program listing: Figure 10.17 shows the program listing (LM35-on-off-cotrol-lcd). Make sure that files fsl_adc.h and fsl_adc.h are included in the project, as described in the previous projects. Text SetTemp: is displayed on the top row of the LCD. Similarly, the text RoomTemp: is displayed at the bottom row. If the variable RoomTemp is lower than the SetTemp than both the LED and the relay are turned ON; otherwise they are both turned OFF. This process repeats every 3 seconds. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *ADC - LM35 - ON-OFF Control with LCD*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "LCD.h" #include "fsl_adc.h" #include "stdio.h" /******************************************************************************* * Definitions ******************************************************************************/ #define ADC_BASE
ADC1
// ADC Base
#define ADC_USER_CHANNEL
1U
// ADC used
● 207
Getting Started with NXP i.MX Development Board - UK.indd 207
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
#define ADC_CHANNEL_GROUP 0U
// Channel group
#define LED 16U // LED #define RELAY 9U // Relay /******************************************************************************* * Prototypes ******************************************************************************/ void SysTick_DelayTicks(uint32_t); /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; const uint32_t g_Adc_12bitFullRange = 4096U;
// 12-bits
/******************************************************************************* * Code ******************************************************************************/ // // Timer interrupt service routine. Refresh the display every 5 ms. // Also, increment the millisecond counter // void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } }
/* Main function */ int main(void) { char Txt[20]; int adcreading; int RoomTemp, SetTemp = 24;
● 208
Getting Started with NXP i.MX Development Board - UK.indd 208
26-10-2023 09:35
Chapter 10 • Analog-To-Digital Converter (Adc)
BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } adc_config_t adcConfigStruct; adc_channel_config_t adcChannelConfigStruct; // // Configure ADC channel // adcConfigStruct.enableAsynchronousClockOutput = true; adcConfigStruct.enableOverWrite =
false;
adcConfigStruct.enableContinuousConversion =
false;
adcConfigStruct.enableHighSpeed =
false;
adcConfigStruct.enableLowPower =
false;
adcConfigStruct.enableLongSample =
false;
adcConfigStruct.referenceVoltageSource = kADC_ReferenceVoltageSourceAlt0;
//
adcConfigStruct.samplePeriodMode =
kADC_SamplePeriod2or12Clocks;
adcConfigStruct.clockSource =
kADC_ClockSourceAD;
adcConfigStruct.clockDriver =
kADC_ClockDriver1;
adcConfigStruct.resolution =
kADC_Resolution12Bit;
ADC_GetDefaultConfig(&adcConfigStruct); ADC_Init(ADC_BASE, &adcConfigStruct);
// Initialize
kStatus_Success == ADC_DoAutoCalibration(ADC_BASE);
// Auto calibrate
adcChannelConfigStruct.channelNumber = ADC_USER_CHANNEL;
// ADC channel
adcChannelConfigStruct.enableInterruptOnConversionCompleted = false; GPIO_PinWrite(GPIO1, RELAY, 0U);
// Relay OFF
GPIO_PinWrite(GPIO1, LED,0U);
// LED OFF
lcd_init();
// Initialize LCD
lcd_print(" SetTemp:");
// Display heading
itoa(SetTemp, Txt, 10); lcd_print(Txt);
● 209
Getting Started with NXP i.MX Development Board - UK.indd 209
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
while(1) { lcd_goto(1,1); lcd_print("RoomTemp:"); ADC_SetChannelConfig(ADC_BASE, ADC_CHANNEL_GROUP, &adcChannelConfigStruct); while (0U == ADC_GetChannelStatusFlags(ADC_BASE, ADC_CHANNEL_GROUP)) { } adcreading = ADC_GetChannelConversionValue(ADC_BASE, ADC_CHANNEL_GROUP); RoomTemp = adcreading * 330 / g_Adc_12bitFullRange; itoa(RoomTemp, Txt, 10); lcd_print(Txt);
// Display head
if(RoomTemp < SetTemp) { GPIO_PinWrite(GPIO1, RELAY, 1U);
// Relay ON
GPIO_PinWrite(GPIO1, LED, 1U);
// LED ON
} else { GPIO_PinWrite(GPIO1, RELAY, 0U);
// Relay OFF
GPIO_PinWrite(GPIO1, LED, 0U);
// LED OFF
} SysTick_DelayTicks(3000);
// Wait 3 secs
} }
Figure 10.17 Program listing Figure 10.18 shows the display.
Figure 10.18 The display
● 210
Getting Started with NXP i.MX Development Board - UK.indd 210
26-10-2023 09:35
Chapter 10 • Analog-To-Digital Converter (Adc)
10.6 Project 5 – Measuring the ambient light intensity Description: In this project, a light-dependent-resistor (LDR) is used to measure and display the ambient light intensity on an LCD. Block diagram: Figure 10.19 shows the block diagram of the project.
Figure 10.19 Block diagram of the project Circuit diagram: The circuit diagram of the project is shown in Figure 10.20. A 10 kΩ fixed resistor is used in the resistive voltage divider circuit. The voltage across this resistor is measured using channel 1 of the ADC (J56, pin 14).
Figure 10.20 Circuit diagram of the project Program listing: Figure 10.21 shows the program listing (ambient-ldr). Make sure that files fsl_adc.h and fsl_adc.h are included in the project, as described in the previous projects. The program reads the analog voltage across the fixed resistor. Notice that there is no need to know the absolute voltage. You can just use the digital value of the voltage (0 to 4095) read (raw in this program). The reading is displayed starting on the top row of the LCD in the format: Light: nnnnn. This value should be calibrated to give the actual physical light intensity in Lux.
● 211
Getting Started with NXP i.MX Development Board - UK.indd 211
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
/* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *ADC - Ambient light intensity*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "LCD.h" #include "fsl_adc.h" #include "stdio.h" /******************************************************************************* * Definitions ******************************************************************************/ #define ADC_BASE
ADC1
// ADC Base
#define ADC_USER_CHANNEL
1U
// ADC used
#define ADC_CHANNEL_GROUP 0U
// Channel group
/******************************************************************************* * Prototypes ******************************************************************************/ void SysTick_DelayTicks(uint32_t); /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; const uint32_t g_Adc_12bitFullRange = 4096U;
// 12-bits
/******************************************************************************* * Code ******************************************************************************/ // // Timer interrupt service routine. Refresh the display every 5 ms. // Also, increment the millisecond counter // void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; }
● 212
Getting Started with NXP i.MX Development Board - UK.indd 212
26-10-2023 09:35
Chapter 10 • Analog-To-Digital Converter (Adc)
} void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } }
/* Main function */ int main(void) { char Txt[20]; int adcreading; BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } adc_config_t adcConfigStruct; adc_channel_config_t adcChannelConfigStruct; // // Configure ADC channel // adcConfigStruct.enableAsynchronousClockOutput = true; adcConfigStruct.enableOverWrite =
false;
adcConfigStruct.enableContinuousConversion =
false;
adcConfigStruct.enableHighSpeed =
false;
adcConfigStruct.enableLowPower =
false;
adcConfigStruct.enableLongSample =
false;
adcConfigStruct.referenceVoltageSource = kADC_ReferenceVoltageSourceAlt0; adcConfigStruct.samplePeriodMode =
kADC_SamplePeriod2or12Clocks;
adcConfigStruct.clockSource =
kADC_ClockSourceAD;
● 213
Getting Started with NXP i.MX Development Board - UK.indd 213
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
adcConfigStruct.clockDriver =
kADC_ClockDriver1;
adcConfigStruct.resolution =
kADC_Resolution12Bit;
//
ADC_GetDefaultConfig(&adcConfigStruct); ADC_Init(ADC_BASE, &adcConfigStruct);
// Initialize
kStatus_Success == ADC_DoAutoCalibration(ADC_BASE);
// Auto calibrate
adcChannelConfigStruct.channelNumber = ADC_USER_CHANNEL;
// ADC channel
adcChannelConfigStruct.enableInterruptOnConversionCompleted = false; lcd_init();
// Initialize LCD
while(1) { lcd_clear(); lcd_home_cursor(); lcd_print("Light: "); ADC_SetChannelConfig(ADC_BASE, ADC_CHANNEL_GROUP, &adcChannelConfigStruct); while (0U == ADC_GetChannelStatusFlags(ADC_BASE, ADC_CHANNEL_GROUP)) { } adcreading = ADC_GetChannelConversionValue(ADC_BASE, ADC_CHANNEL_GROUP); itoa(adcreading, Txt, 10); lcd_print(Txt);
// Display result
SysTick_DelayTicks(1000);
// WAit 1 sec
} }
Figure 10.21 Program listing Calibration A light meter is required to calibrate the readings. Measurements should be made at different light levels and a table should be created to list the Lux readings of the meter and the corresponding output from the ADC. Then, a formula can be derived that describes the relationship between the light level and the ADC readings. Alternatively, this table can be indexed for a given ADC reading to find the corresponding light level. Interpolation can be made for values between two readings.
10.7 Project 6 – Ohmmeter Description: This is an ohmmeter project. The project measures the value of an unknown resistor and displays on the LCD.
● 214
Getting Started with NXP i.MX Development Board - UK.indd 214
26-10-2023 09:35
Chapter 10 • Analog-To-Digital Converter (Adc)
Circuit diagram: The circuit diagram of the project is shown in Figure 10.22. A fixed resistor (10 kΩ) is used in series with the unknown resistor. The program measures the voltage across the fixed resistor and then calculates the value of the unknown resistor (Rx).
Figure 10.22 Circuit diagram of the project Program listing: Figure 10.23 shows the program listing (ohmmeter). If RF is the fixed resistance and Rx is the unknown resistance, assuming the circuit is supplied from +3.3 V (3300 mV), the voltage at the output of the fixed resistor will be: V = 3300 × RF / (RF + Rx) If we choose RF to be 10 kΩ then, V = 33000 / (10 + Rx) Where V is in millivolts and RF and Rx are in kilohms. As Rx changes from, say, 1 kΩ to 1 MΩ, the voltage across the fixed resistor will change from: V = 33000/11 = 3000 mV to V = 33000/1001 = 33 mV We can easily measure these voltages with the ADC. Therefore, the resistance measurement range of our ohmmeter is well below 1 kΩ and above 1 MΩ. What we really want is to measure resistance Rx. We can write: Rx = 3300 × RF/V – RF
● 215
Getting Started with NXP i.MX Development Board - UK.indd 215
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
Remembering that the ADC resolution is 4096 steps and RF = 10 kΩ, we can write: Rx = 4096 × RF / Vm – RF Where Vm is the digital value directly read from the ADC. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *ADC - Ohmmeter*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "LCD.h" #include "fsl_adc.h" #include "stdio.h" /******************************************************************************* * Definitions ******************************************************************************/ #define ADC_BASE
ADC1
// ADC Base
#define ADC_USER_CHANNEL
1U
// ADC used
#define ADC_CHANNEL_GROUP 0U
// Channel group
/******************************************************************************* * Prototypes ******************************************************************************/ void SysTick_DelayTicks(uint32_t); /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t g_systickCounter; const uint32_t g_Adc_12bitFullRange = 4096U;
// 12-bits
/******************************************************************************* * Code ******************************************************************************/ // // Timer interrupt service routine. Refresh the display every 5 ms. // Also, increment the millisecond counter //
● 216
Getting Started with NXP i.MX Development Board - UK.indd 216
26-10-2023 09:35
Chapter 10 • Analog-To-Digital Converter (Adc)
void SysTick_Handler(void) { if (g_systickCounter != 0U) { g_systickCounter--; } } void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } }
/* Main function */ int main(void) { char Txt[20]; int adcreading; float Rx; const float RF = 10.0; uint32_t RxOhms; BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); /* Set systick reload value to generate 1 ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } adc_config_t adcConfigStruct; adc_channel_config_t adcChannelConfigStruct; // // Configure ADC channel // adcConfigStruct.enableAsynchronousClockOutput = true;
● 217
Getting Started with NXP i.MX Development Board - UK.indd 217
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
adcConfigStruct.enableOverWrite =
false;
adcConfigStruct.enableContinuousConversion =
false;
adcConfigStruct.enableHighSpeed =
false;
adcConfigStruct.enableLowPower =
false;
adcConfigStruct.enableLongSample =
false;
adcConfigStruct.referenceVoltageSource = kADC_ReferenceVoltageSourceAlt0; adcConfigStruct.samplePeriodMode =
kADC_SamplePeriod2or12Clocks;
adcConfigStruct.clockSource =
kADC_ClockSourceAD;
adcConfigStruct.clockDriver =
kADC_ClockDriver1;
adcConfigStruct.resolution =
kADC_Resolution12Bit;
//
ADC_GetDefaultConfig(&adcConfigStruct); ADC_Init(ADC_BASE, &adcConfigStruct);
// Initialize
kStatus_Success == ADC_DoAutoCalibration(ADC_BASE);
// Auto calibrate
adcChannelConfigStruct.channelNumber = ADC_USER_CHANNEL;
// ADC channel
adcChannelConfigStruct.enableInterruptOnConversionCompleted = false; lcd_init();
// Initialize LCD
while(1) { lcd_clear(); ADC_SetChannelConfig(ADC_BASE, ADC_CHANNEL_GROUP, &adcChannelConfigStruct); while (0U == ADC_GetChannelStatusFlags(ADC_BASE, ADC_CHANNEL_GROUP)) { } adcreading = ADC_GetChannelConversionValue(ADC_BASE, ADC_CHANNEL_GROUP); Rx = g_Adc_12bitFullRange * RF / adcreading - RF;
// Calculate Rx
RxOhms = 1000 * Rx;
// In Ohms
lcd_print("R= ");
// Display R=
itoa(RxOhms, Txt, 10); lcd_print(Txt);
// Display Rx
SysTick_DelayTicks(1000);
// Wait 1 sec
} }
Figure 10.23 Program listing Example output from the program is shown in Figure 10.24. In this example, the unknown resistor was 1000 Ω.
● 218
Getting Started with NXP i.MX Development Board - UK.indd 218
26-10-2023 09:35
Chapter 10 • Analog-To-Digital Converter (Adc)
Figure 10.24 Example output
● 219
Getting Started with NXP i.MX Development Board - UK.indd 219
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
Chapter 11 • Using Pulse Width Modulation (PWM) 11.1 Overview Pulse Width Modulation (PWM) is commonly used to drive heavy loads such as motors, actuators and so on. As you will see in this chapter, PWM is basically a positive rectangular waveform whose pulse width can be changed. By changing the pulse width, you can effectively change the average voltage supplied to the load.
11.2 Basic theory of the pulse width modulation Pulse Width Modulation (PWM) is a commonly used technique for controlling the power delivered to analog loads using digital waveforms. Although analog voltages (and currents) can be used to control the delivered power, they have several drawbacks. Controlling large analog loads requires large voltages and currents that cannot easily be obtained using standard analog circuits and DACs. Precision analog circuits can be heavy, large, and expensive, and they are also sensitive to noise. By using the PWM technique, the average value of voltage (and current) fed to a load is controlled by switching the supply voltage ON and OFF at a fast rate. The longer the power on time, the higher is the voltage supplied to the load. Figure 11.1 shows a typical PWM waveform where the signal is basically a repetitive positive pulse, having the period T, ON time TON and OFF time of T – TON seconds. The minimum and maximum values of the voltage supplied to the load are 0 and VP, respectively. The PWM switching frequency is usually set to be very high (usually in the order of several kHz) so that it does not affect the load that uses the power. The main advantage of PWM is that the load is operated efficiently since the power loss in the switching device is very low. When the switch is ON, there is practically no voltage drop across the switch, and when the switch is OFF there is no current supplied to the load.
Figure 11.1 PWM waveform The duty cycle (or D) of a PWM waveform is defined as the ratio of the ON time to its period. Expressed mathematically, Duty Cycle (D) = TON / T The duty cycle is usually expressed as a percentage, and therefore, D = (TON / TOFF) × 100% ● 220
Getting Started with NXP i.MX Development Board - UK.indd 220
26-10-2023 09:35
Chapter 11 • Using Pulse Width Modulation (PWM)
By varying the duty cycle between 0% and 100%, we can effectively control the average voltage supplied to the load between 0 and Vp. The average value of the voltage applied to the load can be calculated by considering a general PWM waveform shown in Figure 1. The average value A of waveform f(t) with period T and peak value ymax and minimum value ymin is calculated as:
or,
In a PWM waveform, ymin = 0 and the above equation becomes
or,
As it can be seen from the above equation, the average value of the voltage supplied to the load is directly proportional to the duty cycle of the PWM waveform, and by varying the duty cycle we control the average load voltage. Figure 11.2 shows the average voltage for different values of the duty cycle.
Figure 11.2 Average voltage (shown as dashed line) supplied to a load It is interesting to notice that with correct low-pass filtering, the PWM can be used as a DAC if the MCU does not have a DAC channel. By varying the duty cycle, you can effectively vary the average analog voltage supplied to the load.
● 221
Getting Started with NXP i.MX Development Board - UK.indd 221
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
11.3 Features of the i.MXRT1010 processor PWM The PWM module (called the eFlexPWM module) on the development kit has the following features: • An eFlexPWM module contains four PWM submodules, each of which is set up to control a single half-bridge power stage. • 16 bits of resolution for center, edge aligned, and asymmetrical PWMs. • Fractional delay for enhanced resolution of the PWM period and edge placement. • PWM outputs that can operate as complementary pairs or independent channels. • Ability to accept signed numbers for PWM generation. • Independent control of both edges of each PWM output. • Support for synchronization to external hardware or other PWMs. • Double buffered PWM registers: - Integral reload rates from 1 to 16. • Halfcycle reload capability. • Multiple output trigger events can be generated per PWM cycle via hardware. • Support for double switching PWM outputs. • Fault inputs can be assigned to control multiple PWM outputs. • Programmable filters for fault inputs. • Independently programmable PWM output polarity. • Independent top and bottom dead time insertion. • Each complementary pair can operate with its own PWM frequency and dead time values. • Individual software control for each PWM output. • All outputs can be programmed to change simultaneously via a FORCE_OUT event. • The PWMX pin can optionally output a third PWM signal from each submodule. • Channels not used for PWM generation can be used for buffered output compare functions. • Channels not used for PWM generation can be used for input capture functions. • Enhanced dual edge capture functionality. • The option to supply the source for each complementary PWM signal pair from any of the following: - Crossbar Switch (XBAR) module outputs. - External ADC input, taking into account the values set in ADC high- and low-limit registers.
11.4 Operation of the PWM The PWM module on the development kit has been developed for complex motor control applications. Its operation is very complex, although it is very flexible. It has numerous features that may seem unnecessary for simple PWM generation. In this section, I will try to summarize its operation briefly.
● 222
Getting Started with NXP i.MX Development Board - UK.indd 222
26-10-2023 09:35
Chapter 11 • Using Pulse Width Modulation (PWM)
The general idea is that each eFlexPWM contains up to 4 submodules (plus one auxiliary submodule called PWMX, but it is preferable not to use it). Each submodule has its own set of counters and channel values to generate two PWMs because in motor control we usually use PWM pairs for high and low side of transistor bridges. Figure 11.3 shows the internal structure of a single PWM submodule that controls two PWM outputs. Basically, VAL 2 and 3 define the pulse width of one channel and VAL 4 and 5 define the other channel. Different from most PWM modules, instead of one register to define the pulse width, two registers are used: one for the start of the pulse, and one for the end of the pulse, so you are not defining the pulse width, instead you are defining the start and end of the pulse. This allows for a lot more flexibility in the generation of PWM waveforms. The INIT register defines the value at which the period starts, and VAL1 defines the end of the period. So, for example, a PWM duty cycle for the first channel pair would go: Start of count at INIT value > Start of duty cycle at VAL2 > end of duty cycle at VAL3 > end of period and VAL1 > return to INIT value and repeat. The process can be edge aligned or center aligned. In the edge aligned case: write 0 to INIT and to VAL2, write PWM period value for VAL1 (for example 1000 counts) and the use VAL3 for the duty cycle from 0 to 1000. For the center aligned case: repeat the similar setup but move VAL2 to half of the desired duty cycle on VAL3 the other half. For center aligned (Figure 11.4), it is easier to imagine the values are in the negative and positive range, this way, your period will go from a negative value (two's complement) to a positive value (the positive of the same number as the INIT). This way, the center aligned duty cycle is easy to define, it is simply the number of necessary counts divided by 2, VAL2 would be the negative of that and VAL3 the positive.
Figure 11.3 Internal structure of a PWM submodule
● 223
Getting Started with NXP i.MX Development Board - UK.indd 223
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
Figure 11.4 Center aligned operation PWM outputs can be configured to operate as a pair of complementary channels. The PWM pins are paired, as shown in Figure 11.5. Complimentary operation is useful in bridge type transistor-based motor drive control applications (Figure 11.6). Deadtime insertion logic is used for each submodule to create non-overlapping complementary signals when in complimentary mode of operation.
Figure 11.5 Complementary operation
Figure 11.6 Transistor-based AC-motor drive Figure 11.7 shows the PWM clocking block diagram of each submodule. Each submodule can select between three clock signals: the IPBus clock, EXT_CLK, and AUX_CLK. The EXT_CLK goes to all the submodules. The AUX_CLK signal is broadcast from submodule0 and can be selected as the clock source by other submodules so that the 8-bit prescaler and MCTRL[RUN] from submodule0 can control all the submodules. To permit lower PWM frequencies, the prescaler produces the PWM clock frequency by dividing the IPBus clock frequency by 1–128. The prescaler bits, CTRL[PRSC], select the prescaler divisor. This pres-
● 224
Getting Started with NXP i.MX Development Board - UK.indd 224
26-10-2023 09:35
Chapter 11 • Using Pulse Width Modulation (PWM)
caler is buffered and will not be used by the PWM generator until MCTRL[LDOK] is set and a new PWM reload cycle begins or CTRL[LDMOD] is set.
Figure 11.7 Clocking block diagram For each submodule, software can select between eight signal sources for the FORCE_OUT signal. The local signals are used when the user simply wants to change the signals on the output pins of the submodule without regard for synchronization with other submodules. However, if it is required that all signals on all submodule outputs change at the same time, the Master, EXT_SYNC, or EXT_FORCE signals should be selected. Register reload logic is used to determine when the outer set of registers for all double buffered register pairs will be transferred to the inner set of registers. The register reload event can be scheduled to occur every 'n' PWM cycles. A half cycle reload option is also supported. Further detailed information on the PWM can be obtained from the NXP reference manual: i.MX RT1010 Processor Reference Manual, Document Number: IMXRT1010RM Rev. 0, 09/2019
11.5 Project 1 – Mosquito repeller Description: It is a well-known fact that sound at 40 kHz ultrasonic frequencies can be used to repel mosquitos. In this project, a 40 kHz PWM waveform is generated. This waveform can be used to drive a piezo transducer to generate ultrasonic sound and repel mosquitos. The aim of this project is to show how a PWM waveform can be generated using the development kit. Block diagram: Figure 11.8 shows the block diagram of the project.
● 225
Getting Started with NXP i.MX Development Board - UK.indd 225
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
Figure 11.8 Block diagram of the project Circuit diagram: Arduino UNO header pin J57, pin 20 (IO2) is connected to a piezo ultrasonic transducer through a switching transistor. Figure 11.9 shows the circuit diagram of the project.
Figure 11.9 Circuit diagram of the project Program listing: Before writing the program, you must configure the PWM port. The steps are as follows: • Click ConfigTools followed by Pins • Click on pin number 11 (GPIO_02) and select PWM1 • Click on ConfigTools followed by Peripherals • Click on Peripheral drivers (Device specific) and select eFlexPWM (Figure 11.10)
● 226
Getting Started with NXP i.MX Development Board - UK.indd 226
26-10-2023 09:35
Chapter 11 • Using Pulse Width Modulation (PWM)
Figure 11.10 Select eFlexPWM • Click Update Code followed by OK Make sure you include pwm using the SDK Manager as described earlier. You are now ready to write the program. Figure 11.11 shows the program listing (PWM-40KHz). At the beginning of the program, the various definitions used in the program are included. The frequency is set to 40,000 Hz in definition APP_DEFAULT_PWM_FREQUENCE. Function PWM_DRV() sets the PWM clock source and configures the PWM port. Inside the main program loop, the PWM is configured and the timer is started. The PWM runs in the background and generates a 40 kHz square waveform as shown in Figure 11.12. In this figure, the frequency is shown to be 40,064 kHz and the height of the waveform is +3.3 V. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *PWM 40 kHz signal with 50% duty cycle*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "fsl_debug_console.h" #include "fsl_pwm.h" #include "fsl_xbara.h" /******************************************************************************* * Definitions ******************************************************************************/
● 227
Getting Started with NXP i.MX Development Board - UK.indd 227
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
#define BOARD_PWM_BASEADDR PWM1
// PWM base address
#define PWM_SRC_CLK_FREQ CLOCK_GetFreq(kCLOCK_IpgClk) #ifndef APP_DEFAULT_PWM_FREQUENCE #define APP_DEFAULT_PWM_FREQUENCE (40000UL)
// 40 kHz
#endif /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ /******************************************************************************* * Code ******************************************************************************/ static void PWM_DRV(void) { uint16_t deadTimeVal; pwm_signal_param_t pwmSignal[1]; uint32_t pwmSourceClockInHz; uint32_t pwmFrequencyInHz = APP_DEFAULT_PWM_FREQUENCE; pwmSourceClockInHz = PWM_SRC_CLK_FREQ; /* Set deadtime count, we set this to about 650ns */ deadTimeVal = ((uint64_t)pwmSourceClockInHz * 650) / 1000000000; pwmSignal[0].pwmChannel
= kPWM_PwmA;
pwmSignal[0].level
= kPWM_HighTrue;
pwmSignal[0].dutyCyclePercent = 50;
// 50 percent duty cycle
pwmSignal[0].deadtimeValue
= deadTimeVal;
pwmSignal[0].faultState
= kPWM_PwmFaultState0;
pwmSignal[0].pwmchannelenable = true; /*********** PWMA_SM0 - phase A, configuration, setup 1 channel as an example ************/ PWM_SetupPwm(BOARD_PWM_BASEADDR, kPWM_Module_0, pwmSignal, 1, kPWM_ SignedCenterAligned, pwmFrequencyInHz, pwmSourceClockInHz); } /* Main function */ int main(void) {
● 228
Getting Started with NXP i.MX Development Board - UK.indd 228
26-10-2023 09:35
Chapter 11 • Using Pulse Width Modulation (PWM)
PRINTF("BEGINNING\n\r"); pwm_config_t pwmConfig; pwm_fault_param_t faultConfig; BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); while(1) { CLOCK_SetDiv(kCLOCK_AhbDiv, 0x2); /* Set AHB PODF to 2, divide by 3 */ CLOCK_SetDiv(kCLOCK_IpgDiv, 0x3); /* Set IPG PODF to 3, divede by 4 */ /* Set the PWM Fault inputs to a low value */ XBARA_Init(XBARA); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault0); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault1); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault2); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault3); pwmConfig.enableDebugMode = false; pwmConfig.enableWait = false; pwmConfig.reloadSelect = kPWM_LocalReload; pwmConfig.clockSource = kPWM_BusClock; pwmConfig.prescale = kPWM_Prescale_Divide_1; pwmConfig.initializationControl = kPWM_Initialize_LocalSync; pwmConfig.forceTrigger = kPWM_Force_Local; pwmConfig.reloadFrequency = kPWM_LoadEveryOportunity; pwmConfig.reloadLogic = kPWM_ReloadImmediate; pwmConfig.pairOperation = kPWM_Independent; //
PWM_GetDefaultConfig(&pwmConfig); #ifdef DEMO_PWM_CLOCK_DEVIDER pwmConfig.prescale = DEMO_PWM_CLOCK_DEVIDER; #endif /* Use full cycle reload */ pwmConfig.reloadLogic = kPWM_ReloadPwmFullCycle;
● 229
Getting Started with NXP i.MX Development Board - UK.indd 229
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
/* Initialize submodule 0 */ if (PWM_Init(BOARD_PWM_BASEADDR, kPWM_Module_0, &pwmConfig) == kStatus_Fail) { PRINTF("PWM initialization failed\n"); return 1; } /* config->faultClearingMode = kPWM_Automatic; config->faultLevel = false; config->enableCombinationalPath = true; config->recoverMode = kPWM_NoRecovery; */ PWM_FaultDefaultConfig(&faultConfig); #ifdef DEMO_PWM_FAULT_LEVEL faultConfig.faultLevel = DEMO_PWM_FAULT_LEVEL; #endif PWM_DRV();
// Call Init function
/* Set the load okay bit for all submodules to load registers from their buffer */ PWM_SetPwmLdok(BOARD_PWM_BASEADDR, kPWM_Control_Module_0, true); /* Start the PWM generation from Submodules 0 */ PWM_StartTimer(BOARD_PWM_BASEADDR, kPWM_Control_Module_0); while (1U) { /* Delay at least 100 PWM periods. */ SDK_DelayAtLeastUs((1000000U / APP_DEFAULT_PWM_FREQUENCE) * 100, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); } } }
Figure 11.11 Program listing
● 230
Getting Started with NXP i.MX Development Board - UK.indd 230
26-10-2023 09:35
Chapter 11 • Using Pulse Width Modulation (PWM)
Figure 11.12 Generated waveform on the oscilloscope Notice that the following lines of code set the PWM channel to a positive logical level, and this way when the trigger condition of the PWM occurs the channel will go to a HIGH State. If these lines are commented, then when the trigger condition of the PWM occurs, nothing will happen, and the channel will stay on the same logical level. XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault0); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault1); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault2); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault3);
To run the program, compile it in Release mode and then copy the .axf file to the virtual COM port assigned to the development board. Changing the duty cycle In the program in Figure 11.11, the duty cycle is fixed at 50%. The duty cycle can easily be changed by setting a variable (e.g. pwmVal) to the required duty cycle between 0 and 100 and then calling the following function: PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_0, kPWM_ PwmA, kPWM_SignedCenterAligned, pwmVal);
● 231
Getting Started with NXP i.MX Development Board - UK.indd 231
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
Chapter 12 • Electric Motors 12.1 Overview An electric motor is an electrical machine that converts electrical energy into mechanical energy in the form of linear or rotational movement (or torque). Electric motors operate on the principles of magnetic fields where a force is generated between the motor's magnetic field and the current through its windings and this force causes linear or rotational movement. Electric motors are classified into two categories as follows: • AC motors • DC motors Electric motors are used in many industrial, commercial, and domestic applications. The MIMXRT1010 processor provides complex and very flexible PWM channels that are aimed for motor control applications. In this chapter we will be looking at the design of simple DC electric motor control applications using the MIMXRT1010-EVK development kit with a PWM channel.
12.2 Project 1 – Two-speed motor speed control Description: This is a simple project where a small, brushed DC motor is connected to the development board through a power MOSFET transistor switch. In addition, a push-button switch is connected to the development board. Normally, the motor spins at 25% of full speed. Pressing the button will increase the speed to 50% of its maximum value. The aim of this project is to show how PWM can be used to control the speed of a DC brushed motor. Block Diagram: Figure 12.1 shows the block diagram of the project. A motor driver (MOSFET transistor) and a push-button switch are connected to the development board.
Figure 12.1 Block diagram of the project Circuit Diagram: The circuit diagram of the project is shown in Figure 12.2. The MOSFET transistor is connected to Arduino header J57, pin 20 (PWM1 output) through a bipolar transistor switch, and the push-button switch is connected to J57 pin 6 (GPIO_19). The button output pin is pulled high by the software, and therefore it is normally at logic 1. Pressing the button changes the button output to logic 0. Figure 12.3 shows the circuit built on a breadboard.
● 232
Getting Started with NXP i.MX Development Board - UK.indd 232
26-10-2023 09:35
Chapter 12 • Electric Motors
Figure 12.2 Circuit diagram
Figure 12.3 Circuit built on a breadboard A bipolar transistor can be used as a switch in small motor applications. There are many applications, however, where a larger motor may be used and/or the motor requires large current to operate, and a bipolar transistor may not be able to supply the required current. In such applications, it is recommended to use a power MOSFET transistor. A MOSFET transistor where the Gate voltage is logic level compatible with +3.3 V, such as the IRL520, IRF3708 and so on can be used. In this project, an IRFZ44 type MOSFET is used which is not +3.3 V compatible. A BC337 type bipolar transistor is used to drive the MOSFET. The continuous Drain current of this power MOSFET transistor can be as high as 20 A (of course an external power supply must be used for large currents since the USB-driven power supply cannot provide more than 0.8 A of current). The pin configurations of the IRL540 MOSFET transistor and BC337 are shown in Figure 12.4.
● 233
Getting Started with NXP i.MX Development Board - UK.indd 233
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
Figure 12.4 Pin configuration of IRFZ44 and BC337 Program Listing: Before writing the program, the development board must be configured as in the PWM projects (see Chapter 10). Additionally, J57, pin 6 (GPIO_AD_05, IO19) must be configured as an input (Figure 12.5).
Figure 12.5 Configure J57, pin 6 (IO19) as input Figure 12.6 shows the program listing (Two-speed-motor). The program generates a PWM waveform with the frequency of 1 kHz. By default, the duty cycle is set to 25% so that the motor operates at a quarter of its full speed. Pressing the button increases the duty cycle to 50% so that the motor rotates at half of its full power (notice the logic inversion by the bipolar transistor and the inverse duty cycle. The MOSFET is ON when the bipolar transistor is OFF. i.e. when its base is at logic LOW).
● 234
Getting Started with NXP i.MX Development Board - UK.indd 234
26-10-2023 09:35
Chapter 12 • Electric Motors
/* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *Two Speed Motor*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "fsl_debug_console.h" #include "fsl_pwm.h" #include "fsl_xbara.h" /******************************************************************************* * Definitions ******************************************************************************/ #define Button 19
// Button at IO19
#define BOARD_PWM_BASEADDR PWM1
// PWM base address
#define PWM_SRC_CLK_FREQ CLOCK_GetFreq(kCLOCK_IpgClk) #ifndef APP_DEFAULT_PWM_FREQUENCE #define APP_DEFAULT_PWM_FREQUENCE (1000UL)
// 1000 Hz
#endif /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ /******************************************************************************* * Code ******************************************************************************/ static void PWM_DRV(void) { uint16_t deadTimeVal; pwm_signal_param_t pwmSignal[1]; uint32_t pwmSourceClockInHz; uint32_t pwmFrequencyInHz = APP_DEFAULT_PWM_FREQUENCE; pwmSourceClockInHz = PWM_SRC_CLK_FREQ; /* Set deadtime count, we set this to about 650ns */ deadTimeVal = ((uint64_t)pwmSourceClockInHz * 650) / 1000000000;
● 235
Getting Started with NXP i.MX Development Board - UK.indd 235
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
pwmSignal[0].pwmChannel
= kPWM_PwmA;
pwmSignal[0].level
= kPWM_HighTrue;
pwmSignal[0].dutyCyclePercent = 25;
// 25% by default
pwmSignal[0].deadtimeValue
= deadTimeVal;
pwmSignal[0].faultState
= kPWM_PwmFaultState0;
pwmSignal[0].pwmchannelenable = true; /*********** PWMA_SM0 - phase A, configuration, setup 1 channel as an example ************/ PWM_SetupPwm(BOARD_PWM_BASEADDR, kPWM_Module_0, pwmSignal, 1, kPWM_ SignedCenterAligned, pwmFrequencyInHz, pwmSourceClockInHz); } /* Main function */ int main(void) { uint8_t DutyCycle; PRINTF("BEGINNING\n\r"); pwm_config_t pwmConfig; pwm_fault_param_t faultConfig; BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); while(1) { CLOCK_SetDiv(kCLOCK_AhbDiv, 0x2); /* Set AHB PODF to 2, divide by 3 */ CLOCK_SetDiv(kCLOCK_IpgDiv, 0x3); /* Set IPG PODF to 3, divede by 4 */ /* Set the PWM Fault inputs to a low value */ XBARA_Init(XBARA); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault0); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault1); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault2); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault3);
● 236
Getting Started with NXP i.MX Development Board - UK.indd 236
26-10-2023 09:35
Chapter 12 • Electric Motors
pwmConfig.enableDebugMode = false; pwmConfig.enableWait = false; pwmConfig.reloadSelect = kPWM_LocalReload; pwmConfig.clockSource = kPWM_BusClock; pwmConfig.prescale = kPWM_Prescale_Divide_1; pwmConfig.initializationControl = kPWM_Initialize_LocalSync; pwmConfig.forceTrigger = kPWM_Force_Local; pwmConfig.reloadFrequency = kPWM_LoadEveryOportunity; pwmConfig.reloadLogic = kPWM_ReloadImmediate; pwmConfig.pairOperation = kPWM_Independent; //
PWM_GetDefaultConfig(&pwmConfig); #ifdef DEMO_PWM_CLOCK_DEVIDER pwmConfig.prescale = DEMO_PWM_CLOCK_DEVIDER; #endif /* Use full cycle reload */ pwmConfig.reloadLogic = kPWM_ReloadPwmFullCycle; /* Initialize submodule 0 */ if (PWM_Init(BOARD_PWM_BASEADDR, kPWM_Module_0, &pwmConfig) ==
kStatus_Fail) { PRINTF("PWM initialization failed\n"); return 1; } /* config->faultClearingMode = kPWM_Automatic; config->faultLevel = false; config->enableCombinationalPath = true; config->recoverMode = kPWM_NoRecovery; */ PWM_FaultDefaultConfig(&faultConfig); #ifdef DEMO_PWM_FAULT_LEVEL faultConfig.faultLevel = DEMO_PWM_FAULT_LEVEL; #endif PWM_DRV();
// Call Init function
/* Set the load okay bit for all submodules to load registers from their buffer */ PWM_SetPwmLdok(BOARD_PWM_BASEADDR, kPWM_Control_Module_0, true);
● 237
Getting Started with NXP i.MX Development Board - UK.indd 237
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
/* Start the PWM generation from Submodules 0 */ PWM_StartTimer(BOARD_PWM_BASEADDR, kPWM_Control_Module_0); while (1U) { if(GPIO_PinRead(GPIO1, Button) == 0) {
DutyCycle = 25;
PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_0,
// 25% Inverse Duty Cycle (faster)
kPWM_PwmA, kPWM_SignedCenterAligned, DutyCycle); } else {
DutyCycle = 50;
PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_0,
// 50% Inverse Duty Cycle (slower)
kPWM_PwmA, kPWM_SignedCenterAligned, DutyCycle); } PWM_SetPwmLdok(BOARD_PWM_BASEADDR, kPWM_Control_Module_0, true); } } }
Figure 12.6 Program listing
12.3 Project 2 – Varying the motor speed Description: This is again a basic project where a small brushed DC motor is connected to the development board through a power MOSFET transistor switch as in the previous project. In addition, a potentiometer is connected to one of the analog inputs of the kit. In this project, the speed of the motor is varied by moving the potentiometer arm. Block diagram: Figure 12.7 shows the block diagram of the project. A motor driver (bipolar transistor and MOSFET) and a potentiometer are connected to the microcontroller.
Figure 12.7 Block diagram of the project The DC motor in this project is controlled by varying the duty cycle of a PWM wave sent to the motor. By turning the potentiometer, the analog voltage read by the microcontroller is varied and this in turn changes the PWM duty cycle of the voltage applied to the motor.
● 238
Getting Started with NXP i.MX Development Board - UK.indd 238
26-10-2023 09:35
Chapter 12 • Electric Motors
Circuit Diagram: The circuit diagram of the project is shown in Figure 12.8. The MOSFET transistor is connected as in the previous project through a bipolar transistor. A 10 kΩ potentiometer is connected to Arduino header J56, pin 14 (GPIO_AD_01, i.e. ADC channel 1).
Figure 12.8 Circuit diagram Program listing: Before writing the program, the PWM channels must be configured as in the previous project. Also, GPIO_AD_01 pin must be configured as an analog input (Figure 12.27).
Figure 12.9 Configure the ports The frequency of the PWM is set to 1,000 Hz, as in the previous project. Figure 12.10 shows the program listing (Varying-motor-speed). Compile the program as usual and upload to your development kit. At the beginning of the program, the PWM and ADC parameters are defined. The PWM part of the program is the same as in the previous program. Inside the
● 239
Getting Started with NXP i.MX Development Board - UK.indd 239
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
main program, the potentiometer voltage is read and stored in variable adcreading. This reading is then multiplied by 100.0 and divided by 4096.0 to map the reading to allowed duty cycle ranges. Varying the potentiometer arm changes the speed of the motor. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *Varying the motor speed*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "fsl_debug_console.h" #include "fsl_pwm.h" #include "fsl_xbara.h" #include "fsl_adc.h" /******************************************************************************* * Definitions ******************************************************************************/ #define BOARD_PWM_BASEADDR PWM1
// PWM base address
#define PWM_SRC_CLK_FREQ CLOCK_GetFreq(kCLOCK_IpgClk) #ifndef APP_DEFAULT_PWM_FREQUENCE #define APP_DEFAULT_PWM_FREQUENCE (1000UL)
// 1000 Hz
#endif #define ADC_BASE
ADC1
// ADC Base
#define ADC_USER_CHANNEL
1U
// ADC used
#define ADC_CHANNEL_GROUP 0U
// Channel group
/******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ const uint32_t g_Adc_12bitFullRange = 4096U;
// 12-bits
int adcreading = 0; /******************************************************************************* * Code ******************************************************************************/
● 240
Getting Started with NXP i.MX Development Board - UK.indd 240
26-10-2023 09:35
Chapter 12 • Electric Motors
static void PWM_DRV(void) { uint16_t deadTimeVal; pwm_signal_param_t pwmSignal[1]; uint32_t pwmSourceClockInHz; uint32_t pwmFrequencyInHz = APP_DEFAULT_PWM_FREQUENCE; pwmSourceClockInHz = PWM_SRC_CLK_FREQ; /* Set deadtime count, we set this to about 650ns */ deadTimeVal = ((uint64_t)pwmSourceClockInHz * 650) / 1000000000; pwmSignal[0].pwmChannel
= kPWM_PwmA;
pwmSignal[0].level
= kPWM_HighTrue;
pwmSignal[0].dutyCyclePercent = 25;
// 25% by default
pwmSignal[0].deadtimeValue
= deadTimeVal;
pwmSignal[0].faultState
= kPWM_PwmFaultState0;
pwmSignal[0].pwmchannelenable = true; /*********** PWMA_SM0 - phase A, configuration, setup 1 channel as an example ************/ PWM_SetupPwm(BOARD_PWM_BASEADDR, kPWM_Module_0, pwmSignal, 1, kPWM_ SignedCenterAligned, pwmFrequencyInHz, pwmSourceClockInHz); } /* Main function */ int main(void) { uint8_t DutyCycle; float mapping; PRINTF("BEGINNING\n\r"); pwm_config_t pwmConfig; pwm_fault_param_t faultConfig; BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); adc_config_t adcConfigStruct; adc_channel_config_t adcChannelConfigStruct;
● 241
Getting Started with NXP i.MX Development Board - UK.indd 241
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
// // Configure ADC channel // adcConfigStruct.enableAsynchronousClockOutput = true; adcConfigStruct.enableOverWrite =
false;
adcConfigStruct.enableContinuousConversion =
false;
adcConfigStruct.enableHighSpeed =
false;
adcConfigStruct.enableLowPower =
false;
adcConfigStruct.enableLongSample =
false;
adcConfigStruct.referenceVoltageSource = kADC_ReferenceVoltageSourceAlt0; adcConfigStruct.samplePeriodMode =
kADC_SamplePeriod2or12Clocks;
adcConfigStruct.clockSource =
kADC_ClockSourceAD;
adcConfigStruct.clockDriver =
kADC_ClockDriver1;
adcConfigStruct.resolution =
kADC_Resolution12Bit;
//
ADC_GetDefaultConfig(&adcConfigStruct); ADC_Init(ADC_BASE, &adcConfigStruct);
// Initialize
kStatus_Success == ADC_DoAutoCalibration(ADC_BASE);
// Auto calibrate
adcChannelConfigStruct.channelNumber = ADC_USER_CHANNEL;
// ADC channel
adcChannelConfigStruct.enableInterruptOnConversionCompleted = false;
while(1) { CLOCK_SetDiv(kCLOCK_AhbDiv, 0x2);
/* Set AHB PODF to 2, divide by 3
CLOCK_SetDiv(kCLOCK_IpgDiv, 0x3);
/* Set IPG PODF to 3, divede by 4
*/ */ /* Set the PWM Fault inputs to a low value */ XBARA_Init(XBARA); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault0); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault1); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault2); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault3); pwmConfig.enableDebugMode = false; pwmConfig.enableWait = false; pwmConfig.reloadSelect = kPWM_LocalReload; pwmConfig.clockSource = kPWM_BusClock; pwmConfig.prescale = kPWM_Prescale_Divide_1;
● 242
Getting Started with NXP i.MX Development Board - UK.indd 242
26-10-2023 09:35
Chapter 12 • Electric Motors
pwmConfig.initializationControl = kPWM_Initialize_LocalSync; pwmConfig.forceTrigger = kPWM_Force_Local; pwmConfig.reloadFrequency = kPWM_LoadEveryOportunity; pwmConfig.reloadLogic = kPWM_ReloadImmediate; pwmConfig.pairOperation = kPWM_Independent; //
PWM_GetDefaultConfig(&pwmConfig); #ifdef DEMO_PWM_CLOCK_DEVIDER pwmConfig.prescale = DEMO_PWM_CLOCK_DEVIDER; #endif /* Use full cycle reload */ pwmConfig.reloadLogic = kPWM_ReloadPwmFullCycle; /* Initialize submodule 0 */ if (PWM_Init(BOARD_PWM_BASEADDR, kPWM_Module_0, &pwmConfig) ==
kStatus_Fail) { PRINTF("PWM initialization failed\n"); return 1; } /* config->faultClearingMode = kPWM_Automatic; config->faultLevel = false; config->enableCombinationalPath = true; config->recoverMode = kPWM_NoRecovery; */ PWM_FaultDefaultConfig(&faultConfig); #ifdef DEMO_PWM_FAULT_LEVEL faultConfig.faultLevel = DEMO_PWM_FAULT_LEVEL; #endif PWM_DRV();
// Call Init function
/* Set the load okay bit for all submodules to load registers from their buffer */ PWM_SetPwmLdok(BOARD_PWM_BASEADDR, kPWM_Control_Module_0, true); /* Start the PWM generation from Submodules 0 */ PWM_StartTimer(BOARD_PWM_BASEADDR, kPWM_Control_Module_0); while (1U) {
● 243
Getting Started with NXP i.MX Development Board - UK.indd 243
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
ADC_SetChannelConfig(ADC_BASE, ADC_CHANNEL_GROUP, &adcChannelConfigStruct); while (0U == ADC_GetChannelStatusFlags(ADC_BASE, ADC_CHANNEL_GROUP)) { } adcreading = ADC_GetChannelConversionValue(ADC_BASE, ADC_CHANNEL_GROUP); mapping = 100.0 * adcreading / g_Adc_12bitFullRange; DutyCycle = mapping; PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_0, kPWM_ PwmA, kPWM_SignedCenterAligned, DutyCycle); PWM_SetPwmLdok(BOARD_PWM_BASEADDR, kPWM_Control_Module_0, true); } } }
Figure 12.10 Program listing
12.4 Project 3 – Changing the speed and motor direction Description: This project shows how the speed and direction of a DC motor can be changed. In this project, the motor is controlled as follows: • Turn clockwise at high speed • Wait 5 seconds • Turn clockwise at low speed • Wait 5 seconds • Turn anticlockwise at high speed • Wait 5 seconds • Turn anticlockwise at low speed The above process is repeated indefinitely until stopped manually. The speed of a DC motor can be changed by changing the applied voltage level. The direction of rotation can be changed by simply reversing the polarity of the supply voltage. In DC motor direction control applications, an H bridge circuit is commonly used to change the polarity of the voltage applied to the motor and hence change the direction of rotation. Figure 12.11 shows the basic operation of an H bridge circuit. Here, we have 4 switches labeled A, B, C, D and the motor is connected in the middle of the circuit. By controlling the switches, we can easily change the polarity of the voltage applied to the motor. For example, by clocking switches A and D and opening B and C, the motor will rotate clockwise. Similarly, by closing switches B and C and opening A and D, the motor will rotate counterclockwise. This is illustrated below (0 and 1 correspond to switch open and close conditions, respectively):
● 244
Getting Started with NXP i.MX Development Board - UK.indd 244
26-10-2023 09:35
Chapter 12 • Electric Motors
A B C D 0 0 0 0 1 0 0 1 0 1 1 0
Motor rotation No rotation Clockwise Anti-clockwise
It is clear from the above table that switches A and D and also B and C must be operated together.
Figure 12.11 H bridge with switches In real motor direction control applications, the switches are replaced by transistors. Figure 12.12 shows an H bridge circuit built using bipolar transistors. In this circuit, four signals are required to control the motor direction as explained above. A simpler circuit using only two control signals is shown in Figure 12.13. By using logic inverters, we can make sure that only one transistor on either side of the motor is turned ON. For example, when A is set to logic 1, the transistor at the top left is turned ON and the one at the bottom left is OFF. NPN and PNP bipolar transistors can also be used in H bridge circuits requiring two control lines, as shown in Figure 12.14.
Figure 12.12 Simple H bridge circuit built using bipolar transistors
● 245
Getting Started with NXP i.MX Development Board - UK.indd 245
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
Figure 12.13 H bridge circuit using only 2 control signals
Figure 12.14 H bridge circuit with NPN and PNP transistors An H bridge circuit built using power MOSFET transistors is shown in Figure 12.15. Notice that diodes are used in the H bridge circuits to protect the transistors from the back emf. Also, in a 4-wire control, A and C or B and D must not be enabled at the same time as this will short the power supply to ground and possibly damage the bridge transistors.
Figure 12.15 H bridge circuit built using MOSFET transistors
● 246
Getting Started with NXP i.MX Development Board - UK.indd 246
26-10-2023 09:35
Chapter 12 • Electric Motors
Using an H Bridge module Block diagram: The block diagram of the project is shown in Figure 12.16. In this project, the LMD18200 H bridge motor controller module is used. The module has a built-in H bridge where the speed and the direction of rotation of a motor can be controlled. The speed is controlled by sending PWM waves to the chip, and the direction of rotation is controlled by setting or clearing a direction control bit. LMD18200 has the following features: • Up to 3A continuous current • Operating voltage: 12 V to 55 V • TTL and CMOS compatible inputs • Motor speed and direction control • Thermal shutdown • Motor stop (break) input
Figure 12.16 Block diagram of the project The LMD18200 module is available with an on-board heat sink, capacitors, resistors, screw terminals, and it has the following pins (Figure 12.17): Left Screw Terminal (J2) GND power supply ground PWM PWM speed control input DIR motor direction control input BRAKE motor brake control (logic 1 to brake) Right Screw Terminal (J3) V+ external power supply input (max 55 V) GND external power supply ground OUT1, 2 motor terminals
Figure 12.17 The LMD18200 module
● 247
Getting Started with NXP i.MX Development Board - UK.indd 247
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
Table 11.1 shows the operational logic table of the LMD18200 module. PWM
DIR
BRAKE
Active output drivers
1
1
0
Rotate in one direction
1
0
0
Rotate in reverse direction
1
1
1
Brake
1
0
1
Brake
X
1
Brake
0
Table 11.1 LMD18200 operational logic table Circuit Diagram: Figure 12.18 shows the circuit diagram of the project. A 12 V DC motor is connected to screw terminals OUT1 and OUT2, and an external +12 V power supply is connected to the V+ input. Arduino header J56 pin 14 (GPIO_AD_01, IO15) and J57 pin 20 (PWM1 output as in the previous project) are connected to the DIR and PWM inputs of the LMD18200 module respectively. Figure 12.19 shows the project built on a breadboard.
Figure 12.18 Circuit diagram
Figure 12.19 Project built on a breadboard
● 248
Getting Started with NXP i.MX Development Board - UK.indd 248
26-10-2023 09:35
Chapter 12 • Electric Motors
Program listing: Before writing the program you must configure the PWM port (Arduino header J57 pin 20) and also configure Arduino header J56 pin 14 (GPIO_AD_01, IO15) as output (Figure 12.20).
Figure 12.20 Configure the I/O ports The program listing is shown in Figure 12.21 (LMD18200). At the beginning of the program, DIR is defined as 15 and various PWM parameters are also defined. The frequency of the PWM waveform is set to 1,000 Hz. The function Rotate has two character arguments: direction and speed. If the direction is 'c' (i.e. clockwise) then the DIR pin of the LMD18200 module is set LOW. If, on the other hand, the direction is 'a' (i.e. anticlockwise) then the DIR pin of the LMD18200 module is set HIGH. If the speed is 'f' (i.e. fast) then the PWM duty cycle is set to 50%; otherwise it is set to 25%. Inside the main program loop, the motor is rotated as required. Notice that a delay was introduced using the function SDK_DelayAtLeastUs(). With a PWM frequency of 1,000 Hz and a setting of 2,000, the delay was approximately 5 seconds. /* * Copyright 2019 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * *LMD18200*/ #include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "fsl_debug_console.h" #include "fsl_pwm.h" #include "fsl_xbara.h" #include "fsl_adc.h" /******************************************************************************* * Definitions
● 249
Getting Started with NXP i.MX Development Board - UK.indd 249
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
******************************************************************************/ #define DIR 15U // Motor direction #define BOARD_PWM_BASEADDR PWM1
// PWM base address
#define PWM_SRC_CLK_FREQ CLOCK_GetFreq(kCLOCK_IpgClk) #ifndef APP_DEFAULT_PWM_FREQUENCE #define APP_DEFAULT_PWM_FREQUENCE (1000UL)
// 1000 Hz
#endif /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ /******************************************************************************* * Code ******************************************************************************/ static void PWM_DRV(void) { uint16_t deadTimeVal; pwm_signal_param_t pwmSignal[1]; uint32_t pwmSourceClockInHz; uint32_t pwmFrequencyInHz = APP_DEFAULT_PWM_FREQUENCE; pwmSourceClockInHz = PWM_SRC_CLK_FREQ; /* Set deadtime count, we set this to about 650ns */ deadTimeVal = ((uint64_t)pwmSourceClockInHz * 650) / 1000000000; pwmSignal[0].pwmChannel
= kPWM_PwmA;
pwmSignal[0].level
= kPWM_HighTrue;
pwmSignal[0].dutyCyclePercent = 10; pwmSignal[0].deadtimeValue
= deadTimeVal;
pwmSignal[0].faultState
= kPWM_PwmFaultState0;
// 10% by default
pwmSignal[0].pwmchannelenable = true; /*********** PWMA_SM0 - phase A, configuration, setup 1 channel as an example ************/ PWM_SetupPwm(BOARD_PWM_BASEADDR, kPWM_Module_0, pwmSignal, 1, kPWM_ SignedCenterAligned, pwmFrequencyInHz, pwmSourceClockInHz); } //
● 250
Getting Started with NXP i.MX Development Board - UK.indd 250
26-10-2023 09:35
Chapter 12 • Electric Motors
// This function rotates the motor. Argument direction can be 'c' for clockwise or 'a' for // anticlockwise. Argument speed can be 'f' for fast or 's' for slow // void Rotate(char direction, char speed) { uint8_t DutyCycle; if(direction == 'c') GPIO_PinWrite(GPIO1, DIR, 0U); else if(direction == 'a') GPIO_PinWrite(GPIO1, DIR, 1U); if(speed == 'f') DutyCycle = 50; else if(speed == 's') DutyCycle = 25; PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_0, kPWM_PwmA, kPWM_ SignedCenterAligned, DutyCycle); PWM_SetPwmLdok(BOARD_PWM_BASEADDR, kPWM_Control_Module_0, true); }
// // The motor is rotated as follows: // 5 seconds clockwise and fast // 5 seconds clockwise and slow // 5 seconds anticlockwise and fast // 5 seconds anticlockwise and slow // int main(void) { PRINTF("BEGINNING\n\r"); pwm_config_t pwmConfig; pwm_fault_param_t faultConfig; BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); while(1) { CLOCK_SetDiv(kCLOCK_AhbDiv, 0x2);
/* Set AHB PODF to 2, divide by 3
CLOCK_SetDiv(kCLOCK_IpgDiv, 0x3);
/* Set IPG PODF to 3, divede by 4
*/ */
● 251
Getting Started with NXP i.MX Development Board - UK.indd 251
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
/* Set the PWM Fault inputs to a low value */ XBARA_Init(XBARA); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault0); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault1); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault2); XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault3); pwmConfig.enableDebugMode = false; pwmConfig.enableWait = false; pwmConfig.reloadSelect = kPWM_LocalReload; pwmConfig.clockSource = kPWM_BusClock; pwmConfig.prescale = kPWM_Prescale_Divide_1; pwmConfig.initializationControl = kPWM_Initialize_LocalSync; pwmConfig.forceTrigger = kPWM_Force_Local; pwmConfig.reloadFrequency = kPWM_LoadEveryOportunity; pwmConfig.reloadLogic = kPWM_ReloadImmediate; pwmConfig.pairOperation = kPWM_Independent; //
PWM_GetDefaultConfig(&pwmConfig); #ifdef DEMO_PWM_CLOCK_DEVIDER pwmConfig.prescale = DEMO_PWM_CLOCK_DEVIDER; #endif /* Use full cycle reload */ pwmConfig.reloadLogic = kPWM_ReloadPwmFullCycle; /* Initialize submodule 0 */ if (PWM_Init(BOARD_PWM_BASEADDR, kPWM_Module_0, &pwmConfig) ==
kStatus_Fail) { PRINTF("PWM initialization failed\n"); return 1; } /* config->faultClearingMode = kPWM_Automatic; config->faultLevel = false; config->enableCombinationalPath = true; config->recoverMode = kPWM_NoRecovery; */ PWM_FaultDefaultConfig(&faultConfig);
● 252
Getting Started with NXP i.MX Development Board - UK.indd 252
26-10-2023 09:35
Chapter 12 • Electric Motors
#ifdef DEMO_PWM_FAULT_LEVEL faultConfig.faultLevel = DEMO_PWM_FAULT_LEVEL; #endif PWM_DRV();
// Call Init function
/* Set the load okay bit for all submodules to load registers from their buffer */ PWM_SetPwmLdok(BOARD_PWM_BASEADDR, kPWM_Control_Module_0, true); /* Start the PWM generation from Submodules 0 */ PWM_StartTimer(BOARD_PWM_BASEADDR, kPWM_Control_Module_0); while (1U) { Rotate('c', 'f');
// Clockwise, fast
SDK_DelayAtLeastUs((1000000U / APP_DEFAULT_PWM_FREQUENCE) * 2000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); Rotate('c', 's');
// Clockwise, slow
SDK_DelayAtLeastUs((1000000U / APP_DEFAULT_PWM_FREQUENCE) * 2000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); Rotate('a', 'f');
// Anticlockwise, fast
SDK_DelayAtLeastUs((1000000U / APP_DEFAULT_PWM_FREQUENCE) * 2000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); Rotate('a', 's');
// Anticlockwise, slow
SDK_DelayAtLeastUs((1000000U / APP_DEFAULT_PWM_FREQUENCE) * 2000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); } } }
Figure 12.21 Program listing
● 253
Getting Started with NXP i.MX Development Board - UK.indd 253
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
Chapter 13 • Using the CMSIS-DSP Library 13.1 Overview CMSIS-DSP is an optimized compute library developed for embedded microcontroller-based systems. The library provides optimized compute kernels for Cortex-M and Cortex-A based processors. The CMSIS-DSP library provides functions in the following topics: • Basic math functions • Fast math functions • Complex math functions • Filtering functions • Matrix functions • Transform functions • Motor control functions • Statistical functions • Support functions • Interpolation functions • Support Vector Machine functions (SVM) • Bayes classifier functions • Distance functions • Quaternion functions Each topic includes many functions related to the topic. For example, the Matrix functions library includes the following matrix operations: • Matrix addition • Cholesky and LDLT decompositions • Complex Matrix Multiplication • Complex Matrix Transpose • Matrix Initialization • Matrix Inverse • Matrix Multiplication • Matrix Scale • Matrix Subtraction • Matrix Transpose • Matrix Vector Multiplication Further details on the CMSIS-DSP library functions can be obtained from the following link: https://www.keil.com/pack/doc/CMSIS/DSP/html/index.html
● 254
Getting Started with NXP i.MX Development Board - UK.indd 254
26-10-2023 09:35
Chapter 13 • Using the CMSIS-DSP Library
In this chapter you will be developing a MCUXpresso based program to show how matrix addition, multiplication, and transpose can be carried out on a 4×4 matrix using the CMSIS-DSP library.
13.2 Project 1 – Matrix addition, multiplication, and transpose Description: In this project two 4×4 matrices, A32 and B32, are defined. Also, two empty matrices C32 and AT32 are defined. The program initially adds matrices A32 and B32 and stores the result in C32. All the elements of matrix C32 are displayed on the Console. Then A32 and B32 are multiplied and the result is stored in C32. Again, all the elements of C32 are displayed on the Console. Finally, the transpose of matrix A32 is taken and stored in matrix AT32. All elements of matrix AT32 are also displayed on the Console. Program listing: The library functions operate on matrix data structures. For example, the type definition for the floating-point matrix structure is shown below (see the above link for further information): typedef struct { uint16_t numRows;
// number of rows of the matrix.
uint16_t numCols;
// number of columns of the matrix.
float32_t *pData;
// points to the data of the matrix.
} arm_matrix_instance_f32;
There is an associated initialization function for each type of matrix data structure. The initialization function sets the values of the internal structure fields. Use of the initialization function is optional. However, if an initialization function is used, then the instance structure cannot be placed into a const data section. To place the instance structure in a const data section, manually initialize the data structure. For example: arm_matrix_instance_f32 S = {nRows, nColumns, pData}; There are similar definitions for Q15 and Q31 data types. Where, nRows specifies the number of rows, nColumns specifies the number of columns, and pData points to the matrix data array. Figure 13.1 shows the program listing (MatrixTest). At the beginning of the program, the header file arm_math.h and other required header files are included in the program. Matrix A32 and matrix B32 are defined as follows: const float32_t A32[16] = { 1.0, 1.0, 4.0, 6.0, 1.0, 12.0, 10.0, 2.0,
● 255
Getting Started with NXP i.MX Development Board - UK.indd 255
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
3.0, 10.0, 4.0, 5.0, 5.0, 16.0, 2.0, 8.0, }; const float32_t B32[16] = { 2.0, 3.0, 4.0, 5.0, 1.0, 3.0, 5.0, 7.0, 3.0, 10.0, 4.0, 5.0, 5.0, 10.0, 2.0, 8.0, };
The matrix instances are then initialized by specifying their names, number of rows (nRows) and number of columns (nColumns). Matrix addition is done using the function arm_mat_add_f32(&A, &B, &C), where matrices A and B are the inputs and C is the result. The result is displayed in the form of rows and columns by calling the function DisplayMatrix. Note that pData points to the resultant matrix structure. Matrix multiplication is done using the function arm_mat_mult_f32(&A, &B, &C), where matrices A and B are the inputs and C is the result as before. The result is displayed by calling the function DisplayMatrix. Transpose of matrix A is done using function: arm_mat_trans_f32(&A, &AT), where matrix A is the input and AT is the result. The result is again displayed by calling the function DisplayMatrix. /* * Copyright 2016-2023 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ /** * @file
MatrixTest.c
* @brief
Application entry point.
* Matrix addition, multiplication, and transpose*/ #include "arm_math.h" #include <stdio.h> #include "board.h" #include "peripherals.h" #include "pin_mux.h"
● 256
Getting Started with NXP i.MX Development Board - UK.indd 256
26-10-2023 09:35
Chapter 13 • Using the CMSIS-DSP Library
#include "clock_config.h" #include "MIMXRT1011.h" #include "fsl_debug_console.h" arm_status status; // // Define matrix A // const float32_t A32[16] = { 1.0,
1.0,
4.0,
6.0,
1.0,
12.0,
10.0,
2.0,
3.0,
10.0,
4.0,
5.0,
5.0,
16.0,
2.0,
8.0,
}; // // Define matrix B // const float32_t B32[16] = { 2.0,
3.0,
4.0,
5.0,
1.0,
3.0,
5.0,
7.0,
3.0,
10.0,
4.0,
5.0,
5.0,
10.0,
2.0,
8.0,
}; // // Display the matrix elements // void DisplayMatrix(float32_t *M) { int i = 0; for(int j=0; j < 16; j++) { if(i == 4) {
i = 0;
PRINTF("\n");
} PRINTF("%3.2f\t", *(M+j)); i++; } PRINTF("\n"); }
● 257
Getting Started with NXP i.MX Development Board - UK.indd 257
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
// // MAin program // void main(void) { float32_t C32[16]; float32_t AT32[16]; /* Board pin init */ BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); arm_matrix_instance_f32 A;
/* Matrix A Instance */
arm_matrix_instance_f32 AT;
/* Matrix AT(A transpose) instance
arm_matrix_instance_f32 B;
/* Matrix B instance */
*/ arm_matrix_instance_f32 C; uint32_t nRows, nColumns;
/* Matrix B instance */
/* Temporary variables */
/* Initialise the matrix Instances */ nRows = 4; nColumns = 4; arm_mat_init_f32(&A, nRows, nColumns, (float32_t *)A32); arm_mat_init_f32(&B, nRows, nColumns, (float32_t *)B32); arm_mat_init_f32(&C, nRows, nColumns, (float32_t *)C32); arm_mat_init_f32(&AT, nRows, nColumns, (float32_t *)AT32); // // Matrix operation: C = A + B // status=arm_mat_add_f32(&A, &B, &C); PRINTF("Matrix operation: C = A + B\n"); DisplayMatrix(C.pData); // // Matrix operation: C = A * B // status=arm_mat_mult_f32(&A, &B, &C); PRINTF("\nMatrix operation: C = A * B\n"); DisplayMatrix(C.pData); // // Matrix transpose of A
● 258
Getting Started with NXP i.MX Development Board - UK.indd 258
26-10-2023 09:35
Chapter 13 • Using the CMSIS-DSP Library
// status = arm_mat_trans_f32(&A, &AT); PRINTF("\nMatrix operation: Transpose of A\n"); DisplayMatrix(AT.pData); }
Figure 13.1 Program listing Creating the program Load the program from the Elektor book website, or carry out the steps below to create the program: • Click to open the MCUXpresso IDE • Enter MatrixTest as the name for your workspace and Click Launch • Click IDE • Click Create a new C/C++ project (Figure 13.2)
Figure 13.2 Click C/C++ to create a new project • Select MIMXRT1010 (Figure 13.3) and click Next
Figure 13.3 Select MIMXRT1010 • Give a name to your project (e.g. MatrixTest)
● 259
Getting Started with NXP i.MX Development Board - UK.indd 259
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
• Click tab CMSIS Drivers and enable to include the DSP library (Figure 13.4)
Figure 13.4 Enable to include the DSP library • Select the Semihost as the SDK Debug Console so that the program outputs will be displayed on the Console • Click Finish • Copy the program shown in Figure 13.1 to your IDE Testing the program • Connect the development kit to your PC • Click Debug under the Quickstart Panel • Select the default debugger probe • Wait until the compilation is ready and the first statement in the program is highlighted (Figure 13.5)
Figure 13.5 The program is ready in the debugger • Click Run, followed by Resume. The program should run and display the results as shown in Figure 13.6.
● 260
Getting Started with NXP i.MX Development Board - UK.indd 260
26-10-2023 09:35
Chapter 13 • Using the CMSIS-DSP Library
Figure 13.6 Program output • Click Run followed by Terminate to terminate the program and the debugger.
● 261
Getting Started with NXP i.MX Development Board - UK.indd 261
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
Chapter 14 • Sound and Audio Signal Processing (DSP) 14.1 Overview Sound waves are created by the vibration of objects, for example, the string of a guitar, the speaker of a radio, the voice box of a person and so on. You can hear sounds because the vibrations of the air cause the ear drum to vibrate, which are converted into nerve signals and sent to the brain. This book is about audible sound waves. Adult humans can hear in a frequency range from about 20 Hz to 20 kHz (20,000 Hz). Children and babies can hear frequencies slightly higher than 20 kHz. Some animals, on the other hand, can hear sounds at higher than 20 kHz or lower than 20 Hz. For example, some bats can hear sounds as high as 200 kHz, but their low limits are around 20 kHz. The hearing frequency ranges of some animals are given below: Animal Dog Horse Rat Elephant Chicken Sheep Mouse Rabbit
Hearing frequency range (Hz) 67 – 45,000 55 – 33,500 200 – 76,000 16 – 10,500 125 – 2,000 125 – 42,500 900 – 79,000 96 – 49,000
The human audio spectrum is divided into 7 bands, with the following frequencies: Band Brilliance Presence Upper midrange Midrange Lower midrange Bass Sub-bass
Frequency range 6 – 20 kHz 4 – 6 kHz 2 – 4 kHz 500 Hz – 2 kHz 250 – 500 Hz 60 – 250 Hz 20 – 60 Hz
The MIMXRT1010-EVK development kit includes an on-board microphone, a headphone output with 3.5 mm jack connector, and a speaker connector. In the following sections, we will be looking at some important theory of analog and digital audio. Several audio-based demo project files are available in the MCUXpresso demo files folders. In the remainder parts of this chapter, we will be looking at some of these audio projects using the development kit.
14.2 Analog and digital audio sound Analog audio has been around for many years and much earlier than digital audio. In analog audio recording, a microphone is used to convert the sound signals into electrical
● 262
Getting Started with NXP i.MX Development Board - UK.indd 262
26-10-2023 09:35
Chapter 14 • Sound and Audio Signal Processing (DSP)
analog signals and then save these audio signals on a medium such as a tape (or tape cassette) or vinyl records with spiral grooves. Most of the early music and speech recordings were analog, with tape cassettes used by the public to record private speech or to play pre-recorded music. In digital audio recording, a microphone is still used to convert the sound signals into electrical signals, but then these analog signals are converted into digital forms using analog-to-digital converter hardware and the resulting digital signals are saved as files in computer memory. Nowadays, it is also possible to use digital microphones and record signals directly in digital form. A comparison of the analog and digital audio signals is given below: Degradation of quality: The quality of analog audio degrades as tape cassettes or vinyl record age. This degradation is also noticeable when a recording is copied or played many times. Digital signals, on the other hand, do not degrade by aging. Digital signals can be copied to other mediums without using their qualities. Signal-to-noise ratio (SNR): SNR is the amount of noise generated when a recording is played. In other words, it describes how much unwanted noise signal is present in a played sound signal. SNR is measured in decibels (dB). The higher the value, the less noise there is in the signal. For example, if an audio signal has a SNR ratio of 50 dB, it means that the level of the desired signal is 50 dB higher than the level of noise. Audio bandwidth: The audio bandwidth is the range of signals permitted to be recorded or played back. The higher the bandwidth, the higher will be the maximum frequency that can be recorded correctly. Media type: Digital audio signals can be stored in digital memories. Nowadays, smartphones, iPads, and tablets can store thousands of digital audio files in minimal space. This is not the case with analog media. Tape cassettes or vinyl records are bulky, and they can store only a limited amount of data. In this book, we are interested in using digital audio files and also carry out digital audio signal processing (DSP) using digital audio files.
14.3 Digital audio sound file formats Digital audio files can be in various formats. Some of the popular file formats are given in this section.
14.3.1 Uncompressed audio file formats Uncompressed audio files are not processed, and they are available as captured and converted to digital format. As a result of this, uncompressed audio files are large and take up a lot of disk space. Some uncompressed audio file formats are:
● 263
Getting Started with NXP i.MX Development Board - UK.indd 263
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
PCM: This stands for Pulse-Code Modulation and there is no compression. The digital recording is very close to its exact representation of analog sound. The average size of a PCM file is about 10 MB per second. Therefore, a 3-minute PCM music file can take up to 30 MB disk space. WAV: This stands for Waveform Audio file format, developed by Microsoft and IBM. Although not all WAV files are uncompressed, most are, and they are in PCM format with wrappers, making them suitable for Windows systems. The average size of a WAV file is about 10 MB per second. Therefore, a 3-minute WAV music file can take up to 30 MB of disk space. AIFF: This stands for Audio Interchange File Format and is similar to WAV. AIFF was developed by Apple for their MAC systems. AIFF is basically PCM type files with wrappers, making them suitable for MAC systems (they can be opened on Windows systems). Although most AIFF audio files are uncompressed PCM files, there are compressed versions as well, called AIFF-C. The average size of a AIFF file is about 10 MB per second. Therefore, a 3-minute AIFF music file can take up to 30 MB of disk space.
14.3.2 Audio files with lossy compressions These files are compressed, and some data is lost during the compression process. The reason for compression is to minimize the disk usage, since uncompressed files take up a lot of disk space. As a result, the sound quality is sacrificed for smaller file sizes. Some lossy compressed audio file formats are: MP3: This stands for MPEG-1 Audio Layer 3. This format is very popular for music files. In this format, the quality of sound is reduced beyond the hearing range of normal ear. Also, the quality of the sound is reduced for sounds that are not easy to hear. Almost all digital devices nowadays with audio playback capabilities (e.g. PCs, smartphones, tablets, smart TVs etc.) can play MP3 audio files. The average bitrate for an MP3 file is 128 kbits per second, which requires about one megabyte of data per minute of audio. Note: do not confuse MP3 and MP4. MP3 is audio only, while MP4 supports both audio and video. AAC: This stands for Advanced Audio Coding. Although it was developed as a competitor to MP3, it never became as popular as MP3, even though it uses a much more advanced compression algorithm than the MP3. AAC is currently used by YouTube, iOS, Android, and by the PlayStation. AAC files are slightly smaller than MP3 files. WMA: This stands for Windows Media Audio. It was developed as a competitor to MP3 and its compression algorithm is very similar to AAC, giving higher quality audio sound. WMA is a proprietary format created by Microsoft and, as such, it has not become very popular. WMA files are slightly larger than MP3 files.
14.3.3 Audio files with lossless compressions In these file formats, the file size is reduced with compression without any data quality loss. These files are larger than the lossy compressed files and smaller than uncompressed files. Some lossless compressed audio file formats are:
● 264
Getting Started with NXP i.MX Development Board - UK.indd 264
26-10-2023 09:35
Chapter 14 • Sound and Audio Signal Processing (DSP)
FLAC: This stands for Free Lossless Audio Codec, and it is a popular file format that can compress a file by up to 60% without losing its quality. FLAC is the main competitor of MP3 for music files, and it is currently regarded as the best audio file format as far as size and sound quality are concerned. For example, a 3-minute FLAC music file takes about 15 MB as compared to WAV which takes about 30 MB. ALAC: This stands for Apple Lossless Audio Codec, and it is similar to FLAC, although it is less efficient as far as compression is concerned. Apple products such as iOS and iTunes support ALAC. ALC file size is similar to those of FLAC. There is also a lossless version of the WMA file format, but it is not as efficient as the FLAC or ALAC.
14.3.4 Which audio file format to choose? The choice depends on the sound quality you are after and the amount of disk space you can spare. If you wish to listen to high quality raw music and not worried about the disk space it will occupy, you should choose an uncompressed file. If at the same time, you wish to use less disk space, then a file with lossless compression, such as FLAC or ALAC could be a good choice. If you wish to listen to music at ordinary quality and worried about disk space, then MP3 or another file format with lossy compression could be a good choice.
14.3.5 High-quality digital audio sound In general terms, high-quality digital audio refers to music files that have higher sampling rates and larger bit depths than standard CD music files. CD music files are specified at 16 bits, 44.1 kHz sampling rate. With a 44.1 kHz sampling rate, all the audible frequencies are covered. Higher quality (Hi-res) audio files are sampled with 24 bits and 48 kHz sampling rate, or 24 bits 96 kHz sampling rate, or even 24 bits 192 kHz sampling rate. In most cases, the human ear cannot differentiate music sampled higher than 16 bits and 44.1 kHz sampling rate. One disadvantage of sampling at higher than 16 bits is that more disk space is required to store the file.
14.4 Audio digital signal processing (Audio DSP) The abbreviation DSP stands for one of two things: Digital Signal Processor, or Digital Signal Processing. A Digital Signal Processor refers to specialized microcontroller hardware which is designed to execute specialized instructions in real time. The topic of this chapter is Digital Signal Processing, we will call it DSP from now on in this chapter. DSP is a very broad field covering signals from very low frequencies to very high frequencies. Here, we are interested only in audio signals which cover the frequency range of 20 Hz to 20 kHz. In a typical DSP application, the input signal (e.g. from a microphone) can either be digital or analog. In the case of analog input, the signal is converted into digital form using an ADC (analog-to-digital converter) and is fed to a microcontroller for further processing. The signal is then processed, such as filtered to remove unwanted high-frequency noise, noise is canceled using special noise-canceling algorithms, the frequency spectrum of the signal is extracted and analyzed, signal shape is modified, audio signals are synthesized, and so on, and the resulting digital signal is converted back into
● 265
Getting Started with NXP i.MX Development Board - UK.indd 265
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
analog using a DAC (digital-to-analog converter) and sent to a speaker or to some other output device. All of the signal processing is carried out using software algorithms with the target microcontroller. It is much easier to edit and manipulate digital signals than analog signals. DSP is used extensively nowadays by professional music makers. Many plugins are available on PCs which simplify the editing and processing of already recorded music files and other audio files. The global audio DSP market size was valued at $11.06 billion in 2019, and is projected to reach $23.43 billion by 2027 (Allied Market Research). In this chapter, you will be using the MIMXRT1010-EVK development board for some real-time DSP projects.
14.4.1 The SAI module SAI is a Serial Audio Interface module that can be used to send and/or receive audio. In addition to I2S, it supports other audio interfaces as well. The MIMXRT1010-EVK development board includes two synchronous SAI modules, which support I2S, AC97, TDM, codec/ DSP interfaces, and MQS interface for medium quality audio via GPIO pad. MQS is used to convert I2S audio data from SAI3 to PWM, and then can drive external speakers, but in practical usage, an amplifier drive circuit is required. Additionally, a headphone jack, external speaker connections, and a microphone are provided on the board.
14.4.2 The I2S bus The I2S (Inter-Integrated Circuit Sound Protocol) is a serial bus interface standard used commonly to connect digital audio devices together. It was introduced in 1986 by Philips Semiconductors (now NXP Semiconductors) and then revised in 1996 and 2022. The I2S protocol sends pulse-code modulation (PCM) digital audio data from a controller to a target. The bus has at least 3 lines: bit clock, word select, and data line. Word select is used to specify which of the stereo channels, left or right, the data should be sent to. Readers should understand that the I2C and I2S are entirely different bus protocols, and they are unrelated. Figure 14.1 shows the block diagram of a conventional audio DSP system which does not use the I2S bus. Here, the audio signal is received from an analog microphone. This signal is then converted into digital form using an ADC. The resulting digital signal is then fed to a microcontroller, which processes this digital signal. At the end, the signal is converted back into analog using a DAC converter and is fed to a speaker. An analog amplifier is usually used (not shown) to increase the signal level for the speaker. In most low frequency applications, the ADC and DAC can be part of the microcontroller. In high-speed and high-quality applications, it may be necessary to use external professional quality ADC and DAC converters.
Figure 14.1 Conventional audio DSP system
● 266
Getting Started with NXP i.MX Development Board - UK.indd 266
26-10-2023 09:35
Chapter 14 • Sound and Audio Signal Processing (DSP)
Figure 14.2 shows the block diagram of a digital audio DSP system using the I2S bus interface. Here, a digital microphone is used, compatible with the I2S bus. The output of the microphone, which is a digital signal, is fed to the microcontroller's I2S bus input. The signal is processed by the microcontroller as required and is sent in digital form to an I2S compatible amplifier module. The output of the amplifier is an analog signal which drives the speaker.
Figure 14.2 Digital audio DSP system The line definitions of the I2S bus are summarized below: Bit clock: this pin is the serial clock line, usually denoted as BCLK. The clock runs continuously. Word select: this pin is usually denoted by WS, and it selects the channels. 0 corresponds to the left channel, while 1 corresponds to the right channel. Data: This pin is usually denoted by SD, or SDATA and is the serial data line. The data is sent out in 2's complement format, with the MSB bit first. The transmitter and receiver do not need to have an agreed-upon word length; the transmitter sends what it has, and the receiver takes what it can use. New data bits can be clocked out on the rising or falling edge of the clock. However, they must be clocked in on the rising edge. The bit clock is sent out for each bit of data on the data line. The frequency of this clock is the given by the product of the sample rate, the number of bits per channel, and the number of channels. In a typical audio CD, the standard is 44.1 kHz sample rate with 16 bits of data. Assuming 2 channels (i.e. stereo), the bit clock frequency will be: 44.1 kHz × 16 × 2 = 1.4112 MHz Therefore, if you want to send two channels of high-quality audio, we would need a clock rate of 1.4112 MHz. The telephone quality sound is sampled at 8 kHz with 8 bits and there is only one channel. Therefore, to send telephone quality audio, you will need a clock frequency of: 8 kHz × 8 × 1 = 64 kHz I2S allows up to two channels to be used on the same data line, and this is selected by the Word select bit. For 2-channel stereo operation, the left audio is transmitted on the low cycle of the Word select, and the right channel is transmitted on the high cycle. The I2S bus
● 267
Getting Started with NXP i.MX Development Board - UK.indd 267
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
is not like the I2C bus, where multiple devices can be connected on the same bus line. In I2S only two channels of a device can be used, multiple devices cannot be connected to the bus. The maximum length of the I2S bus is specified as 3 meters. The bit clock and Word select signals can be generated with a receiver, transmitter, or a third-party controller (see Figure 14.3).
Figure 14.3 I2S configurations (taken from the I2S specification) Figure 14.4 shows the timing diagram of the I2S bus.
Figure 14.4 I2S bus timing (taken from the I2S specification) The advantages of the I2S bus are: • There are no data synchronization issues since there is a single master device • An I2S microphone does not require an analog front end • Separate clock and data lines are used, which results in low jitter • ADC and DAC are not required since the signals are digital The disadvantages of the I2S bus are: • There is no error detection or correction and therefore, errors can occur on the line • As a result of the propagation delays, there could be synchronization problems at high clock rates • There is no standard I2S cabling or connector procedure, different manufacturers use different cables and connectors
● 268
Getting Started with NXP i.MX Development Board - UK.indd 268
26-10-2023 09:35
Chapter 14 • Sound and Audio Signal Processing (DSP)
14.4.3 The SAI bus The SAI module on the MIXRT1010-EVK development board contains a Transmitter and Receiver with the following signals (see Chapter 3.1): • SAI_MCLK: master clock, used to generate the bit clock, master output, slave input. • SAI_TX_BCLK: Transmit bit clock, master output, slave input • SAI_TX_SYNC: Transmit Frame sync, master output, slave input, L/R channel select • SAI_TX_DATA[4]:Transmit data line, 1-3 share with RX_DATA[1-3] • SAI_RX_BCLK: receiver bit clock • SAI_RX_SYNC: receiver frame sync • SAI_RX_DATA[4]: receiver data line SAI module clocks are audio master clock, bus clock, and bit clock. The SAI module Frame sync has 3 modes: 1: Transmit and receive using its own BCLK and SYNC 2) Transmit async, receive sync: use transmit BCLK and SYNC, transmit enable at first, disable at last. 3) Transmit sync, receive async: use receive BCLK and SYNC, receiver enable at first, disable at last.
14.5 MIMXRT1010-EVK development kit audio demo project files Several audio demo project files are included in the MCUXpresso SDK. These files are in the demo folders demo_apps and usb_examples (see Figure 14.5). There is only one file (sai) in demo_apps, and six files in usb_examples (dev_audio_…). Readers can modify and use these project files in their own projects.
Figure 14.5 Audio demo project files In this section, we will look at some details of project file sai. When this project file is run in debug mode, the following options are given to the user: • Record and playback at the same time • Playback sine wave
● 269
Getting Started with NXP i.MX Development Board - UK.indd 269
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
Users can use the on-board analog microphone to record and at the same time play the recording. There is also the option of using an SD card and an external digital microphone, but these are disabled in the code and are highlighted. First, load the project files in folder sai: • Start the MCUXpresso IDE by specifying a workspace name • Click IDE • Click Import SDK Examples under the Quickstart Panel • Click to select MIMXRT1011xxxxx under MIMXRT1010 and click Next • Click to expand demo_apps and select sai to load the project files and click NEXT followed by FINISH • You should see the project files loaded. Click to expand source and you should see the following C programs: playbackSineWave.c recordPlayback.c sai.c Program sai: This is the main program that runs when debugging is started. At the beginning of the programs, the required header files are included in the program and various CODEC and audio definitions are declared. Notice here that the headphone volume is set to 100 which is the maximum value (the range is 0 to 100). Then, the SAI1 clock source and frequency are defined. The wm8960 Codec is initialized by specifying the route, left and right input sources, play source, the I2S bus, the format of the data (16-bit data with 16 kHz sample rate), and the master_slave mode is set to false. Code relating to the SD card and external digital microphone are highlighted and are not compiled. WM8960 Codec is initialized with the following options: wm8960_config_t wm8960Config = { .i2cConfig = {.codecI2CInstance = BOARD_CODEC_I2C_INSTANCE, .codecI2CSourceClock = BOARD_CODEC_I2C_CLOCK_FREQ}, .route = kWM8960_RoutePlaybackandRecord, .leftInputSource = kWM8960_InputDifferentialMicInput3, .rightInputSource = kWM8960_InputDifferentialMicInput2, .playSource = kWM8960_PlaySourceDAC, .slaveAddress = WM8960_I2C_ADDR, .bus = kWM8960_BusI2S, .format = {.mclk_HZ = 6144000U, .sampleRate = kWM8960_AudioSampleRate16KHz, .bitWidth = kWM8960_AudioBitWidth16bit},
● 270
Getting Started with NXP i.MX Development Board - UK.indd 270
26-10-2023 09:35
Chapter 14 • Sound and Audio Signal Processing (DSP)
.master_slave = false, };
kWM8960_InputDifferentialMicInput2 refers to the on-board analog microphone, and kWM8960_InputDifferentialMicInput3 to the headphone microphone. Inside the main program loop, the LPI2C and SAI1 clocks are set and MCLK is enabled. Function CODEC_Init() initializes the Codec. Function CODEC_SetVolume() sets the volume to DEMO_CODEC_VOLUME on the right headphone. The program displays the message SAI Demo Started on the console, initializes the SAI, configures I2S, enables interrupts to handle FIFO errors, and then displays the following menu (note that the SD card and external digital microphone options are disabled): Please choose the option: 1.Record and Playback at same time 2.Playback sine Wave 3.Quit If option 1 is selected: The board is configured for record-playback by calling function BOARD_CONFIGCODEC_FOR_RECORD_PLAYBACK(). Finally, the function RecordPlayback() is called (program: recordPlayback.c) to record audio and playback on the headphone. If option 2 is selected: function BOARD_CONFIGCODEC_FOR_RECORD_PLAYBACK() is called, followed by function PlaybackSine() (program: playbackSineWave.c) to play a sine wave on the headphone. Function playbackSineWave: This function has 3 arguments: base address, frequency in Hz, and duration in seconds. The function arm_sin_q15() is called to generate values for trigonometric sine in q15 format. The generated sine wave values are stored in audioBuff(). The frequency of the data is then calculated by calling the FFT function do_ fft(). The calculated frequency is displayed on the Console. The function plays a 250 Hz sine wave for 5 seconds. The Function SAI_TransferSendEDMA() is called to perform a non-blocking SAI transfer to the headphone using DMA. All the prepared data in audioBuff() is sent out. Function recordPlayback: This function has 2 arguments: the base address and the duration in seconds. In this project, the duration is set to 30 seconds by the calling program sai.c. This function calls SAI_TransferReceiveEDMA() to read non-blocking data using DMA from the microphone into structure xfer and then to transfer this data to the headphone by calling function SAI_TransferSendEDMA(), in non-blocking mode using DMA.
● 271
Getting Started with NXP i.MX Development Board - UK.indd 271
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
Testing The steps are: • Connect the MIMXRT1010-EVK development kit to your PC • Connect a headphone to the headphone jack • Click on the project name (evkmimxrt1010_sai<Debug>) in Project Explorer. Click Quick Settings → SDK Debug Console and make sure that Semihost console is selected • Click debug under the Quickstart Panel to load the programs and start debugging • Click Run followed by Resume to start the program running in debug mode • You should see the menu displayed (Figure 14.6). Choose option 2 to hear the 250 Hz sine wave played for 5 seconds. You may find that the volume is very low. • Choose option 1. Speak to the on-board analog microphone, and you should hear the sound on the right speaker of the headphone • Repeat the testing as many times as desired • Click Run followed by Terminate to exit the debug mode
Figure 14.6 The menu
● 272
Getting Started with NXP i.MX Development Board - UK.indd 272
26-10-2023 09:35
Chapter 15 • References
Chapter 15 • References Readers may find useful information on MCUXpresso and related topics at the following web sites: https://www.nxp.com/design/development-boards/i-mx-evaluation-and-developmentboards/i-mx-rt1010-evaluation-kit:MIMXRT1010-EVK https://www.nxp.com/webapp/sps/download/preDownload.jsp?render=true https://www.nxp.com/document/guide/getting-started-with-i-mx-rt1010-evaluationkit:GS-MIMXRT1010-EVK https://www.nxp.com/webapp/sps/download/preDownload.jsp?render=true https://www.nxp.com/design/development-boards/i-mx-evaluation-and-developmentboards/i-mx-rt1010-evaluation-kit:MIMXRT1010-EVK https://community.nxp.com/t5/Blog/New-updated-MCUXpresso-tool-training-videosavailable/ba-p/1131113 https://www.nxp.com/design/training/getting-started-with-the-i-mx-rt1010-evk-andmcuxpresso:TIP-GS-IMXRT1010 https://arm-software.github.io/CMSIS_5/DSP/html/group__groupMatrix.html https://developer.arm.com/Additional%20Resources/CMSIS%20DSP%20Software%20 Library
● 273
Getting Started with NXP i.MX Development Board - UK.indd 273
26-10-2023 09:35
Get Started with the NXP i.MX RT1010 Development Kit
Index A ACK ADC AIFF Audio bandwidth Audio file formats Audio sound
125 191 264 263 263 262
B BC337 Binary down counter Binary up counter Bit clock Breakpoint
234 90 90 267 51
C Center aligned Common anode Common cathode ConfigTools Continuous conversion Conveyor belt CPHA CPOL
223 97 97 60 136 182 146 146
D DAP Deadtime insertion logic Debugging Degradation of quality DSP Duty cycle
14 224 30 263 262 220
E Edge aligned Event counter Export Extended mode External interrupt
223 93 27 136 187
F FPU
11
G GPT1 GPT2
14 14
H H-bridge H-bridge module HD44780
245 247 156
I Import IRFZ44
27 234
J JTAG connector
13
K KPP
14
L LDR LED indicators Light intensity LMD18200 LM35 197 LPUART
183 13 211 249 27 15
M Matrix operations Motor speed and direction control Motor speed control MP3 Multiplexed LED MQS
255 244 232 264 102 12
O Ohmmeter One-shot conversion
214 136
P PCM Periodic interrupt timer Pin_mux.c
264 12 23
● 274
Getting Started with NXP i.MX Development Board - UK.indd 274
26-10-2023 09:35
Index
Pointer register PortClear Port expander PortSet Pow Power supply PWM
136 77 125, 147 77 78 13 220
Q Quickstart Panel
50
R Random number Resume RS232
83 26 118
S SAI bus SCL SDA Serial communication Signal to noise ratio SPDIF SPI bus SysTick
269 124 124 117 263 12 145 21
T Temperatue controller Temperature sensor Terminate Thermostat mode Timer interrupt TMP102 TRNG
201 196 26 136 109 135 12
U Update code User button
61 13
V Visual studio
28
W WAV WMA Word select
264 264 267
● 275
Getting Started with NXP i.MX Development Board - UK.indd 275
26-10-2023 09:35
> LED and LCDs > ADC > I2C > SPI > PWM > UART > Motor Control > Audio and Digital Audio Processing (DSP)
About the Author Prof Dr Dogan Ibrahim has a BSc degree in electronic engineering, an MSc degree in automatic control engineering, and a PhD degree in digital signal processing. Dogan has worked in many industrial organizations before he returned to academic life. Prof Ibrahim is the author of over 60 technical books and over 200 technical articles on microcontrollers, microprocessors, and related fields. He is a Chartered electrical engineer and a Fellow of the Institution of Engineering Technology.
Get Started with the NXP i.MX RT1010 Development Kit
Conveniently, several on-board debug probes are supplied with the kit allowing you to debug your programs by talking directly to the MCU. Helped by the debugger, you can single-step through a program, insert breakpoints, view and modify variables, and so on. Using the MCUXpresso IDE and the SDK, many working and tested projects are developed in the book based on parts, modules, and technologies, including:
H0W2
At the heart of NXP Semiconductors‘ MIMXRT1010 Development Kit is the i.MX RT1010 Crossover MCU sporting an Arm Cortex-M7 core truly capable of running power- and memory hungry DSP applications. The popular MCUXpresso IDE is key to creating software for the development kit, while a powerful SDK is provided to reduce program development time and effort. The dev kit offers great connectivity through its audio CODECs, 4-way headphone jack, external speaker connection, microphone, and Arduino interface.
H0W2
Volume 3
Get Started with the NXP i.MX RT1010 Development Kit Develop Arm® Cortex®-M7 powered Audio, DSP and Motor Control Projects
Dogan Ibrahim
Dogan Ibrahim
Elektor International Media www.elektor.com
3 Cover HOW2 - Getting Started With NXP iMX Development Board.indd 3
knows how
26-10-2023 10:14