提交 5af97a1f 编写于 作者: mysterywolf's avatar mysterywolf

move documentation repo to rt-thread repo

上级 a65e6d16
# RT-Thread
RT-Thread (Real-Time Thread) is an open source embedded real-time operating system and released under Apache License v2.0. It has a strong scalability: from a nano kernel running on a tiny MCU, for example ARM Cortex-M0, or Cortex-M3/4/7, to a rich feature system running on MIPS32, ARM Cortex-A, even the emerging open source RISC-V architecture is supported. RT-Thread can run either on single-core systems or on symmetric multi-core processors(SMP) systems.
## Introduction
RT-Thread has not noly a real-time kernel, but also rich components. Its architecture is as follows:
![RT-Thread system framework](figures/02Software_framework_diagram.png)
- **Kernel**: It includes preemptive multi-task real-time scheduler, and infrastructure such as semaphore, mutex, mailbox, message queue, signal, event, memory management, timer management, interrupt management, etc. It also includes libcpu/BSP (file related to chip transplantation/board support package).
- **components**: It is a software unit on the RT-Thread kernel layer, such as command line (FinSH), device driver framework (Device Drivers), network framework, virtual file system (FAT, YAFFS, UFFS, ROM/RAM file system, etc.), TCP/IP network protocol stack (lwIP), libc/POSIX standard layer and so on. Generally, a software component is placed in a folder in the RT-Thread/components directory, and each software component is described by a *SConscript* file and added to the RT-Thread construction system. When a software component is opened in the system configuration, it will be compiled and linked to the final RT-Thread firmware.
- **Packages**: It is a middleware running on RT-Thread IoT operating system platform and facing different application fields. Packages consist of description information, source code or library files. These packages can be provided by RT-Thread, third-party developers and enterprises, and the license agreement of the packages is provided by the author of the packages. These software packages have strong reusability and high modularity, which greatly facilitates application developers to build their desired application systems in the shortest time. For more package information, visit the [RT-Thread package repository](https://github.com/RT-Thread-packages).
## Licence
RT-Thread is an open source software and has been licensed under Apache License Version 2.0 since v3.1.1. License information and copyright information can generally be seen at the beginning of the code:
```
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
```
To avoid possible future license conflicts, developers need to sign a Contributor License Agreement (CLA) when submitting PR to RT-Thread.
> Note: Because the BSP also contains the code provided by the chip manufacturer, this part of the code follows the license provided by the chip manufacturer, such as STM32 HAL, NXP, Atmel, etc. Relevant codes are usually only used in the chips of the corresponding manufacturers.
## Supported Architectures
RT-Thread RTOS can support many architectures,and has covered the major architectures in current applications. Architecture and chip manufacturer involved:
- **ARM Cortex-M0/M0+**:manufacturers like ST
- **ARM Cortex-M3**:manufacturers like ST、Winner Micro、MindMotion, ect.
- **ARM Cortex-M4**:manufacturers like ST、Nuvton、NXP、GigaDevice、Realtek、Ambiq Micro, ect.
- **ARM Cortex-M7**:manufacturers like ST、NXP
- **ARM Cortex-M23**:manufacturers like GigaDevice
- **ARM Cortex-R4**
- **ARM Cortex-A8/A9**:manufacturers like NXP
- **ARM7**:manufacturers like Samsung
- **ARM9**:manufacturers like Allwinner、Xilinx 、GOKE
- **ARM11**:manufacturers like Fullhan
- **MIPS32**:manufacturers like loongson、Ingenic
- **RISC-V**:manufacturers like Hifive、Kendryte
- **ARC**:manufacturers like SYNOPSYS
- **DSP**:manufacturers like TI
- **C-Sky**
- **x86**
## Supported IDE and Compiler
The main IDE/compilers supported by RT-Thread are:
- MDK KEIL
- IAR
- GCC
Use Python-based [scons](http://www.scons.org) for command-line builds.
# Source Code and Tools
**Get the source code**: The source code of RT-Thread is hosted on Github, and click on the link to get the source code.
- [Download RT-Thread source code](https://github.com/RT-Thread/rt-thread)
**Get the Env Tool**: To better help developers, the RT-Thread team also provides Env tools (or Env scripts for Linux/MacOS). On Windows, Env tool is a development assistant tool launched by RT-Thread. It provides compiling and building environment, graphical system configuration and software package management functions for project projects based on RT-Thread operating system. Its built-in menuconfig provides a simple and easy-to-use configuration tailoring tool, which can tailor the kernel, components and software packages freely, so that the system can be built in the way of building blocks.
- [Download Env Tool]()
- [User manual of Env](env/env.md)
# Getting Started
RT-Thread BSP can be compiled directly and downloaded to the corresponding development board for use. In addition, RT-Thread also provides qemu-vexpress-a9 BSP, which can be used without hardware platform. See the getting started guide below for details.
- [Getting Started of QEMU (Windows)](quick-start/quick_start_qemu/quick_start_qemu.md)
- [Getting Started of QEMU (Ubuntu)](quick-start/quick_start_qemu/quick_start_qemu_linux.md)
# Help
Any questions can be asked in the [issue section of rtthread-manual-doc](https://github.com/RT-Thread/rtthread-manual-doc/issues). By creating a new issue to describe your questions, community members will answer them.
# Contribution
If you are interested in RT-Thread and want to join in the development of RT-Thread and become a code contributor,please refer to the [Code Contribution Guide](documentation/contribution_guide/contribution_guide.md).
# Manual Catalogue
- [RT-Thread Introduction](introduction/introduction.md)
- [Start Guide: Simulate STM32F103 on KEIL simulator](quick-start/quick-start.md)
**Kernel**
- [Kernel Basics](basic/basic.md)
- [Thread Management](thread/thread.md)
- [Clock&Timer Management](timer/timer.md)
- [Inter-thread Synchronization](thread-sync/thread-sync.md)
- [Inter-thread Communication](thread-comm/thread-comm.md)
- [Memory Management](memory/memory.md)
- [Interrupt Management](interrupt/interrupt.md)
- [Kernel Porting](kernel-porting/kernel-porting.md)
**Tool**
- [User Manual of Env](env/env.md)
- [SCons](scons/scons.md)
**Device**
- [I/O Device Framework](device/device.md)
- [PIN Device](device/pin/pin.md)
- [UART Device](device/uart/uart.md)
- [ADC Device](device/adc/adc.md)
- [I2C Bus Device](device/i2c/i2c.md)
- [SPI Device](device/spi/spi.md)
- [PWM Device](device/pwm/pwm.md)
- [RTC Device](device/rtc/rtc.md)
- [HWTIMER Device](device/hwtimer/hwtimer.md)
- [WATCHDOG Device](device/watchdog/watchdog.md)
- [WLAN Device](device/wlan/wlan.md)
- [Sensor Device](device/sensor/sensor.md)
**Components**
- [FinSH Console](finsh/finsh.md)
- [Virtual File System](filesystem/README.md)
- [utest Framework](utest/utest.md)
- [Dynamic Module: dlmodule](dlmodule/README.md)
- [Socket Abstraction Layer: SAL](sal/sal.md)
- [AT Commands](at/at.md)
- [POSIX Interface](posix/README.md)
- [Ulog Log](ulog/ulog.md)
- [Power Management: PM](pm/pm.md)
- [Network Framework](network/network.md)
此差异已折叠。
此差异已折叠。
# Contribution Guide
We sincerely thank you for your contribution, and welcome to submit the code through GitHub's fork and Pull Request processes.
First, explain the word Pull Request. Pull request means to send a request. The purpose of the developer initiating Pull Request is to request the repository maintainer to adopt the code submitted by the developer.
When you want to correct mistakes in other people's repositories, follow the following procedure:
- To fork someone else's repository is equivalent to copying someone else's information. Because you can't guarantee that your modification is correct and beneficial to the project, you can't modify it directly in someone else's repository, but first fork it into your own git repository.
- Clone code to your own PC local, create a new branch, modify bugs or add new features, and then launch pull request to the original repository, so that the original repository manager can see the changes you submitted.
- The original repository manager reviews this submission and, if correct, merge it into his own project. Merge means merging, merging the part of code you modified into the original repository to add code or replace the original code. So far, the whole Pull Request process is over.
## Coding Style
Refer to the `coding_style_en.txt` file in the rt_thread project documentation directory for the RT-Thread code programming style.
## Preparation
Install Git: You need to add Git's directory to the system environment variable.
## Contribution Process
Now take RT-Thread repository as an example to illustrate the process of contributing code:
### Fork
Fork the RT-Thread/rt-thread repository into your git repository.
![fork rt-thread repository](figures/cloneformgit.png)
### Clone
In your repository, copy the repository links after your fork:
![clone rt-thread from your repo](figures/cloneformgit2.png)
You can use the `git clone` command to copy the repository to your PC:
```
git clone [url]
```
![git clone](figures/git_clone.png)
### Create a New Branch
It is recommended that you create your own development branch based on the master branch, and use following commands to create a new branch:
```
git checkout -b YourBranchName
```
For example, create a branch named "dev": `git checkout -b dev`.
### Developing
Modify bugs and submit new functional code. For example, suppose the developer adds a USB driver:
![Add a USB driver](figures/add_usb_driver.png)
### Temporarily Store Modified Files
Add all changes to the temporary area:
```
git add .
```
If you only want to add some specified files to the temporary area, use other commands of `git add`.
### Commit
Submit this modification to the local repository:
```
git commit -m "Describe your submission here"
```
> Note: If there are multiple commits in the local development branch, in order to ensure that the RT-Thread repository commit is clean, please tidy up the local commits. More than five commits are not accepted by Pull Request.。
### Push to Your Remote Repository
Push the modified content to the branch of your remote repository. It is recommended that the branch name of the remote repository be consistent with the local branch name.Use the following command to push:
```
git push origin YourBranchName
```
### Create a Pull Request
Enter the RT-Thread repository under your Github account and click `New pull request -> Create pull request`. Make sure you choose the right branch.
![Create a Pull Request](figures/pull_request_step2.png)
Step 1: Fill in the title of this Pull Request
Step 2: Modify the description information of this Pull Request (modify it in `Write` and preview it with `Preview`):
- Modify PR Description: Replace the content in the red box below with the description of this pull request according to the requirements in the red box below.
- Check PR Options: Fill in [x] in the OK Options check box to confirm. Note that there are no spaces on both sides of [x].
![Modify PR Description and Check PR Options](figures/pr_description.png)
Step 3:Create pull request.
### Sign CLA
The first contribution to RT-Thread requires signing the *Contributor License Agreement*.
![Sign CLA](figures/cla.png)
Make sure that CLA shows successful signing and CI compilation, as shown in the following figure:
![CLA successful](figures/checkok.png)
Note: Do not submit commmit using a non-GitHub account, or commit using a different account, which can lead to CLA signing failure.
### Review Pull Request
Once the request is successful, the RT-Thread maintainer can see the code you submitted. The code will be reviewed and comments will be filled in on GitHub. Please check the PR status in time and update the code according to the comments.
### Merge Pull Request
If the Pull Request code is okay, the code will be merged into the RT-Thread repository. This time Pull Request succeeded.
So far, we have completed a code contribution process.
## Keep in Sync with RT-Thread Repository
The content of the RT-Thread GitHub repository is always updated. To develop based on the latest RT-Thread code, you need to update the local repository.
After clone, the local master branch content is consistent with the master branch content of the RT-Thread repository. But when the RT-Thread repository is updated, your local code is different from the RT-Thread code.
The local master is synchronized with the RT-Thread repository of your own GitHub account. If there is no content modification for the master branch (please create a new branch for development), then you can keep the local code synchronized with the RT-Thread repository according to the following steps:
- To view the existing remote repository, there is usually only one default origin, which is your own remote repository:
```c
$ git remote -v
origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (fetch)
origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (push)
```
* Add the RT-Thread remote repository and name it `rtt`, or you can customize the name by yourself:
```c
$ git remote add rtt https://github.com/RT-Thread/rt-thread.git
```
* View all remote repositories tracked locally:
```c
$ git remote -v
origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (fetch)
origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (push)
rtt https://github.com/RT-Thread/rt-thread.git (fetch)
rtt https://github.com/RT-Thread/rt-thread.git (push)
```
* Pull the code from the master branch of RT-Thread remote repository and merge it into the local master branch:
```c
git pull rtt master
```
## Reference
* Refer to the [*GitHub - Contributing to a Project*](https://git-scm.com/book/en/v2/GitHub-Contributing-to-a-Project) section of the official Git document for details.
# ADC Device
## An Introduction to ADC
ADC refers to analog to digital converter which is a device that converts continuously changing analog signals into discrete digital signals. Analog signals , such as temperature, pressure, sound, or images, need to be converted into digital forms that are easier to be stored, processed, and transmitted. The analog-to-digital converter can do this, and it can be found in a variety of different products. The corresponding DAC (Digital-to-Analog Converter) has a reverse conversion process compared to that of the ADC. The ADC was first used to convert wireless signals to digital signals such as television signals, or signals from long-short broadcast stations.
### Conversion Process
As shown in the figure below, the analog-to-digital conversion generally involves steps of sampling, holding, quantifying, and encoding. In actual circuits, some processes are combined, such as sampling and holding, and quantization and encoding are implemented simultaneously in the conversion process.
![ADC Conversion Process](figures/adc-p.png)
Sampling is the conversion of analog signals that changes continuously over time into time-discrete analog signals. It takes a certain amount of time for the analog signals obtained by sampling to be converted into digital signals. In order to provide a stable value for the subsequent quantization coding process, it is required to keep the sampling analog signals for a period of time after the sampling circuit.
The process of converting a numerically continuous analog quantity into a digital quantity is called quantization. Digital signals are discrete numerically. The output voltage of the sample-and-hold circuit also needs to be naturalized to a corresponding discrete level in a similar way, and any digital quantity can only be an integer multiple of a certain minimum quantity unit. The quantized value also requires the encoding process, which is the digital output of the A/D converter.
### Resolution
The resolution is expressed in binary (or decimal) digits. Generally, there are 8 bits, 10 bits, 12 bits, 16 bits, etc. It explains the resolution capability of the input signals by the analog-to-digital converter. The more bits, the higher the resolution, and the more accurate the analog signal will be.
### Precision
Precision represents the maximum error value between the analog and real values of the ADC devices at all numerical points, that is, the distance at which the output value deviates from the linear maximum.
>Precision and resolution are two different concepts, so please pay attention to the distinction.
### Conversion Rate
The conversion rate is the reciprocal of the time it takes for the A/D converter to complete an AD conversion from analog to digital. For example, an A/D converter with a conversion rate of 1MHz means that an AD conversion time is 1 microsecond.
## Access ADC Device
The application accesses the ADC hardware through the ADC device management interface provided by RT-Thread. The relevant interfaces are as follows:
| **Function** | Description |
| --------------- | ------------------ |
| rt_device_find() | Find device handles based on ADC device name |
| rt_adc_enable() | Enable ADC devices |
| rt_adc_read() | Read ADC device data |
| rt_adc_disable() | Close the ADC device |
### Find ADC Devices
The application gets the device handle based on the ADC device name, which in turn operates the ADC device. Functions for looking for devices are as follows:
```c
rt_device_t rt_device_find(const char* name);
```
| **Parameter** | Description |
| -------- | ---------------------------------- |
| name | The name of the ADC device |
| **Return** | —— |
| Device handle | Finding the corresponding device will return to the corresponding device handle |
| RT_NULL | No device found |
In general, the names of the ADC device registered to the system are adc0, adc1, etc., and an usage example is as follows:
```c
#define ADC_DEV_NAME "adc1" /* ADC device name */
rt_adc_device_t adc_dev; /* ADC device handle */
/* find the device */
adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
```
### Enable ADC Channel
Before reading the ADC device data, Use the following function to enable the device:
```c
rt_err_t rt_adc_enable(rt_adc_device_t dev, rt_uint32_t channel);
```
| Parameter | Description |
| ---------- | ------------------------------- |
| dev | ADC device handle |
| channel | ADC channel |
| **Return** | —— |
| RT_EOK | Succeed |
| -RT_ENOSYS | Failed, the device operation method is empty |
| Other error code | Failed |
An usage example is as follows:
```c
#define ADC_DEV_NAME "adc1" /* ADC device name */
#define ADC_DEV_CHANNEL 5 /* ADC channel */
rt_adc_device_t adc_dev; /* ADC device handle */
/* find the device */
adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
/* enable the device */
rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);
```
### Read ADC Channel Sample Values
Reading the ADC channel sample values can be done by the following function:
```c
rt_uint32_t rt_adc_read(rt_adc_device_t dev, rt_uint32_t channel);
```
| Parameter | Description |
| ---------- | ----------------- |
| dev | ADC device handle |
| channel | ADC channel |
| **Return** | —— |
| Read values | |
An example of using the ADC sampled voltage value is as follows:
```c
#define ADC_DEV_NAME "adc1" /* ADC device name */
#define ADC_DEV_CHANNEL 5 /* ADC channel */
#define REFER_VOLTAGE 330 /* Reference voltage 3.3V, data accuracy multiplied by 100 and reserve 2 decimal places*/
#define CONVERT_BITS (1 << 12) /* The number of conversion bits is 12 */
rt_adc_device_t adc_dev; /* ADC device handle */
rt_uint32_t value
/* find the device */
adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
/* enable the device */
rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);
/* Read sampling values */
value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL);
/* Convert to the corresponding voltage value */
vol = value * REFER_VOLTAGE / CONVERT_BITS;
rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
```
The calculation formula of the actual voltage value is: `sampling value * reference voltage/(1 << resolution digit)`. In the above example, variable *vol* was enlarged 100 times, so finally the integer part of voltage is obtained through *vol / 100*, and the decimal part of voltage is obtained through *vol % 100*.
### Close the ADC Channel
Use the following function can close the ADC channel :
```c
rt_err_t rt_adc_disable(rt_adc_device_t dev, rt_uint32_t channel);
```
| **Parameter** | **Description** |
| ---------- | ------------------------------- |
| dev | ADC device handle |
| channel | ADC channel |
| **Return** | —— |
| RT_EOK | Succeed |
| -RT_ENOSYS | Failed, the device operation method is empty |
| Other error code | Failed |
An example:
```c
#define ADC_DEV_NAME "adc1" /* ADC device name */
#define ADC_DEV_CHANNEL 5 /* ADC channel */
rt_adc_device_t adc_dev; /* ADC device handle */
rt_uint32_t value
/* find the device */
adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
/* enable the device */
rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);
/* read sampling values */
value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL);
/* convert to the corresponding voltage value */
vol = value * REFER_VOLTAGE / CONVERT_BITS;
rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
/* close the channel */
rt_adc_disable(adc_dev, ADC_DEV_CHANNEL);
```
### FinSH Command
Before using the device, you need to find out whether the device exists. You can use the command `adc probe` followed by the name of the registered ADC device. As follows:
```c
msh >adc probe adc1
probe adc1 success
```
A channel of the enabled device can use the command `adc enable` followed by the channel number.
```c
msh >adc enable 5
adc1 channel 5 enables success
```
To read data from a channel of an ADC device, you can use the command `adc read` followed by the channel number.
```c
msh >adc read 5
adc1 channel 5 read value is 0x00000FFF
msh >
```
To close a channel of an ADC device, you can use the command `adc disable` followed by the channel number.
```c
msh >adc disable 5
adc1 channel 5 disable success
msh >
```
## ADC Device Usage Example
The specific usage of the ADC device can refer to the following sample code. The main steps of the sample code are as follows:
1. First find the device handle based on the ADC device name “adc1”.
2. After the device is enabled, read the sample value of the corresponding channel 5 of the adc1 device, and then calculate the actual voltage value with the resolution of 12 bits and the reference voltage of 3.3V.
3. Finally close the corresponding channel of the ADC device.
Running result: Print the raw and converted data which actually read , and print the calculated actual voltage value.
```c
/*
* Program Listing: ADC Device Usage Routines
* The routine exports the adc_sample command to the control terminal
* adc_sample Command call format: adc_sample
* Program function: The voltage value is sampled by the ADC device and converted to a numerical value.
* The sample code reference voltage is 3.3V and the number of conversion bits is 12 bits.
*/
#include <rtthread.h>
#include <rtdevice.h>
#define ADC_DEV_NAME "adc1" /* ADC device name */
#define ADC_DEV_CHANNEL 5 /* ADC channel */
#define REFER_VOLTAGE 330 /* Reference voltage 3.3V, data accuracy multiplied by 100 and reserve 2 decimal places*/
#define CONVERT_BITS (1 << 12) /* The number of conversion bits is 12 */
static int adc_vol_sample(int argc, char *argv[])
{
rt_adc_device_t adc_dev;
rt_uint32_t value, vol;
rt_err_t ret = RT_EOK;
/* find the device */
adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
if (adc_dev == RT_NULL)
{
rt_kprintf("adc sample run failed! can't find %s device!\n", ADC_DEV_NAME);
return RT_ERROR;
}
/* enable the device */
ret = rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);
/* read sampling values */
value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL);
rt_kprintf("the value is :%d \n", value);
/* convert to the corresponding voltage value */
vol = value * REFER_VOLTAGE / CONVERT_BITS;
rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
/* close the channel */
ret = rt_adc_disable(adc_dev, ADC_DEV_CHANNEL);
return ret;
}
/* export to the msh command list */
MSH_CMD_EXPORT(adc_vol_sample, adc voltage convert sample);
```
此差异已折叠。
# HWTIMER Device
## Introduction to the Timer
Hardware timers generally have two modes of operation, timer mode and counter mode. No matter which mode is operated, it works by counting the pulse signal counted by the internal counter module. Here are some important concepts of timers.
**Counter mode:** Counts the external pulse.
**Timer mode **: Counts the internal pulse. Timers are often used as timing clocks for timing detection, timing response, and timing control.
**Counter **: Counter can count up or down. The maximum count value of the 16-bit counter is 65535, and the maximum value of the 32-bit counter is 4294967295.
**Counting frequency **:As for the number of counts within the counter time unit under the timer mode, since the system clock frequency is fixed, the timer time can be calculated according to the counter count value. `Timing time = count value / count frequency`. For example, if the counting frequency is 1 MHz, the counter counts once is 1 / 1000000 second. That is, every 1 microsecond counter is incremented by one (or subtract one), at this time, the maximum timing capability of the 16-bit counter is 65535 microseconds, which is 65.535 milliseconds.
## Access Hardware Timer Device
The application accesses the hardware timer device through the I/O device management interface provided by RT-Thread. The related interfaces are as follows:
| **Function** | **Description** |
| -------------------- | ---------------------------------- |
| rt_device_find() | to look up the timer device |
| rt_device_open() | to open the timer device in read-write mode |
| rt_device_set_rx_indicate() | to set the timeout callback function |
| rt_device_control() | to control the timer device, you can set the timing mode (single time /cycle),counting frequency, or stop the timer |
| rt_device_write() | to set the timeout value of the timer. The timer then starts |
| rt_device_read() | to get the current value of the timer |
| rt_device_close() | to turn off the timer device. |
### Find Timer Device
The application obtains the device handle based on the hardware timer device name, and thus can operate the hardware timer device. The device function is as follows:
```c
rt_device_t rt_device_find(const char* name);
```
| Parameter | **Description** |
| -------- | ---------------------------------- |
| name | hardware timer device name |
| **return** | —— |
| timer device handle | will return to the corresponding device handle if the corresponding device is found |
| RT_NULL | No device found |
In general, the hardware timer device name registered to the system is timer0, timer1, etc. The usage examples are as follows:
```c
#define HWTIMER_DEV_NAME "timer0" /* timer name */
rt_device_t hw_dev; /* timer device handle */
/* find timer device */
hw_dev = rt_device_find(HWTIMER_DEV_NAME);
```
### Open Timer Device
With the device handle, the application can open the device. When the device is open, it will detect whether the device has been initialized. If it is not initialized, it will call the initialization interface to initialize the device by default. Open the device with the following function:
```c
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
```
| Parameter | Description |
| ---------- | ------------------------------- |
| dev | hardware timer device handle |
| oflags | device open mode, is generally opened in read and write mode, which is to take the value:RT_DEVICE_OFLAG_RDWR |
| **return** | —— |
| RT_EOK | device opened successfully |
| other error code | device fail to open |
An example of use is as follows:
```c
#define HWTIMER_DEV_NAME "timer0" /* timer name */
rt_device_t hw_dev; /* timer device handle */
/* find timer device */
hw_dev = rt_device_find(HWTIMER_DEV_NAME);
/* to open the timer device in read-write mode */
rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
```
### Set the Timeout Callback Function
Set the timer timeout callback function by the following function, this callback function will be called when the timer expires:
```c
rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size))
```
| Parameter | **Description** |
| ---------- | ------------------------------- |
| dev | device handle |
| rx_ind | timeout callback function, provided by the caller |
| **return** | —— |
| RT_EOK | success |
An example of use is as follows:
```c
#define HWTIMER_DEV_NAME "timer0" /* timer name */
rt_device_t hw_dev; /* timer device handle */
/* timer timeout callback function */
static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
{
rt_kprintf("this is hwtimer timeout callback fucntion!\n");
rt_kprintf("tick is :%d !\n", rt_tick_get());
return 0;
}
static int hwtimer_sample(int argc, char *argv[])
{
/* find timer device */
hw_dev = rt_device_find(HWTIMER_DEV_NAME);
/* open the device in read and write mode */
rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
/* set the timeout callback function */
rt_device_set_rx_indicate(hw_dev, timeout_cb);
}
```
### Control the Timer Device
By commanding the control word, the application can configure the hardware timer device by the following function:
```c
rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
```
| Parameter | **Description** |
| ---------------- | ------------------------------ |
| dev | device handle |
| cmd | command control word |
| arg | controlled parameter |
| **return** | —— |
| RT_EOK | function executed successfully |
| -RT_ENOSYS | execution failed,dev is null |
| other error code | execution failed |
The command control words available for the hardware timer device are as follows:
| **Control word** | Description |
| ---------------------- | ------------------------ |
| HWTIMER_CTRL_FREQ_SET | set the counting frequency |
| HWTIMER_CTRL_STOP | stop the timer |
| HWTIMER_CTRL_INFO_GET | get timer feature information |
| HWTIMER_CTRL_MODE_SET | set timer mode |
Get the timer parameter arg,which is a pointer to the structure struct rt_hwtimer_info, to save the obtained information.
>Setting frequency is valid only when the timer hardware and driver support sets the counting frequency. Generally, the default frequency of the driving setting can be used.
When setting the timer mode, the parameter arg can take the following values:
```c
HWTIMER_MODE_ONESHOT /* Single timing */
HWTIMER_MODE_PERIOD /* Periodic timing */
```
An example of using the timer count frequency and timing mode is as follows:
```c
#define HWTIMER_DEV_NAME "timer0" /* timer name */
rt_device_t hw_dev; /* timer device handle */
rt_hwtimer_mode_t mode; /* timer mode */
rt_uint32_t freq = 10000; /* couting frequency */
/* Timer timeout callback function */
static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
{
rt_kprintf("this is hwtimer timeout callback fucntion!\n");
rt_kprintf("tick is :%d !\n", rt_tick_get());
return 0;
}
static int hwtimer_sample(int argc, char *argv[])
{
/* find timer device */
hw_dev = rt_device_find(HWTIMER_DEV_NAME);
/* open the device in read and write mode */
rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
/* Set the timeout callback function */
rt_device_set_rx_indicate(hw_dev, timeout_cb);
/* Set the counting frequency (1Mhz or the supported minimum counting frequency by default) */
rt_device_control(hw_dev, HWTIMER_CTRL_FREQ_SET, &freq);
/* Set the mode to periodic timer */
mode = HWTIMER_MODE_PERIOD;
rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
}
```
### Set the Timer Timeout Value
The timer timeout value can be set by the following function:
```c
rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);
```
| **Parameter** | Description |
| ---------- | ------------------------------------------ |
| dev | device handle |
| pos | write data offset, unused now, can set 0 value |
| buffer | pointer to the timer timeout structure |
| size | timeout structure size |
| **return** | —— |
| The actual size of the written data | |
| 0 | fail |
The prototype of the timeout structure is shown below :
```c
typedef struct rt_hwtimerval
{
rt_int32_t sec; /* second */
rt_int32_t usec; /* microsecond */
} rt_hwtimerval_t;
```
An example of using the timer timeout value is as follows:
```c
#define HWTIMER_DEV_NAME "timer0" /* timer name */
rt_device_t hw_dev; /* timer device handle */
rt_hwtimer_mode_t mode; /* timer mode */
rt_hwtimerval_t timeout_s; /* Timer timeout value */
/* Timer timeout callback function */
static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
{
rt_kprintf("this is hwtimer timeout callback fucntion!\n");
rt_kprintf("tick is :%d !\n", rt_tick_get());
return 0;
}
static int hwtimer_sample(int argc, char *argv[])
{
/* find timer device */
hw_dev = rt_device_find(HWTIMER_DEV_NAME);
/* open the device in read-write mode */
rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
/* set the timeout callback function */
rt_device_set_rx_indicate(hw_dev, timeout_cb);
/* set the mode as periodic timer */
mode = HWTIMER_MODE_PERIOD;
rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
/* Set the timer timeout value to 5s and start the timer */
timeout_s.sec = 5; /* second */
timeout_s.usec = 0; /* microsecond */
rt_device_write(hw_dev, 0, &timeout_s, sizeof(timeout_s));
}
```
### Obtain the Current Value of the Timer
The current value of the timer can be obtained by the following function:
```c
rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
```
| **Parameter** | Description |
| ---------- | ------------------------------------------ |
| dev | timer device handle |
| pos | write data offset, unused now , can set 0 value |
| buffer | output parameter, a pointer point to the timeout structure |
| size | timeout structure size |
| **return** | —— |
| Timeout structure size | success |
| 0 | fail |
An example of use is shown below:
```c
rt_hwtimerval_t timeout_s; /* Used to save the time the timer has elapsed */
/* Read the elapsed time of the timer */
rt_device_read(hw_dev, 0, &timeout_s, sizeof(timeout_s));
```
### Close the Timer Device
The timer device can be closed by the following function:
```c
rt_err_t rt_device_close(rt_device_t dev);
```
| Parameter | Description |
| ---------- | ---------------------------------- |
| dev | timer device handle |
| **return** | —— |
| RT_EOK | close device successfully |
| -RT_ERROR | the device has been completely shut down and cannot be closed repeatedly |
| other error code | fail to close the device |
To close the device interface and open the device interface should be used in pairs. When open a device, close the device after use , so that the device can be completely shut down, otherwise the device will remain a opening status.
An example of use is shown below:
```c
#define HWTIMER_DEV_NAME "timer0" /* timer name */
rt_device_t hw_dev; /* timer device handle */
/* find timer device */
hw_dev = rt_device_find(HWTIMER_DEV_NAME);
... ...
rt_device_close(hw_dev);
```
>Timing errors may occur. Assume that the counter has a maximum value of 0xFFFF, a counting frequency of 1Mhz, and a timing time of 1 second and 1 microsecond. Since the timer can only count up to 65535us at a time, the timing requirement for 1000001us can be completed 20 times at 50000us, and the calculation error will be 1us.
## Hardware Timer Device Usage Example
The specific use of the hardware timer device can refer to the following sample code. The main steps of the sample code are as follows:
1. First find the device handle based on the timer device name "timer0".
2. Open the device "timer0" in read-write mode.
3. Set the timer timeout callback function.
4. Set the timer mode to periodic timer and set the timeout period to 5 seconds. At this time, the timer starts.
5. Read the timer after 3500ms delay, the read value will be displayed in seconds and microseconds.
```c
/*
* Program listing: This is an hwtimer device usage routine
  * The routine exports the hwtimer_sample command to the control terminal
  * Command call format: hwtimer_sample
  * Program function: The hardware timer timeout callback function periodically prints the current tick value, and the difference between the two tick values is converted to the time equivalent to the timing time value.
*/
#include <rtthread.h>
#include <rtdevice.h>
#define HWTIMER_DEV_NAME "timer0" /* timer name */
/* Timer timeout callback function */
static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
{
rt_kprintf("this is hwtimer timeout callback fucntion!\n");
rt_kprintf("tick is :%d !\n", rt_tick_get());
return 0;
}
static int hwtimer_sample(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
rt_hwtimerval_t timeout_s; /* timer timeout value */
rt_device_t hw_dev = RT_NULL; /* timer device value */
rt_hwtimer_mode_t mode; /* timer mode */
/* find timer device */
hw_dev = rt_device_find(HWTIMER_DEV_NAME);
if (hw_dev == RT_NULL)
{
rt_kprintf("hwtimer sample run failed! can't find %s device!\n", HWTIMER_DEV_NAME);
return RT_ERROR;
}
/* Open the device in read-write mode */
ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
if (ret != RT_EOK)
{
rt_kprintf("open %s device failed!\n", HWTIMER_DEV_NAME);
return ret;
}
/* set timeout callback function */
rt_device_set_rx_indicate(hw_dev, timeout_cb);
/* Setting mode is periodic timer */
mode = HWTIMER_MODE_PERIOD;
ret = rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
if (ret != RT_EOK)
{
rt_kprintf("set mode failed! ret is :%d\n", ret);
return ret;
}
/* Set the timer timeout value to 5s and start the timer. */
timeout_s.sec = 5; /* second */
timeout_s.usec = 0; /* microsecond */
if (rt_device_write(hw_dev, 0, &timeout_s, sizeof(timeout_s)) != sizeof(timeout_s))
{
rt_kprintf("set timeout value failed\n");
return RT_ERROR;
}
/* delay 3500ms */
rt_thread_mdelay(3500);
/* read the current value of timer */
rt_device_read(hw_dev, 0, &timeout_s, sizeof(timeout_s));
rt_kprintf("Read: Sec = %d, Usec = %d\n", timeout_s.sec, timeout_s.usec);
return ret;
}
/* Export to the msh command list */
MSH_CMD_EXPORT(hwtimer_sample, hwtimer sample);
```
# I2C Bus Device
## Introduction of I2C
The I2C (Inter Integrated Circuit) bus is a half-duplex, bidirectional two-wire synchronous serial bus developed by PHILIPS. The I2C bus has only two signal lines, one is the bidirectional data line SDA (serial data), and the other is the bidirectional clock line SCL (serial clock). The SPI bus has two lines for receiving data and transmitting data between the master and slave devices, while the I2C bus uses only one line for data transmission and reception.
Like SPI, I2C works in a master-slave manner. Unlike SPI-master-multi-slave architecture, it allows multiple master devices to exist at the same time. Each device connected to the bus has a unique address, and the master device initiates data transfer, and generates a clock signal. The slave device is addressed by the master device, and only one master device is allowed at a time. As shown below:
![I2C Bus master-slave device connection mode](figures/i2c1.png)
The main data transmission format of the I2C bus is shown in the following figure:
![I2C Bus Data Transmission Format](figures/i2c2.png)
When the bus is idle, both SDA and SCL are in a high state. When the host wants to communicate with a slave, it will send a start condition first, then send the slave address and read and write control bits, and then transfer the data (host send or receive data). The host will send a stop condition when the data transfer ends. Each byte transmitted is 8 bits, with the high bit first and the low bit last. The different terms in the data transmission process are as follows:
* **Starting Condition:** When SCL is high, the host pulls SDA low, indicating that data transfer is about to begin.
* **Slave Address:** The first byte sent by the master is the slave address, the upper 7 bits are the address, the lowest bit is the R/W read/write control bit, R/W bit equals to 1 means the read operation, and 0 means the write operation. The general slave address has 7-bit address mode and 10-bit address mode. In the 10-bit address mode, the first 7 bits of the first byte are a combination of 11110XX, where the last two bits (XX) are two highest 10-bit addresses. The second byte is the remaining 8 bits of the 10-bit slave address, as shown in the following figure:
![7-bit address and 10-bit address format](figures/i2c3.png)
* **Answer Signal:** Each time a byte of data is transmitted, the receiver needs to reply with an ACK (acknowledge). The slave sends an ACK when writing data and the ACK by the host when reading data. When the host reads the last byte of data, it can send NACK (Not acknowledge) and then stop the condition.
* **Data:** After the slave address is sent, some commands may be sent, depending on the slave, and then the data transmission starts, and is sent by the master or the slave. Each data is 8 bits, and the number of bytes of data is not limited.
* **Repeat Start Condition:** In a communication process, when the host may need to transfer data with different slaves or need to switch read and write operations, the host can send another start condition.
* **Stop Condition:** When SDA is low, the master pulls SCL high and stays high, then pulls SDA high to indicate the end of the transfer.
## Access to I2C Bus Devices
In general, the MCU's I2C device communicates as a master and slave. In the RT-Thread, the I2C master is virtualized as an I2C bus device. The I2C slave communicates with the I2C bus through the I2C device interface. The related interfaces are as follows:
| **Function** | **Description** |
| --------------- | ---------------------------------- |
| rt_device_find() | Find device handles based on I2C bus device name |
| rt_i2c_transfer() | transfer data |
### Finding I2C Bus Device
Before using the I2C bus device, you need to obtain the device handle according to the I2C bus device name, so that you can operate the I2C bus device. The device function is as follows.
```c
rt_device_t rt_device_find(const char* name);
```
| Parameter | Description |
| -------- | ---------------------------------- |
| name | I2C bus device name |
| **Return Value** | —— |
| device handle | Finding the corresponding device will return the corresponding device handle |
| RT_NULL | No corresponding device object found |
In general, the name of the I2C device registered to the system is i2c0, i2c1, etc. The usage examples are as follows:
```c
#define AHT10_I2C_BUS_NAME "i2c1" /* Sensor connected I2C bus device name */
struct rt_i2c_bus_device *i2c_bus; /* I2C bus device handle */
/* Find the I2C bus device and get the I2C bus device handle */
i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name);
```
### Data Transmission
You can use `rt_i2c_transfer()` for data transfer by getting the I2C bus device handle. The function prototype is as follows:
```c
rt_size_t rt_i2c_transfer(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg msgs[],
rt_uint32_t num);
```
| Parameter | Description |
|--------------------|----------------------|
| bus | I2C bus device handle |
| msgs[] | Message array pointer to be transmitted |
| num | The number of elements in the message array |
| **Return Value** | —— |
| the number of elements in the message array | succeeded |
| error code | failed |
Like the custom transport interface of the SPI bus, the data transmitted by the custom transport interface of the I2C bus is also in units of one message. The parameter msgs[] points to the array of messages to be transmitted. The user can customize the content of each message to implement two different data transmission modes supported by the I2C bus. If the master needs to send a repeat start condition, it will need to send 2 messages.
>This function will call rt_mutex_take(), which cannot be called inside the interrupt service routine, which will cause assertion to report an error.
The prototypes of the I2C message data structure are as follows:
```c
struct rt_i2c_msg
{
rt_uint16_t addr; /* Slave address */
rt_uint16_t flags; /* Reading, writing signs, etc. */
rt_uint16_t len; /* Read and write data bytes */
rt_uint8_t *buf; /* Read and write data buffer pointer */
}
```
Slave address (addr): Supports 7-bit and 10-bit binary addresses. You need to view the data sheets of different devices.
>The slave address used by the RT-Thread I2C device interface does not contain read/write bits. The read/write bit control needs to modify the flag `flags`.
The flags `flags` can be defined as macros that can be combined with other macros using the bitwise operation "|" as needed.
```c
#define RT_I2C_WR 0x0000 /* Write flag */
#define RT_I2C_RD (1u << 0) /* Read flag */
#define RT_I2C_ADDR_10BIT (1u << 2) /* 10-bit address mode */
#define RT_I2C_NO_START (1u << 4) /* No start condition */
#define RT_I2C_IGNORE_NACK (1u << 5) /* Ignore NACK */
#define RT_I2C_NO_READ_ACK (1u << 6) /* Do not send ACK when reading */
```
Examples of use are as follows:
```c
#define AHT10_I2C_BUS_NAME "i2c1" /* Sensor connected I2C bus device name */
#define AHT10_ADDR 0x38 /* Slave address */
struct rt_i2c_bus_device *i2c_bus; /* I2C bus device handle */
/* Find the I2C bus device and get the I2C bus device handle */
i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name);
/* Read sensor register data */
static rt_err_t read_regs(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *buf)
{
struct rt_i2c_msg msgs;
msgs.addr = AHT10_ADDR; /* Slave address */
msgs.flags = RT_I2C_RD; /* Read flag */
msgs.buf = buf; /* Read and write data buffer pointer */
msgs.len = len; /* Read and write data bytes */
/* Call the I2C device interface to transfer data */
if (rt_i2c_transfer(bus, &msgs, 1) == 1)
{
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
```
## I2C Bus Device Usage Example
The specific usage of the I2C device can be referred to the following sample code. The main steps of the sample code are as follows:
1. First find the I2C name based on the I2C device name, get the device handle, and then initialize the aht10 sensor.
2. The two functions that control the sensor are the write sensor register `write_reg()` and the read sensor register `read_regs()`, both called `rt_i2c_transfer()` to transfer the data. The function `read_temp_humi()` calls the above two functions to read the temperature and humidity information.
```c
/*
* Program listing: This is an I2C device usage routine
* The routine exports the i2c_aht10_sample command to the control terminal
* Command call format: i2c_aht10_sample i2c1
* Command explanation: The second parameter of the command is the name of the I2C bus device to be used. If it is empty, the default I2C bus device is used.
* Program function: read the temperature and humidity data of the aht10 sensor and print.
*/
#include <rtthread.h>
#include <rtdevice.h>
#define AHT10_I2C_BUS_NAME "i2c1" /* Sensor connected I2C bus device name */
#define AHT10_ADDR 0x38 /* Slave address */
#define AHT10_CALIBRATION_CMD 0xE1 /* Calibration command */
#define AHT10_NORMAL_CMD 0xA8 /* General command */
#define AHT10_GET_DATA 0xAC /* Get data command */
static struct rt_i2c_bus_device *i2c_bus = RT_NULL; /* I2C bus device handle */
static rt_bool_t initialized = RT_FALSE; /* Sensor initialization status */
/* Write sensor register */
static rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t *data)
{
rt_uint8_t buf[3];
struct rt_i2c_msg msgs;
buf[0] = reg; //cmd
buf[1] = data[0];
buf[2] = data[1];
msgs.addr = AHT10_ADDR;
msgs.flags = RT_I2C_WR;
msgs.buf = buf;
msgs.len = 3;
/* Call the I2C device interface to transfer data */
if (rt_i2c_transfer(bus, &msgs, 1) == 1)
{
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
/* Read sensor register data */
static rt_err_t read_regs(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *buf)
{
struct rt_i2c_msg msgs;
msgs.addr = AHT10_ADDR;
msgs.flags = RT_I2C_RD;
msgs.buf = buf;
msgs.len = len;
/* Call the I2C device interface to transfer data */
if (rt_i2c_transfer(bus, &msgs, 1) == 1)
{
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
static void read_temp_humi(float *cur_temp, float *cur_humi)
{
rt_uint8_t temp[6];
write_reg(i2c_bus, AHT10_GET_DATA, 0); /* send command */
rt_thread_mdelay(400);
read_regs(i2c_bus, 6, temp); /* obtian sensor data */
/* Humidity data conversion */
*cur_humi = (temp[1] << 12 | temp[2] << 4 | (temp[3] & 0xf0) >> 4) * 100.0 / (1 << 20);
/* Temperature data conversion */
*cur_temp = ((temp[3] & 0xf) << 16 | temp[4] << 8 | temp[5]) * 200.0 / (1 << 20) - 50;
}
static void aht10_init(const char *name)
{
rt_uint8_t temp[2] = {0, 0};
/* Find the I2C bus device and get the I2C bus device handle */
i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name);
if (i2c_bus == RT_NULL)
{
rt_kprintf("can't find %s device!\n", name);
}
else
{
write_reg(i2c_bus, AHT10_NORMAL_CMD, temp);
rt_thread_mdelay(400);
temp[0] = 0x08;
temp[1] = 0x00;
write_reg(i2c_bus, AHT10_CALIBRATION_CMD, temp);
rt_thread_mdelay(400);
initialized = RT_TRUE;
}
}
static void i2c_aht10_sample(int argc, char *argv[])
{
float humidity, temperature;
char name[RT_NAME_MAX];
humidity = 0.0;
temperature = 0.0;
if (argc == 2)
{
rt_strncpy(name, argv[1], RT_NAME_MAX);
}
else
{
rt_strncpy(name, AHT10_I2C_BUS_NAME, RT_NAME_MAX);
}
if (!initialized)
{
/* Sensor initialization */
aht10_init(name);
}
if (initialized)
{
/* Read temperature and humidity data */
read_temp_humi(&temperature, &humidity);
rt_kprintf("read aht10 sensor humidity : %d.%d %%\n", (int)humidity, (int)(humidity * 10) % 10);
rt_kprintf("read aht10 sensor temperature: %d.%d \n", (int)temperature, (int)(temperature * 10) % 10);
}
else
{
rt_kprintf("initialize sensor failed!\n");
}
}
/* Export to the msh command list */
MSH_CMD_EXPORT(i2c_aht10_sample, i2c aht10 sample);
```
# PIN Device
## Introduction of Pin
The pins on the chip are generally divided into four categories: power supply, clock, control, and I/O. The I/O port is further divided into General Purpose Input Output (GPIO) and function multiplex I/O (such as SPI/I2C/UART, etc.) in the usage mode.
Most MCU pins have more than one function. The internal structure of different pins is different and the functions are different. The actual function of the pin can be switched through different configurations. The main features of the General Purpose Input Output (GPIO) port are as follows:
* Programmable Interrupt: The interrupt trigger mode is configurable. Generally, there are five interrupt trigger modes as shown in the following figure:
![5 Interrupt Trigger Modes](figures/pin2.png)
* Input and output modes can be controlled.
* Output modes generally include Output push-pull, Output open-drain, Output pull-up, and Output pull-down. When the pin is in the output mode, the connected peripherals can be controlled by configuring the level of the pin output to be high or low.
* Input modes generally include: Input floating, Input pull-up, Input pull-down, and Analog. When the pin is in the input mode, the level state of the pin can be read, that is, high level or low level.
## Access PIN Device
The application accesses the GPIO through the PIN device management interface provided by RT-Thread. The related interfaces are as follows:
| Function | **Description** |
| ---------------- | ---------------------------------- |
| rt_pin_mode() | Set pin mode |
| rt_pin_write() | Set the pin level |
| rt_pin_read() | Read pin level |
| rt_pin_attach_irq() | Bind pin interrupt callback function |
| rt_pin_irq_enable() | Enable pin interrupt |
| rt_pin_detach_irq() | Detach pin interrupt callback function |
### Obtain Pin Number
The pin numbers provided by RT-Thread need to be distinguished from the chip pin numbers. They are not the same concept. The pin numbers are defined by the PIN device driver and are related to the specific chip. There are two ways to obtain the pin number: use the macro definition or view the PIN driver file.
#### Use Macro Definition
If you use the BSP in the `rt-thread/bsp/stm32` directory, you can use the following macro to obtain the pin number:
```c
GET_PIN(port, pin)
```
The sample code for the pin number corresponding to LED0 with pin number PF9 is as follows:
```c
#define LED0_PIN GET_PIN(F, 9)
```
#### View Driver Files
If you use a different BSP, you will need to check the PIN driver code `drv_gpio.c` file to confirm the pin number. There is an array in this file that holds the number information for each PIN pin, as shown below:
```c
static const rt_uint16_t pins[] =
{
__STM32_PIN_DEFAULT,
__STM32_PIN_DEFAULT,
__STM32_PIN(2, A, 15),
__STM32_PIN(3, B, 5),
__STM32_PIN(4, B, 8),
__STM32_PIN_DEFAULT,
__STM32_PIN_DEFAULT,
__STM32_PIN_DEFAULT,
__STM32_PIN(8, A, 14),
__STM32_PIN(9, B, 6),
... ...
}
```
Take `__STM32_PIN(2, A, 15)` as an example, 2 is the pin number used by RT-Thread, A is the port number, and 15 is the pin number, so the pin number corresponding to PA15 is 2.
### Set Pin Mode
Before the pin is used, you need to set the input or output mode first, and the following functions are used:
```c
void rt_pin_mode(rt_base_t pin, rt_base_t mode);
```
| Parameter | **Discription** |
| --------- | ------------------ |
| pin | Pin number |
| mode | Pin operation mode |
At present, the pin working mode supported by RT-Thread can take one of the five macro definition values as shown. The mode supported by the chip corresponding to each mode needs to refer to the specific implementation of the PIN device driver:
```c
#define PIN_MODE_OUTPUT 0x00 /* Output */
#define PIN_MODE_INPUT 0x01 /* Input */
#define PIN_MODE_INPUT_PULLUP 0x02 /* input Pull up */
#define PIN_MODE_INPUT_PULLDOWN 0x03 /* input Pull down */
#define PIN_MODE_OUTPUT_OD 0x04 /* output Open drain */
```
An example of use is as follows:
```c
#define BEEP_PIN_NUM 35 /* PB0 */
/* Buzzer pin is in output mode */
rt_pin_mode(BEEP_PIN_NUM, PIN_MODE_OUTPUT);
```
### Set The Pin Level
The function to set the pin output level is as follows:
```c
void rt_pin_write(rt_base_t pin, rt_base_t value);
```
| **Parameter** | Discription |
|----------|-------------------------|
| pin | Pin number |
| value | Level logic value, which can take one of two macro definition values: PIN_LOW means low level, or PIN_HIGH means high level |
Examples of use are as follows:
```c
#define BEEP_PIN_NUM 35 /* PB0 */
/* Beep's pin is in output mode */
rt_pin_mode(BEEP_PIN_NUM, PIN_MODE_OUTPUT);
/* Set low level */
rt_pin_write(BEEP_PIN_NUM, PIN_LOW);
```
### Read Pin Level
The functions to read the pin level are as follows:
```c
int rt_pin_read(rt_base_t pin);
```
| Parameter | Description |
| ---------- | ----------- |
| pin | Pin number |
| **return** | —— |
| PIN_LOW | Low level |
| PIN_HIGH | High level |
Examples of use are as follows:
```c
#define BEEP_PIN_NUM 35 /* PB0 */
int status;
/* Buzzer pin is in output mode */
rt_pin_mode(BEEP_PIN_NUM, PIN_MODE_OUTPUT);
/* Set low level */
rt_pin_write(BEEP_PIN_NUM, PIN_LOW);
status = rt_pin_read(BEEP_PIN_NUM);
```
### Bind Pin Interrupt Callback Function
To use the interrupt function of the pin, you can use the following function to configure a pin to some interrupt trigger mode and bind an interrupt callback function to the corresponding pin. When the pin interrupt occurs, the callback function will be executed. :
```c
rt_err_t rt_pin_attach_irq(rt_int32_t pin, rt_uint32_t mode,
void (*hdr)(void *args), void *args);
```
| Parameter | Description |
| ---------- | ------------------------------------------------------------ |
| pin | Pin number |
| mode | Interrupt trigger mode |
| hdr | Interrupt callback function. Users need to define this function |
| args | Interrupt the parameters of the callback function, set to RT_NULL when not needed |
| return | —— |
| RT_EOK | Binding succeeded |
| error code | Binding failed |
Interrupt trigger mode mode can take one of the following five macro definition values:
```c
#define PIN_IRQ_MODE_RISING 0x00 /* Rising edge trigger */
#define PIN_IRQ_MODE_FALLING 0x01 /* Falling edge trigger */
#define PIN_IRQ_MODE_RISING_FALLING 0x02 /* Edge trigger (triggered on both rising and falling edges)*/
#define PIN_IRQ_MODE_HIGH_LEVEL 0x03 /* High level trigger */
#define PIN_IRQ_MODE_LOW_LEVEL 0x04 /* Low level trigger */
```
Examples of use are as follows:
```c
#define KEY0_PIN_NUM 55 /* PD8 */
/* Interrupt callback function */
void beep_on(void *args)
{
rt_kprintf("turn on beep!\n");
rt_pin_write(BEEP_PIN_NUM, PIN_HIGH);
}
static void pin_beep_sample(void)
{
/* Button 0 pin is the input mode */
rt_pin_mode(KEY0_PIN_NUM, PIN_MODE_INPUT_PULLUP);
/* Bind interrupt, rising edge mode, callback function named beep_on */
rt_pin_attach_irq(KEY0_PIN_NUM, PIN_IRQ_MODE_FALLING, beep_on, RT_NULL);
}
```
### Enable Pin Interrupt
After binding the pin interrupt callback function, use the following function to enable pin interrupt:
```c
rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint32_t enabled);
```
| **Parameter** | **Description** |
|----------|----------------|
| pin | Pin number |
| enabled | Status, one of two values: PIN_IRQ_ENABLE, and PIN_IRQ_DISABLE |
| **return** | —— |
| RT_EOK | Enablement succeeded |
| error code | Enablement failed |
Examples of use are as follows:
```c
#define KEY0_PIN_NUM 55 /* PD8 */
/* Interrupt callback function */
void beep_on(void *args)
{
rt_kprintf("turn on beep!\n");
rt_pin_write(BEEP_PIN_NUM, PIN_HIGH);
}
static void pin_beep_sample(void)
{
/* Key 0 pin is the input mode */
rt_pin_mode(KEY0_PIN_NUM, PIN_MODE_INPUT_PULLUP);
/* Bind interrupt, rising edge mode, callback function named beep_on */
rt_pin_attach_irq(KEY0_PIN_NUM, PIN_IRQ_MODE_FALLING, beep_on, RT_NULL);
/* Enable interrupt */
rt_pin_irq_enable(KEY0_PIN_NUM, PIN_IRQ_ENABLE);
}
```
### Detach Pin Interrupt Callback Function
You can use the following function to detach the pin interrupt callback function:
```c
rt_err_t rt_pin_detach_irq(rt_int32_t pin);
```
| **Parameter** | **Description** |
| ------------- | -------------------- |
| pin | Pin number |
| **return** | —— |
| RT_EOK | Detachment succeeded |
| error code | Detachment failed |
After the pin detaches the interrupt callback function, the interrupt is not closed. You can also call the bind interrupt callback function to bind the other callback functions again.
```c
#define KEY0_PIN_NUM 55 /* PD8 */
/* Interrupt callback function */
void beep_on(void *args)
{
rt_kprintf("turn on beep!\n");
rt_pin_write(BEEP_PIN_NUM, PIN_HIGH);
}
static void pin_beep_sample(void)
{
/* Key 0 pin is the input mode */
rt_pin_mode(KEY0_PIN_NUM, PIN_MODE_INPUT_PULLUP);
/* Bind interrupt, rising edge mode, callback function named beep_on */
rt_pin_attach_irq(KEY0_PIN_NUM, PIN_IRQ_MODE_FALLING, beep_on, RT_NULL);
/* Enable interrupt */
rt_pin_irq_enable(KEY0_PIN_NUM, PIN_IRQ_ENABLE);
/* Detach interrupt callback function */
rt_pin_detach_irq(KEY0_PIN_NUM);
}
```
## PIN Device Usage Example
The following sample code is the pin device usage example. The main steps of the sample code are as follows:
1. Set the corresponding pin of the beep to the output mode and give a default low state.
2. Set the key 0 and button 1 corresponding to the input mode, then bind the interrupt callback function and enable the interrupt.
3. When the key 0 is pressed, the beep starts to sound, and when the key 1 is pressed, the beep stops.
```c
/*
* Program listing: This is a PIN device usage routine
* The routine exports the pin_beep_sample command to the control terminal
* Command call format:pin_beep_sample
* Program function: control the buzzer by controlling the level state of the corresponding pin of the buzzer by pressing the button
*/
#include <rtthread.h>
#include <rtdevice.h>
/* Pin number, determined by looking at the device driver file drv_gpio.c */
#ifndef BEEP_PIN_NUM
#define BEEP_PIN_NUM 35 /* PB0 */
#endif
#ifndef KEY0_PIN_NUM
#define KEY0_PIN_NUM 55 /* PD8 */
#endif
#ifndef KEY1_PIN_NUM
#define KEY1_PIN_NUM 56 /* PD9 */
#endif
void beep_on(void *args)
{
rt_kprintf("turn on beep!\n");
rt_pin_write(BEEP_PIN_NUM, PIN_HIGH);
}
void beep_off(void *args)
{
rt_kprintf("turn off beep!\n");
rt_pin_write(BEEP_PIN_NUM, PIN_LOW);
}
static void pin_beep_sample(void)
{
/* Beep pin is in output mode */
rt_pin_mode(BEEP_PIN_NUM, PIN_MODE_OUTPUT);
/* Default low level */
rt_pin_write(BEEP_PIN_NUM, PIN_LOW);
/* KEY 0 pin is the input mode */
rt_pin_mode(KEY0_PIN_NUM, PIN_MODE_INPUT_PULLUP);
/* Bind interrupt, falling edge mode, callback function named beep_on */
rt_pin_attach_irq(KEY0_PIN_NUM, PIN_IRQ_MODE_FALLING, beep_on, RT_NULL);
/* Enable interrupt */
rt_pin_irq_enable(KEY0_PIN_NUM, PIN_IRQ_ENABLE);
/* KEY 1 pin is input mode */
rt_pin_mode(KEY1_PIN_NUM, PIN_MODE_INPUT_PULLUP);
/* Binding interrupt, falling edge mode, callback function named beep_off */
rt_pin_attach_irq(KEY1_PIN_NUM, PIN_IRQ_MODE_FALLING, beep_off, RT_NULL);
/* Enable interrupt */
rt_pin_irq_enable(KEY1_PIN_NUM, PIN_IRQ_ENABLE);
}
/* Export to the msh command list */
MSH_CMD_EXPORT(pin_beep_sample, pin beep sample);
```
# PWM Device
## Introduction to PWM
PWM (Pulse Width Modulation) is a method of digitally encoding the level of an analog signal. The frequency of the square wave is used to encode the level of a specific analog signal by pulses of different frequencies. The output receives a series of pulses of equal magnitude and uses these pulses to replace the device with the desired waveform.
![PWM Schematic Diagram](figures/pwm-f.png)
Above is a simple schematic diagram of PWM. Assuming that the timer works in a up-counter mode. When the count value is less than the threshold, it outputs a level state, such as a high level. When the count value is greater than the threshold, it outputs the opposite, such as a low level. When the count value reaches the maximum value, the counter recounts from 0 and returns to the original level state. The ratio of the high-level duration (pulse width) to the cycle time is the duty cycle, ranging from 0 to 100%. The high level of the above picture is just half of the cycle time, so the duty cycle is 50%.
One of the common PWM control scenarios is to adjust the brightness of the light or screen. The brightness can be adjusted according to the duty cycle. The PWM adjusts the brightness not continuously, but constantly lights up and turns off the screen. When the light is turned on and off fast enough, the naked eye will always think that it is always bright. In the process of on and off, the longer the light is off, the lower the brightness of the screen to the naked eye. The longer the light is on, the less time is spent and the screen will be brighter.
![PWM Brightness Adjustment](figures/pwm-l.png)
## Access to PWM Devices
The application accesses the PWM device hardware through the PWM device management interface provided by RT-Thread. The related interfaces are as follows:
| **Function** | Description |
| ----------------- | ---------------------------------- |
| rt_device_find() | Find device handles based on the name of PWM device |
| rt_pwm_set() | Set PWM period and pulse width |
| rt_pwm_enable() | Enable PWM device |
| rt_pwm_disable() | Disable the PWM device |
### Find the PWM Device
The application obtains the device handle based on the name of PWM device, which in turn can operate the PWM device. The function is as follows:
```c
rt_device_t rt_device_find(const char* name);
```
| Parameter | Description |
| -------- | ---------------------------------- |
| name | Device |
| **Return** | —— |
| Device handle | Found the corresponding device, will return the corresponding device handle |
| RT_NULL | Device not found |
In general, the name of the PWM device registered to the system is pwm0, pwm1, etc. The usage examples are as follows:
```c
#define PWM_DEV_NAME "pwm3" /* name of PWM device */
struct rt_device_pwm *pwm_dev; /* PWM device handle */
/* Search the device */
pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
```
### Set PWM Period and Pulse Width
Set the PWM period and duty cycle by using the following function:
```c
rt_err_t rt_pwm_set(struct rt_device_pwm *device,
int channel,
rt_uint32_t period,
rt_uint32_t pulse);
```
| Parameter | Description |
| ---------- | ----------------- |
| device | PWM device handle |
| channel | PWM channel |
| period | PWM period (ns) |
| pulse | PWM pulse width time (ns) |
| **Return** | —— |
| RT_EOK | successful |
| -RT_EIO | device is null |
| -RT_ENOSYS | Device operation method is null |
| Other Errors | Execute failed |
The output frequency of the PWM is determined by the period. For example, the time of a period is 0.5ms (milliseconds), the period value is 500000ns (nanoseconds), the output frequency is 2KHz, the duty cycle is `pulse / period`, and the pulse value cannot exceed period.
An example of use is as follows:
```c
#define PWM_DEV_NAME "pwm3" /* name of PWM device */
#define PWM_DEV_CHANNEL 4 /* PWM channel */
struct rt_device_pwm *pwm_dev; /* PWM device handle */
rt_uint32_t period, pulse;
period = 500000; /* The period is 0.5ms, the unit is nanoseconds */
pulse = 0; /* PWM pulse width value, the unit is nanoseconds */
/* Search the device */
pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
/* Set the PWM period and pulse width */
rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
```
### Enable the PWM Device
After setting the PWM period and pulse width, you can enable the PWM device by the following function:
```c
rt_err_t rt_pwm_enable(struct rt_device_pwm *device, int channel);
```
| Parameter | Description |
| ---------- | ------------------------------- |
| device | PWM device handle |
| channel | PWM channel |
| **Return** | —— |
| RT_EOK | Enable device successful |
| -RT_ENOSYS | Device operation method is null |
| Other Errors | Enable device failed |
An example of use is as follows:
```c
#define PWM_DEV_NAME "pwm3" /* name of PWM device */
#define PWM_DEV_CHANNEL 4 /* PWM channel */
struct rt_device_pwm *pwm_dev; /* PWM device handle */
rt_uint32_t period, pulse;
period = 500000; /* The period is 0.5ms, the unit is nanoseconds */
pulse = 0; /* PWM pulse width value, the unit is nanoseconds */
/* Search the device */
pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
/* Set the PWM period and pulse width */
rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
/* Enable the device */
rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
```
### Disable the PWM device Channel
Use the following function to turn off the corresponding channel of the PWM device.
```c
rt_err_t rt_pwm_disable(struct rt_device_pwm *device, int channel);
```
| **Parameter** | Description |
| ---------- | ------------------------------- |
| device | PWM device handle |
| channel | PWM channel |
| **Return** | —— |
| RT_EOK | Turn off device successful |
| -RT_EIO | Device handle is null |
| Other Errors | Turn off device failed |
An example of use is as follows:
```c
#define PWM_DEV_NAME "pwm3" /* name of PWM device */
#define PWM_DEV_CHANNEL 4 /* PWM channel */
struct rt_device_pwm *pwm_dev; /* PWM device handle */
rt_uint32_t period, pulse;
period = 500000; /* The period is 0.5ms, the unit is nanoseconds */
pulse = 0; /* PWM pulse width value, the unit is nanoseconds */
/* Search the device */
pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
/* Set the PWM period and pulse width */
rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
/* Enable the device */
rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
/* Turn off the device channel */
rt_pwm_disable(pwm_dev,PWM_DEV_CHANNEL);
```
## FinSH Command
To set the period and duty cycle of a channel of a PWM device, use the command `pwm_set pwm1 1 500000 5000`. The first parameter is the command, the second parameter is the PWM device name, the third parameter is the PWM channel, and the fourth parameter is PWM period(ns), the fifth parameter is the pulse width (ns).
```c
msh />pwm_set pwm1 1 500000 5000
msh />
```
To enable a channel of the PWM device, use the command`pwm_enable pwm1 1`. The first parameter is the command, the second parameter is the PWM device name, and the third parameter is the PWM channel.
```c
msh />pwm_enable pwm1 1
msh />
```
To disable a channel of the PWM device, use the command `pwm_disable pwm1 1`. The first parameter is the command, the second parameter is the PWM device name, and the third parameter is the PWM channel.
```c
msh />pwm_disable pwm1 1
msh />
```
## PWM Device Usage Example
The following sample code is a PWM device usage sample . The main steps of the sample code are as follows:
1. Find the PWM device to get the device handle.
2. Set the PWM period and pulse width.
3. Enable the PWM device.
4. The pulse width is modified every 50 milliseconds in the while loop.
5. Connect the PWM channel to a LED, and you can see that the LED changes from dark to bright gradually, and then from bright to dark.
```c
/*
* Program list: This is PWM device usage example
* The routine exports the pwm_led_sample command to the control terminal
* Format for Command: pwm_led_sample
* Program function: By controlling the brightness of the LED light through the PWM device,
* you can see that the LED changes from dark to bright gradually, then from bright to dark.
*/
#include <rtthread.h>
#include <rtdevice.h>
#define PWM_DEV_NAME "pwm3" /* PWM device name */
#define PWM_DEV_CHANNEL 4 /* PWM channel */
struct rt_device_pwm *pwm_dev; /* PWM device handle */
static int pwm_led_sample(int argc, char *argv[])
{
rt_uint32_t period, pulse, dir;
period = 500000; /* The period is 0.5ms, the unit is nanoseconds */
dir = 1; /* Increase or decrease direction of PWM pulse width value */
pulse = 0; /* PWM pulse width value, the unit is nanoseconds*/
/* Set LED pin mode to output */
rt_pin_mode(LED_PIN_NUM, PIN_MODE_OUTPUT);
/* Set high LED pin mode */
rt_pin_write(LED_PIN_NUM, PIN_HIGH);
/* Search the Device */
pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
if (pwm_dev == RT_NULL)
{
rt_kprintf("pwm sample run failed! can't find %s device!\n", PWM_DEV_NAME);
return RT_ERROR;
}
/* Set PWM period and pulse width defaults */
rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
/* Enable device */
rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
while (1)
{
rt_thread_mdelay(50);
if (dir)
{
pulse += 5000; /* Increase 5000ns each time from 0 */
}
else
{
pulse -= 5000; /* 5000ns reduction from the maximum */
}
if (pulse >= period)
{
dir = 0;
}
if (0 == pulse)
{
dir = 1;
}
/* Set the PWM period and pulse width */
rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
}
}
/* Export to the msh command list */
MSH_CMD_EXPORT(pwm_led_sample, pwm sample);
```
# RTC Device
## Introduction of RTC
The RTC (Real-Time Clock) provides accurate real-time clock time, which can be used to generate information such as year, month, day, hour, minute, and second. At present, most real-time clock chips use a higher precision crystal oscillator as a clock source. In order to work when the main power supply is powered down, some clock chips will be powered by a battery to keep the time information valid.
The RT-Thread RTC device provides the basic services for the operating system's time system. In the face of more and more IoT scenarios, RTC has become the standard configuration of the product, and even in the secure transmission process such as SSL, RTC has become an indispensable part.
## Access RTC Devices
The application accesses the RTC hardware through the RTC device management interface, and the relevant interfaces are as follows:
| **Function** | Description |
| ------------- | ---------------------------------- |
| set_date() | Set date, year, month, day |
| set_time() | Set time, hour, minute, second |
| time() | Obtain current time |
### Set Date
Set the current date value of the RTC device by the following functions:
```c
rt_err_t set_date(rt_uint32_t year, rt_uint32_t month, rt_uint32_t day)
```
| **Parameter** | **Description** |
| -------- | ---------------------------------- |
|year |The year to be set to take effect|
|month |The month to be set to take effect|
|day | The date to be set to take effect |
| **return** | —— |
| RT_EOK | Set-up succeeded |
| -RT_ERROR | Set-up failed, no rtc device found |
| other error code | Set-up failed |
An example of use is as follows:
```c
/* Set the date to December 3, 2018 */
set_date(2018, 12, 3);
```
### Set Time
Set the current time value of the RTC device by the following function:
```c
rt_err_t set_time(rt_uint32_t hour, rt_uint32_t minute, rt_uint32_t second)
```
| **Parameter** | **Description** |
| ---------- | ------------------------------- |
|hour |The hour to be set to take effect|
|minute |The minute to be set to take effect|
|second |The second to be set to take effect|
| **return** | —— |
| RT_EOK | Set-up succeeded |
| -RT_ERROR | Set-up failed, no rtc device found |
| other error code | Set-up failed |
An example of use is as follows:
```c
/* Set the time to 11:15:50 */
set_time(11, 15, 50);
```
### Obtain Current Time
Obtain time using the time API in the C standard library:
```c
time_t time(time_t *t)
```
| **Parameter** | **Description** |
| ---------- | ------------------------------- |
|t |Time data pointer |
| **return** | —— |
| Current time value | |
Examples of use are as follows:
```c
time_t now; /* Save the current time value obtained */
/* Obtain Time */
now = time(RT_NULL);
/* Printout time information */
rt_kprintf("%s\n", ctime(&now));
```
>Currently only one RTC device is allowed in the system and the name is `"rtc"`.
## Functional Configuration
### Enable Soft RTC (Software Emulation RTC)
You can use the function of enabling RTC software emulation, which is ideal for products that do not require high time precision and have no hardware RTC. The configuration options of menuconfig are as follows:
```c
RT-Thread Components
Device Drivers:
-*- Using RTC device drivers /* Use RTC device driver */
[ ] Using software simulation RTC device /* Use software simulation RTC device */
```
### Enable NTP Time Automatic Synchronization
If the RT-Thread is connected to the Internet, you can enable automatic NTP time synchronization to synchronize local time periodically.
First open the NTP function in menuconfig as follows:
```c
RT-Thread online packages
IoT - internet of things
netutils: Networking utilities for RT-Thread:
[*] Enable NTP(Network Time Protocol) client
```
After the NTP is turned on, the RTC's automatic synchronization function will be automatically turned on, and the synchronization period and the delay time of the first synchronization can also be set:
```c
RT-Thread Components
Device Drivers:
-*- Using RTC device drivers /* Use RTC device driver */
[ ] Using software simulation RTC device /* Use software simulation RTC device */
[*] Using NTP auto sync RTC time /* Automatically synchronize RTC time with NTP */
(30) NTP first sync delay time(second) for network connect /* The delay for performing NTP time synchronization for the first time. The purpose of the delay is to reserve a certain amount of time for the network connection and try to increase the success rate of the first NTP time synchronization. The default time is 30S; */
(3600) NTP auto sync period(second) /* NTP The synchronization period is automatically synchronized in seconds, and the default period is one hour (ie 3600S). */
```
## FinSH Command
Enter `date` to view the current time.
```c
msh />date
Fri Feb 16 01:11:56 2018
msh />
```
Also use the `date` command, after the command, enter `year` `month` `date` `hour ` ` minute ` ` second ` (between spaces, 24H system), and set the current time to 2018-02-16 01:15:30. The approximate effect is as follows:
```c
msh />date 2018 02 16 01 15 30
msh />
```
## RTC Device Usage Examples
For the specific usage of the RTC device, refer to the following example code. First, set the year, month, date, hour, minute and second information, and then delay the data for 3 seconds to get the current time information.
```c
/*
* Program listing: This is an RTC device usage routine
* The routine exports the rtc_sample command to the control terminal
* Command call format:rtc_sample
* Program function: Set the date and time of the RTC device. After a delay, obtain the current time and print the display.
*/
#include <rtthread.h>
#include <rtdevice.h>
static int rtc_sample(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
time_t now;
/* Set date */
ret = set_date(2018, 12, 3);
if (ret != RT_EOK)
{
rt_kprintf("set RTC date failed\n");
return ret;
}
/* Set time */
ret = set_time(11, 15, 50);
if (ret != RT_EOK)
{
rt_kprintf("set RTC time failed\n");
return ret;
}
/* Delay 3 seconds */
rt_thread_mdelay(3000);
/* Obtain Time */
now = time(RT_NULL);
rt_kprintf("%s\n", ctime(&now));
return ret;
}
/* Export to the msh command list */
MSH_CMD_EXPORT(rtc_sample, rtc sample);
```
# Sensor Device
## Introduction
Sensor is an important part of the Internet of Things, and "Sensor to the Internet of Things" is equivalent to "eyes to humans". Without eyes, human beings can not see the vast world of flowers. The same is true for the Internet of Things.
Nowadays, with the development of Internet of Things, a large number of Sensors have been developed for developers to choose, such as Accelerometer, Magnetometer, Gyroscope, Barometer/pressure, Humidometer and so on. These sensors, manufactured by the world's leading semiconductor manufacturers, have increased market selectivity and made application development more difficult. Because different sensor manufacturers and sensors need their own unique drivers to run, so when developing applications, they need to adapt to different sensors, which naturally increases the difficulty of development. In order to reduce the difficulty of application development and increase the reusability of sensor driver, we designed a Sensor device.
The function of Sensor device is to provide a unified operation interface for the upper layer and improve the reusability of the upper code.
### Characteristics of Sensor Device
- **Interface**: Standard device interface (open/close/read/control)
- **Work mode**: support polling, interruption, FIFO three modes
- **Power mode**: support four modes: power failure, common, low power consumption and high power consumption
## Access Sensor Device
The application accesses the sensor device through the I/O device management interface provided by RT-Thread. The related interfaces are as follows:
| Functions | Description |
| --------------------------- | ------------------------------------------------------------ |
| rt_device_find() | Finding device handles based on device name of sensor device |
| rt_device_open() | open sensor device |
| rt_device_read() | read data |
| rt_device_control() | control sensor device |
| rt_device_set_rx_indicate() | setting reveive callback fuction |
| rt_device_close() | close sensor device |
### Find Sensor Device
The application obtains the device handle according to the name of the sensor device, and then can operate the sensor device. The function of finding the device is as follows:
```c
rt_device_t rt_device_find(const char* name);
```
| **Parameter** | **Description** |
| ------------- | ------------------------------------------------------------ |
| name | sensor device name |
| **return** | —— |
| handle | Finding the corresponding device returns the corresponding device handle |
| RT_NULL | No corresponding device object was found |
The use example is as follows:
```c
#define SENSOR_DEVICE_NAME "acce_st" /* sensor device name */
static rt_device_t sensor_dev; /* sensor device handle */
/* Find the sensor device according to the device name and get the device handle */
sensor_dev = rt_device_find(SENSOR_DEVICE_NAME);
```
### Open Sensor Device
Through the device handle, the application can open and close the device. When the device is opened, it will check whether the device has been initialized or not. If it is not initialized, it will call the initialization interface initialization device by default. Open the device through the following functions:
```c
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
```
| **Parameter** | **Description** |
| ------------- | ------------------------------------------------------------ |
| dev | device handle |
| oflags | open mode flag |
| **Return** | —— |
| RT_EOK | open success |
| -RT_EBUSY | If the RT_DEVICE_FLAG_STANDALONE parameter is included in the parameter specified at the time of device registration, the device will not be allowed to open repeatedly. |
| -RT_EINVAL | Unsupported open mode |
| other err | open failed |
The oflags parameter supports the following parameters:
```c
#define RT_DEVICE_FLAG_RDONLY 0x001 /* Read-only mode for standard device, polling mode for corresponding sensors */
#define RT_DEVICE_FLAG_INT_RX 0x100 /* Interrupt Receiving Mode */
#define RT_DEVICE_FLAG_FIFO_RX 0x200 /* FIFO receiving mode */
```
There are three modes of receiving and sending sensor data: interrupt mode, polling mode and FIFO mode. When using these three modes, **only one of them can be chosen**. If the sensor's open parameter oflags does not specify the use of interrupt mode or FIFO mode, polling mode is used by default.
FIFO ,means first Input first output. FIFO transmission mode needs sensor hardware support, data is stored in hardware FIFO, read multiple data at a time, which saves CPU resources to do other operations. Very useful in low power mode
If the sensor uses FIFO receiving mode, the value of oflags is RT_DEVICE_FLAG_FIFO_RX.
An example of turning on sensor devices in polling mode is as follows:
```c
#define SAMPLE_SENSOR_NAME "acce_st" /* sensor device name */
int main(void)
{
rt_device_t dev;
struct rt_sensor_data data;
/* find sensor device */
dev = rt_device_find(SAMPLE_SENSOR_NAME);
/* Open sensor devices in read-only and polling mode */
rt_device_open(dev, RT_DEVICE_FLAG_RDWR);
if (rt_device_read(dev, 0, &data, 1) == 1)
{
rt_kprintf("acce: x:%5d, y:%5d, z:%5d, timestamp:%5d\n", data.data.acce.x, data.data.acce.y, data.data.acce.z, data.timestamp);
}
rt_device_close(dev);
return RT_EOK;
}
```
### Control Sensor Device
By command control word, the application program can configure the sensor device through the following functions:
```c
rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
```
| **Parameter** | **Description** |
| ------------- | ------------------------------------------------------------ |
| dev | device handle |
| cmd | command control word, see below for more details. |
| arg | the parameters of command control word, see below for more details. |
| **Return** | —— |
| RT_EOK | success |
| -RT_ENOSYS | failed,device is NULL |
| other err | failed |
`cmd` currently supports the following command control words:
```c
#define RT_SEN_CTRL_GET_ID (0) /* read device ID */
#define RT_SEN_CTRL_GET_INFO (1) /* get device information */
#define RT_SEN_CTRL_SET_RANGE (2) /* Setting the measuring range of the sensor */
#define RT_SEN_CTRL_SET_ODR (3) /* Setting the Output Rate of Sensor Data,unit is HZ */
#define RT_SEN_CTRL_SET_MODE (4) /* Setting up working mode */
#define RT_SEN_CTRL_SET_POWER (5) /* Setting up power mode */
#define RT_SEN_CTRL_SELF_TEST (6) /* selfcheck */
```
#### Get device information
```c
struct rt_sensor_info info;
rt_device_control(dev, RT_SEN_CTRL_GET_INFO, &info);
LOG_I("vendor :%d", info.vendor);
LOG_I("model :%s", info.model);
LOG_I("unit :%d", info.unit);
LOG_I("intf_type :%d", info.intf_type);
LOG_I("period_min:%d", info.period_min);
```
#### Read Device ID
```c
rt_uint8_t reg = 0xFF;
rt_device_control(dev, RT_SEN_CTRL_GET_ID, &reg);
LOG_I("device id: 0x%x!", reg);
```
#### Setting the measuring range of the sensor
The unit that sets the measuring range of the sensor is the unit that is provided when the device is registered.
```c
rt_device_control(dev, RT_SEN_CTRL_SET_RANGE, (void *)1000);
```
#### Setting the Output Rate of Sensor Data
Set the output rate to 100HZ and call the following interface.
```c
rt_device_control(dev, RT_SEN_CTRL_SET_ODR, (void *)100);
```
#### Setting up working mode
```c
/* Set the working mode to polling mode */
rt_device_control(dev, RT_SEN_CTRL_SET_MODE, (void *)RT_SEN_MODE_POLLING);
/* Set working mode to interrupt mode */
rt_device_control(dev, RT_SEN_CTRL_SET_MODE, (void *)RT_SEN_MODE_INT);
/* Set working mode to FIFO mode */
rt_device_control(dev, RT_SEN_CTRL_SET_MODE, (void *)RT_SEN_MODE_FIFO);
```
#### Setting up power mode
```c
/* Set power mode to power-off mode */
rt_device_control(dev, RT_SEN_CTRL_SET_POWER, (void *)RT_SEN_POWER_DOWN);
/* Set power mode to normal mode */
rt_device_control(dev, RT_SEN_CTRL_SET_POWER, (void *)RT_SEN_POWER_NORMAL);
/* Setting Power Mode to Low Power Consumption Mode */
rt_device_control(dev, RT_SEN_CTRL_SET_POWER, (void *)RT_SEN_POWER_LOW);
/* Setting Power Mode to High Performance Mode */
rt_device_control(dev, RT_SEN_CTRL_SET_POWER, (void *)RT_SEN_POWER_HIGH);
```
#### Device self-inspection
```c
int test_res;
/* Control equipment self-check and return the results. Returning RT_EOK indicates success of self-check and other values indicate failure of self-check. */
rt_device_control(dev, RT_SEN_CTRL_SELF_TEST, &test_res);
```
### Setting Reveive Callback Fuction
Data reception instructions can be set by following functions. When the sensor receives data, it notifies the upper application thread that data arrives:
```c
rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));
```
| **Parameter** | **Description** |
| ------------- | --------------------------------------------- |
| dev | device handle |
| rx_ind | Callback function pointer |
| dev | device handle(parameter of callback function) |
| size | buffer size(parameter of callback function) |
| **Return** | —— |
| RT_EOK | Successful setup |
The callback function of the function is provided by the user. If the sensor is opened in interrupt mode, when the sensor receives data and interrupts, the callback function will be called, and the data size of the buffer will be placed in the `size` parameter, and the sensor device handle will be placed in the `dev` parameter for users to obtain.
Generally, receiving callback function can send a semaphore or event to inform sensor data processing thread that data arrives. The use example is as follows:
```c
#define SAMPLE_SENSOR_NAME "acce_st" /* sensor device name */
static rt_device_t dev; /* sensoe device handle*/
static struct rt_semaphore rx_sem; /* The semaphore used to receive messages */
/* Callback function for receiving data */
static rt_err_t sensor_input(rt_device_t dev, rt_size_t size)
{
/* When the sensor receives the data, it generates an interrupt, calls the callback function, and sends the semphore . */
rt_sem_release(&rx_sem);
return RT_EOK;
}
static int sensor_sample(int argc, char *argv[])
{
dev = rt_device_find(SAMPLE_SENSOR_NAME);
/* Open Sensor Device in Interrupt Receive and Poll Send Mode */
rt_device_open(dev, RT_DEVICE_FLAG_INT_RX);
/* init semphore */
rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
/* setting reveive callback function */
rt_device_set_rx_indicate(dev, sensor_input);
}
```
### Read Data of Sensor Device
The following functions can be called to read the data received by the sensor:
```c
rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
```
| **Parameter** | **Description** |
| ---------------------- | ------------------------------------------------------------ |
| dev | device handle |
| pos | Read data offset, sensor does not use this parameter |
| buffer | Buffer pointer, read data will be saved in the buffer |
| size | Size of read data |
| **Return** | —— |
| Real size of read data | Returns the number of read data |
| 0 | The errno of the current thread needs to be read to determine the error status |
The sensor uses the interrupt receiving mode and cooperates with the receiving callback function as follows:
```c
static rt_device_t dev; /* sensor device handle */
static struct rt_semaphore rx_sem; /* The semaphore used to receive messages */
/* Threads receiving data */
static void sensor_irq_rx_entry(void *parameter)
{
rt_device_t dev = parameter;
struct rt_sensor_data data;
rt_size_t res;
while (1)
{
rt_sem_take(rx_sem, RT_WAITING_FOREVER);
res = rt_device_read(dev, 0, &data, 1);
if (res == 1)
{
sensor_show_data(dev, &data);
}
}
}
```
The sensor uses FIFO receiving mode and cooperates with receiving callback function as follows:
```c
static rt_sem_t sensor_rx_sem = RT_NULL;
rt_err_t rx_cb(rt_device_t dev, rt_size_t size)
{
rt_sem_release(sensor_rx_sem);
return 0;
}
static void sensor_fifo_rx_entry(void *parameter)
{
rt_device_t dev = parameter;
struct rt_sensor_data data;
rt_size_t res, i;
data = rt_malloc(sizeof(struct rt_sensor_data) * 32);
while (1)
{
rt_sem_take(sensor_rx_sem, RT_WAITING_FOREVER);
res = rt_device_read(dev, 0, data, 32);
for (i = 0; i < res; i++)
{
sensor_show_data(dev, &data[i]);
}
}
}
int main(void)
{
static rt_thread_t tid1 = RT_NULL;
rt_device_t dev;
struct rt_sensor_data data;
sensor_rx_sem = rt_sem_create("sen_rx_sem", 0, RT_IPC_FLAG_FIFO);
tid1 = rt_thread_create("sen_rx_thread",
sensor_fifo_rx_entry, dev,
1024,
15, 5);
if (tid1 != RT_NULL)
rt_thread_startup(tid1);
dev = rt_device_find("acce_st");
rt_device_set_rx_indicate(dev, rx_cb);
rt_device_open(dev, RT_SEN_FLAG_FIFO);
return RT_EOK;
}
```
### Close Sensor Device
When the application completes the sensor operation, the sensor device can be closed by the following functions:
```c
rt_err_t rt_device_close(rt_device_t dev);
```
| **Parameter** | **Description** |
| ------------- | ------------------------------------------------------------ |
| dev | device handle |
| **Return** | —— |
| RT_EOK | The equipment was closed successfully. |
| -RT_ERROR | The device has been completely shut down and cannot be closed repeatedly. |
| other err | failed to close th device |
Closing the device interface and opening the device interface should be used in pairs, opening the primary device should close the primary device, so that the device will be completely closed, otherwise the device is still in an open state.
## Example Code for Sensor Device
The specific use of sensor devices can be referred to the following sample code, the main steps of the sample code are as follows:
1. Find the sensor device first and get the device handle.
2. Open the sensor device by polling.
3. Read the data five times in a row and print it out.
4. Close the sensor device.
This sample code is not limited to a specific BSP. According to the BSP registered sensor device, input different dev_name to run.
```c
/*
* Program List: This is a routine for sensor devices
* The routine exports the sensor_sample command to the control terminal
* Command Call Format:sensor_sample dev_name
* Command Interpretation: The second parameter of the command is the name of the sensor device to be used.
* Program function: Open the corresponding sensor, and then read the data five times in a row and print it out.
*/
#include "sensor.h"
static void sensor_show_data(rt_size_t num, rt_sensor_t sensor, struct rt_sensor_data *sensor_data)
{
switch (sensor->info.type)
{
case RT_SENSOR_CLASS_ACCE:
rt_kprintf("num:%3d, x:%5d, y:%5d, z:%5d, timestamp:%5d\n", num, sensor_data->data.acce.x, sensor_data->data.acce.y, sensor_data->data.acce.z, sensor_data->timestamp);
break;
case RT_SENSOR_CLASS_GYRO:
rt_kprintf("num:%3d, x:%8d, y:%8d, z:%8d, timestamp:%5d\n", num, sensor_data->data.gyro.x, sensor_data->data.gyro.y, sensor_data->data.gyro.z, sensor_data->timestamp);
break;
case RT_SENSOR_CLASS_MAG:
rt_kprintf("num:%3d, x:%5d, y:%5d, z:%5d, timestamp:%5d\n", num, sensor_data->data.mag.x, sensor_data->data.mag.y, sensor_data->data.mag.z, sensor_data->timestamp);
break;
case RT_SENSOR_CLASS_HUMI:
rt_kprintf("num:%3d, humi:%3d.%d%%, timestamp:%5d\n", num, sensor_data->data.humi / 10, sensor_data->data.humi % 10, sensor_data->timestamp);
break;
case RT_SENSOR_CLASS_TEMP:
rt_kprintf("num:%3d, temp:%3d.%dC, timestamp:%5d\n", num, sensor_data->data.temp / 10, sensor_data->data.temp % 10, sensor_data->timestamp);
break;
case RT_SENSOR_CLASS_BARO:
rt_kprintf("num:%3d, press:%5d, timestamp:%5d\n", num, sensor_data->data.baro, sensor_data->timestamp);
break;
case RT_SENSOR_CLASS_STEP:
rt_kprintf("num:%3d, step:%5d, timestamp:%5d\n", num, sensor_data->data.step, sensor_data->timestamp);
break;
default:
break;
}
}
static void sensor_sample(int argc, char **argv)
{
rt_device_t dev = RT_NULL;
struct rt_sensor_data data;
rt_size_t res, i;
/* Finding Sensor Devices in the System */
dev = rt_device_find(argv[1]);
if (dev == RT_NULL)
{
rt_kprintf("Can't find device:%s\n", argv[1]);
return;
}
/* Open sensor devices in polling mode */
if (rt_device_open(dev, RT_DEVICE_FLAG_RDWR) != RT_EOK)
{
rt_kprintf("open device failed!");
return;
}
for (i = 0; i < 5; i++)
{
/* Read a data from a sensor */
res = rt_device_read(dev, 0, &data, 1);
if (res != 1)
{
rt_kprintf("read data failed!size is %d", res);
}
else
{
sensor_show_data(i, (rt_sensor_t)dev, &data);
}
rt_thread_mdelay(100);
}
/* Close the sensor device */
rt_device_close(dev);
}
MSH_CMD_EXPORT(sensor_sample, sensor device sample);
```
此差异已折叠。
此差异已折叠。
# WATCHDOG Device
## An Introduction to WATCHDOG
The hardware watchdog timer is a timer whose timing output is connected to the reset terminal of the circuit. In a productized embedded system, in order to automatically reset the system under abnormal conditions, it generally needs a watchdog.
When the watchdog was started, the counter starts counting automatically. If it is not reset counter value before the counter overflows, the counter overflow will generate a reset signal to the CPU to restart the system. When the system is running normally, it is necessary to clear the watchdog counter within the time interval allowed by the watchdog (commonly known as "feeding the dog"), and the reset signal will not be generated. If the program can "feed the dog" on time,the system does not go wrong,otherwise the system will reset.
In general, users can feed the dog in the idlehook function and key function of RT-Thread.
## Access to the WATCHDOG Device
The application accesses the watchdog hardware through the I/O device management interface provided by RT-Thread. The related interfaces are as follows:
| **Function** | **Description** |
| ---------------- | ---------------------------------- |
| rt_device_find() | Find the device handle based on the device name of the watchdog device |
| rt_device_init() | Initialize the watchdog device |
| rt_device_control() |Control the watchdog device |
| rt_device_close() | Close the watchdog device |
### Find the Watchdog Device
The application obtains the device handle based on the watchdog device's name, and then it can operate the watchdog device. The function for finding a device is as follows:
```c
rt_device_t rt_device_find(const char* name);
```
| **Function** | **Description** |
| -------- | ---------------------------------- |
| name | the name of the watchdog device |
| **return** | —— |
| device handle | finding the corresponding device and then return to the corresponding device handle |
| RT_NULL | no corresponding device object found |
An usage example is as follows:
```c
#define IWDG_DEVICE_NAME "iwg" /* the name of the watchdog device */
static rt_device_t wdg_dev; /* device handle of the watchdog */
/* find the watchdog device based on the device's name and obtain the device handle */
wdg_dev = rt_device_find(IWDG_DEVICE_NAME);
```
### Initialize the Watchdog Device
The watchdog device need to be initialized before using, which can be done by the following function:
```c
rt_err_t rt_device_init(rt_device_t dev);
```
| **Function** | **Description** |
| ---------- | ------------------------------- |
| dev | handle of the watchdog device |
| **return** | —— |
| RT_EOK | the device succeeded initializing |
| -RT_ENOSYS | initialization failed, the watchdog device driver initialization function is empty |
| other error code | the device failed to open |
An example is as follows:
```c
#define IWDG_DEVICE_NAME "iwg" /* the name of the watchdog device */
static rt_device_t wdg_dev; /* handle of the watchdog device */
/* find the watchdog device based on the device's name and obtain the device handle */
wdg_dev = rt_device_find(IWDG_DEVICE_NAME);
/* initialize the device */
rt_device_init(wdg_dev);
```
### Control the Watchdog Device
The application can configure the watchdog device using the command control word, which can be done by the following function:
```c
rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
```
| **Function** | **Description** |
| ---------------- | ---------------------------------- |
| dev | handle of the watchdog device |
| cmd | the command word |
| arg | controlled parameter |
| **return** | —— |
| RT_EOK | function executed successfully |
| -RT_ENOSYS | execution failed, the dev is empty |
| other error code | execution failed |
The command control word `'cmd'` can take the following macro definition values:
```c
#define RT_DEVICE_CTRL_WDT_GET_TIMEOUT (1) /* get the overflow time */
#define RT_DEVICE_CTRL_WDT_SET_TIMEOUT (2) /* set the overflow time */
#define RT_DEVICE_CTRL_WDT_GET_TIMELEFT (3) /* get the remaining time */
#define RT_DEVICE_CTRL_WDT_KEEPALIVE (4) /* feed the dog */
#define RT_DEVICE_CTRL_WDT_START (5) /* start the watchdog */
#define RT_DEVICE_CTRL_WDT_STOP (6) /* stop the watchdog */
```
An example of setting the overflow time of the watchdog is as follows:
```c
#define IWDG_DEVICE_NAME "iwg" /* the name of the watchdog device */
rt_uint32_t timeout = 1000; /* the overflow time */
static rt_device_t wdg_dev; /* handle of the watchdog device */
/* find the watchdog device based on the device's name and obtain the device handle */
wdg_dev = rt_device_find(IWDG_DEVICE_NAME);
/* initialize the device */
rt_device_init(wdg_dev);
/* set the overflow time of the watch dog */
rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, (void *)timeout);
/* set idle-hook function */
rt_thread_idle_sethook(idle_hook);
```
An example of feeding a dog in an idle thread hook function is as follows:
```c
static void idle_hook(void)
{
/* Feed the dog in the callback function of the idle thread */
rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_KEEPALIVE, NULL);
}
```
### Close the Watchdog Device
When the application completes the operation of the watchdog, it can close the watchdog device:
```c
rt_err_t rt_device_close(rt_device_t dev);
```
| **Function** | **Description** |
| ---------- | ---------------------------------- |
| dev | handle of the watchdog device |
| **return** | —— |
| RT_EOK | close the device successfully |
| -RT_ERROR | The device has been completely shut down and cannot be closed repeatedly |
| other error code | fail to close the device |
Closing the device interface and opening the device interface need to match each other. When you open the device, you need to close the device once correspondingly, so that the device will be completely shut down, otherwise the device will remain unclosed.
## Watchdog Device usage example
The specific use of the watchdog device can be referred to the following sample code. The main steps of the sample code are as follows:
1. First find the device handle based on the device name "iwg".
2. Set the overflow time of the watchdog after initializing the device.
3. Set the idle thread callback function.
4. This callback function will run and feed the dog when the system executes idle threads.
```c
/*
* Program list: This is an independent watchdog device usage routine
* The routine exports the iwdg_sample command to the control terminal
* Command call format: iwdg_sample iwg
* Command explanation: The second parameter of the command is the name of the watchdog device to be used. If it is empty, you can use the default watchdog device of the routine.
* Program function: The program finds the watchdog device through the device's name, and then initializes the device and sets the overflow time of the watchdog device.
* Then set the idle thread callback function, which will feed the dog in the idle callback function.
*/
#include <rtthread.h>
#include <rtdevice.h>
#define IWDG_DEVICE_NAME "iwg" /* the name of the watchdog device */
static rt_device_t wdg_dev; /* handle of the watchdog device */
static void idle_hook(void)
{
/* feed the dog in the callback function */
rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_KEEPALIVE, NULL);
rt_kprintf("feed the dog!\n ");
}
static int iwdg_sample(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
rt_uint32_t timeout = 1000; /* the overflow time */
char device_name[RT_NAME_MAX];
/* Determine if the command-line parameter is given the device name */
if (argc == 2)
{
rt_strncpy(device_name, argv[1], RT_NAME_MAX);
}
else
{
rt_strncpy(device_name, IWDG_DEVICE_NAME, RT_NAME_MAX);
}
/* find the watchdog device based on the device's name and obtain the device handle */
wdg_dev = rt_device_find(device_name);
if (!wdg_dev)
{
rt_kprintf("find %s failed!\n", device_name);
return RT_ERROR;
}
/* initialize the device */
ret = rt_device_init(wdg_dev);
if (ret != RT_EOK)
{
rt_kprintf("initialize %s failed!\n", device_name);
return RT_ERROR;
}
/* set the overflow time of the watch dog */
ret = rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, &timeout);
if (ret != RT_EOK)
{
rt_kprintf("set %s timeout failed!\n", device_name);
return RT_ERROR;
}
/* set idle thread callback function */
rt_thread_idle_sethook(idle_hook);
return ret;
}
/* export to the msh command list */
MSH_CMD_EXPORT(iwdg_sample, iwdg sample);
```
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册