diff --git a/rt-thread-version/rt-thread-nano/nano-ref/nano-device-adc/demo_code/drv_adc.c b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-adc/demo_code/drv_adc.c new file mode 100644 index 0000000000000000000000000000000000000000..db8860ab00b67d5f5bc2eec8701e12b656d012ba --- /dev/null +++ b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-adc/demo_code/drv_adc.c @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-08-21 RiceChen the first version + */ + +#include +#include "drv_adc.h" + +#ifdef RT_USING_ADC + +struct rt_i2c_config +{ + char *name; + ADC_HandleTypeDef ADC_Handler; +}; + +struct rt_i2c_config adc_config[] = +{ +#ifdef RT_USING_ADC1 + ADC1_CONFIG, +#endif +}; + +struct stm32_adc +{ + struct rt_i2c_config *config; + struct rt_adc_device stm32_adc_device; +}; + +static struct stm32_adc stm32_adc_obj[sizeof(adc_config) / sizeof(adc_config[0])]; + +static rt_err_t stm32_adc_enabled(struct rt_adc_device *device, rt_uint32_t channel, rt_bool_t enabled) +{ + ADC_HandleTypeDef *stm32_adc_handler; + RT_ASSERT(device != RT_NULL); + stm32_adc_handler = (ADC_HandleTypeDef *)device->user_data; + + rt_kprintf("%d: 0x%08x\r\n", __LINE__, stm32_adc_handler); + + if (enabled) + { + ADC_Enable(stm32_adc_handler); + } + else + { + ADC_Disable(stm32_adc_handler); + } + + return RT_EOK; +} + +static rt_uint32_t stm32_adc_get_channel(rt_uint32_t channel) +{ + rt_uint32_t stm32_channel = 0; + + switch (channel) + { + case 0: + stm32_channel = ADC_CHANNEL_0; + break; + case 1: + stm32_channel = ADC_CHANNEL_1; + break; + case 2: + stm32_channel = ADC_CHANNEL_2; + break; + case 3: + stm32_channel = ADC_CHANNEL_3; + break; + case 4: + stm32_channel = ADC_CHANNEL_4; + break; + case 5: + stm32_channel = ADC_CHANNEL_5; + break; + case 6: + stm32_channel = ADC_CHANNEL_6; + break; + case 7: + stm32_channel = ADC_CHANNEL_7; + break; + case 8: + stm32_channel = ADC_CHANNEL_8; + break; + case 9: + stm32_channel = ADC_CHANNEL_9; + break; + case 10: + stm32_channel = ADC_CHANNEL_10; + break; + case 11: + stm32_channel = ADC_CHANNEL_11; + break; + case 12: + stm32_channel = ADC_CHANNEL_12; + break; + case 13: + stm32_channel = ADC_CHANNEL_13; + break; + case 14: + stm32_channel = ADC_CHANNEL_14; + break; + case 15: + stm32_channel = ADC_CHANNEL_15; + break; + } + + return stm32_channel; +} + +static rt_err_t stm32_get_adc_value(struct rt_adc_device *device, rt_uint32_t channel, rt_uint32_t *value) +{ + ADC_ChannelConfTypeDef ADC_ChanConf; + ADC_HandleTypeDef *stm32_adc_handler; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(value != RT_NULL); + + stm32_adc_handler = (ADC_HandleTypeDef *)device->user_data; + + rt_memset(&ADC_ChanConf, 0, sizeof(ADC_ChanConf)); + + if (channel <= 18) + { + /* set stm32 ADC channel */ + ADC_ChanConf.Channel = stm32_adc_get_channel(channel); + } + else + { + rt_kprintf("ADC channel must be between 0 and 18."); + return -RT_ERROR; + } + + ADC_ChanConf.Rank = 1; + ADC_ChanConf.SamplingTime = ADC_SAMPLETIME_247CYCLES_5; + ADC_ChanConf.Offset = 0; + ADC_ChanConf.OffsetNumber = ADC_OFFSET_NONE; + ADC_ChanConf.SingleDiff = LL_ADC_SINGLE_ENDED; + HAL_ADC_ConfigChannel(stm32_adc_handler, &ADC_ChanConf); + + if (HAL_ADCEx_Calibration_Start(stm32_adc_handler, ADC_ChanConf.SingleDiff) != HAL_OK) + { + rt_kprintf("ADC calibration error!\n"); + return -RT_ERROR; + } + /* start ADC */ + HAL_ADC_Start(stm32_adc_handler); + + /* Wait for the ADC to convert */ + HAL_ADC_PollForConversion(stm32_adc_handler, 100); + + /* get ADC value */ + *value = (rt_uint32_t)HAL_ADC_GetValue(stm32_adc_handler); + + return RT_EOK; +} + +rt_uint32_t rt_adc_read(rt_adc_device_t dev, rt_uint32_t channel) +{ + rt_uint32_t value; + + RT_ASSERT(dev); + + stm32_get_adc_value(dev, channel, &value); + + return value; +} + +rt_err_t rt_adc_enable(rt_adc_device_t dev, rt_uint32_t channel) +{ + rt_err_t result = RT_EOK; + + RT_ASSERT(dev); + + result = stm32_adc_enabled(dev, channel, RT_TRUE); + + return result; +} + +rt_err_t rt_adc_disable(rt_adc_device_t dev, rt_uint32_t channel) +{ + rt_err_t result = RT_EOK; + + RT_ASSERT(dev); + + result = stm32_adc_enabled(dev, channel, RT_FALSE); + + return result; +} + +struct rt_adc_device *rt_adc_device_find(const char *name) +{ + int i = 0; + for (i = 0; i < sizeof(adc_config) / sizeof(adc_config[0]); i++) + { + if(rt_strncmp(stm32_adc_obj[i].config->name, name, RT_NAME_MAX) == 0) + { + return &stm32_adc_obj[i].stm32_adc_device; + } + } + return RT_NULL; +} + +static int rt_hw_adc_init(void) +{ + int i = 0; + for (i = 0; i < sizeof(adc_config) / sizeof(adc_config[0]); i++) + { + stm32_adc_obj[i].config = &adc_config[i]; + stm32_adc_obj[i].stm32_adc_device.user_data = (uint8_t *)&stm32_adc_obj[i].config->ADC_Handler; + rt_kprintf("%d: 0x%08x\r\n", __LINE__, &stm32_adc_obj[i].config->ADC_Handler); + + rt_kprintf("%d: 0x%08x\r\n", __LINE__, ADC1); + rt_kprintf("%d: 0x%08x\r\n", __LINE__, stm32_adc_obj[i].config->ADC_Handler.Instance); + + if (HAL_ADC_Init(&stm32_adc_obj[i].config->ADC_Handler) != HAL_OK) + { + rt_kprintf("%s init failed", stm32_adc_obj[i].config->name); + return -RT_ERROR; + } + } + + return RT_EOK; +} +INIT_APP_EXPORT(rt_hw_adc_init); + +void adc_get_obj(void) +{ + int32_t value = 0; + struct rt_adc_device *dev = RT_NULL; + dev = rt_adc_device_find("adc1"); + if(dev == RT_NULL) + { + rt_kprintf("%s not found\r\n", "adc1"); + return; + } + else + { + rt_adc_enable(dev, 3); + value = rt_adc_read(dev, 3); + rt_kprintf("adc value: %d\r\n", value); + } +} +MSH_CMD_EXPORT(adc_get_obj, adc_get_obj); + +#endif /* RT_USING_ADC */ diff --git a/rt-thread-version/rt-thread-nano/nano-ref/nano-device-adc/demo_code/drv_adc.h b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-adc/demo_code/drv_adc.h new file mode 100644 index 0000000000000000000000000000000000000000..bf0db11c53274320156fb3ff2773f60098983187 --- /dev/null +++ b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-adc/demo_code/drv_adc.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-04-20 RiceChen first version + */ + +#ifndef __DRV_ADC_H__ +#define __DRV_ADC_H__ + +#include +#include +#include "adc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef RT_USING_ADC1 +#ifndef ADC1_CONFIG +#define ADC1_CONFIG \ + { \ + .name = "adc1", \ + .ADC_Handler.Instance = ADC1, \ + .ADC_Handler.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4, \ + .ADC_Handler.Init.Resolution = ADC_RESOLUTION_12B, \ + .ADC_Handler.Init.DataAlign = ADC_DATAALIGN_RIGHT, \ + .ADC_Handler.Init.ScanConvMode = DISABLE, \ + .ADC_Handler.Init.EOCSelection = DISABLE, \ + .ADC_Handler.Init.ContinuousConvMode = DISABLE, \ + .ADC_Handler.Init.NbrOfConversion = 1, \ + .ADC_Handler.Init.DiscontinuousConvMode = DISABLE, \ + .ADC_Handler.Init.NbrOfDiscConversion = 0, \ + .ADC_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START, \ + .ADC_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE, \ + .ADC_Handler.Init.DMAContinuousRequests = DISABLE, \ + } +#endif /* ADC1_CONFIG */ +#endif /* RT_USING_ADC1 */ + +#ifdef __cplusplus +} +#endif + +#endif /* __DRV_ADC_H__ */ + diff --git a/rt-thread-version/rt-thread-nano/nano-ref/nano-device-adc/nano-device-adc.md b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-adc/nano-device-adc.md new file mode 100644 index 0000000000000000000000000000000000000000..794010b6783bad8720aea61c8b562bb6c1b47597 --- /dev/null +++ b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-adc/nano-device-adc.md @@ -0,0 +1,112 @@ +# 在 RT-Thread Studio 上使用 RT-Thread Nano 并使用ADC设备接口 + +本文介绍了如何在 RT-Thread Studio 上使用 RT-Thread Nano,并基于 BearPI-IOT STM32L431RCT6 的基础工程进行讲解如何使用 ADC 设备接口。 + +## 为什么需要设备接口 +1. RT-Thread 分为标准版本和 Nano 版本,其特点如下: + - RT-Thread 标准版:拥有驱动框架,软件包等组件,软件包都是基于设备驱动接口来实现。 + - RT-Thread Nano :仅仅只是一个 RTOS 内核。没有任何组件。 +2. Nano 是无法直接使用 RT-Thread 丰富软件包功能。 +3. Nano 是一个面向低资源的 MCU 等芯片,不可能增加如同标准版的设备驱动框架。 +4. Nano 需要一套统一设备驱动 API,屏蔽不同芯片的 HAL 层的区别。方便移植工程到不同的平台。 +4. Nano 需要一套设备驱动 API,可以方便使用丰富软件包组件。 + +## 准备工作 +1. 使用 RT-Thread Studio 建立一个 STM32L431RCT6 的 RT-Thread Nano 基础工程。 +2. 基础工程创建可参考:[在 RT-Thread Studio 上使用 RT-Thread Nano](../../nano-port-studio/an0047-nano-port-studio.md) + +## ADC 设备接口 + +1. 在 RT-Thread 标准版中,[ADC设备](../../../rt-thread-standard/programming-manual/device/adc/adc.md)驱动提供了一套设备管理接口来访问 ADC,用户程序可以直接使用该 API 操作 ADC 的功能,设备管理接口如下: + +| **函数** | **描述** | +| --------------- | ------------------ | +| rt_device_find() | 根据 ADC 设备名称查找设备获取设备句柄 | +| rt_adc_enable() | 使能 ADC 设备 | +| rt_adc_read() | 读取 ADC 设备数据 | +| rt_adc_disable() | 关闭 ADC 设备 | + +2. 由于 RT-Thread Nano 不使用设备驱动框架,所以没有对应的 rt_device_find() 这个API获取设备对象。当为了能够与 RT-Thread 标准版的接口相近,我们需要做了简单的修改,设备管理接口如下: + +| **函数** | **描述** | +| --------------- | ------------------ | +| rt_adc_device_find() | 根据 ADC 设备名称查找设备获取设备句柄 | +| rt_adc_enable() | 使能 ADC 设备 | +| rt_adc_read() | 读取 ADC 设备数据 | +| rt_adc_disable() | 关闭 ADC 设备 | + +3. 对于 RT-Thread Nano,只需要适配如上这套 API,便可简单修改后使用 RT-Thread 丰富软件包功能。 + +## 适配 ADC 设备驱动接口 + +1. 复制 RT-Thread 完整版工程中的 adc.h 文件(路径:rt-thread\components\drivers\include\drivers\adc.h)到我们准备好的 STM32L431RCT6 的 RT-Thread Nano 基础工程中。 + +2. 由于 RT-Thread Nano 没有驱动框架,所以我们要把 adc.h 中有关完整版的内容去掉。整理完之后的 adc.h 文件如下: + +``` C +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-08-22 RiceChen first version + */ + +#ifndef __ADC_H__ +#define __ADC_H__ +#include + +struct rt_adc_device +{ + uint8_t *user_data; +}; +typedef struct rt_adc_device *rt_adc_device_t; + +typedef enum +{ + RT_ADC_CMD_ENABLE, + RT_ADC_CMD_DISABLE, +} rt_adc_cmd_t; + +struct rt_adc_device *rt_adc_device_find(const char *name); +rt_uint32_t rt_adc_read(rt_adc_device_t dev, rt_uint32_t channel); +rt_err_t rt_adc_enable(rt_adc_device_t dev, rt_uint32_t channel); +rt_err_t rt_adc_disable(rt_adc_device_t dev, rt_uint32_t channel); + +#endif /* __ADC_H__ */ +``` + +3. 我们需要适配如上4个 ADC 设备 API,参考实例:[drv_adc.c](./demo_code/drv_adc.c) 和 [drv_adc.h](./demo_code/drv_adc.h)。 + + +## 编写 ADC 设备使用示例 + +``` C +void adc_test(void) +{ + int32_t value = 0; + struct rt_adc_device *dev = RT_NULL; + dev = rt_adc_device_find("adc1"); + if(dev == RT_NULL) + { + rt_kprintf("%s not found\r\n", "adc1"); + return; + } + else + { + rt_adc_enable(dev, 3); + value = rt_adc_read(dev, 3); + rt_kprintf("adc value: %d\r\n", value); + } +} +MSH_CMD_EXPORT(adc_test, adc test); +``` + +1. 实例代码运行现象: +``` C +msh >adc_test +adc value: 565 +msh > +``` diff --git a/rt-thread-version/rt-thread-nano/nano-ref/nano-device-i2c/demo_code/drv_i2c.c b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-i2c/demo_code/drv_i2c.c new file mode 100644 index 0000000000000000000000000000000000000000..7b072aa4fb8a1cef1557d1f60d401ea2a71e3f73 --- /dev/null +++ b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-i2c/demo_code/drv_i2c.c @@ -0,0 +1,504 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-08-21 RiceChen the first version + */ + +#include +#include "drv_i2c.h" + +#ifdef RT_USING_I2C + +enum +{ +#ifdef RT_USING_I2C1 + I2C1_INDEX, +#endif +#ifdef RT_USING_I2C2 + I2C2_INDEX, +#endif +}; + +static struct rt_i2c_config i2c_config[] = +{ +#ifdef RT_USING_I2C1 + RT_I2C1_CONFIG, +#endif +#ifdef RT_USING_I2C2 + RT_I2C1_CONFIG +#endif +}; + +static struct rt_i2c_bus_device i2c_bus[sizeof(i2c_config) / sizeof(i2c_config[0])] = {0}; + +static void rt_i2c_configure(struct rt_i2c_bus_device *bus) +{ + rt_uint8_t scl_pin = bus->config->scl; + rt_uint8_t sda_pin = bus->config->sda; + + rt_pin_mode(scl_pin, PIN_MODE_OUTPUT_OD); + rt_pin_mode(sda_pin, PIN_MODE_OUTPUT_OD); + + rt_pin_write(scl_pin, PIN_HIGH); + rt_pin_write(sda_pin, PIN_HIGH); +} + +static void rt_i2c_set_sda(struct rt_i2c_bus_device *bus, rt_uint32_t state) +{ + rt_uint8_t sda_pin = bus->config->sda; + + if (state) + { + rt_pin_write(sda_pin, PIN_HIGH); + } + else + { + rt_pin_write(sda_pin, PIN_LOW); + } +} + +static void rt_i2c_set_scl(struct rt_i2c_bus_device *bus, rt_uint32_t state) +{ + rt_uint8_t scl_pin = bus->config->scl; + + if (state) + { + rt_pin_write(scl_pin, PIN_HIGH); + } + else + { + rt_pin_write(scl_pin, PIN_LOW); + } +} + +static rt_uint32_t rt_i2c_get_sda(struct rt_i2c_bus_device *bus) +{ + rt_uint8_t sda_pin = bus->config->sda; + + return rt_pin_read(sda_pin); +} + +static rt_uint32_t rt_i2c_get_scl(struct rt_i2c_bus_device *bus) +{ + rt_uint8_t scl_pin = bus->config->scl; + + return rt_pin_read(scl_pin); +} + +static void rt_i2c_udelay(rt_uint32_t us) +{ + rt_hw_us_delay(us); +} + +#define SET_SDA(bus, val) rt_i2c_set_sda(bus, val) +#define SET_SCL(bus, val) rt_i2c_set_scl(bus, val) +#define GET_SDA(bus) rt_i2c_get_sda(bus) +#define GET_SCL(bus) rt_i2c_get_scl(bus) + +#define SDA_L(bus) SET_SDA(bus, 0) +#define SDA_H(bus) SET_SDA(bus, 1) +#define SCL_L(bus) SET_SCL(bus, 0) + +static rt_err_t SCL_H(struct rt_i2c_bus_device *bus) +{ + rt_tick_t start; + + SET_SCL(bus, 1); + + if(rt_i2c_get_scl(bus)) + { + goto done; + } + + start = rt_tick_get(); + while (!GET_SCL(bus)) + { + if ((rt_tick_get() - start) > 100) + return -RT_ETIMEOUT; + rt_thread_delay(100); + } +done: + rt_i2c_udelay(1); + + return RT_EOK; +} + +static void rt_i2c_start(struct rt_i2c_bus_device *bus) +{ + SDA_L(bus); + rt_i2c_udelay(1); + SCL_L(bus); +} + +static void rt_i2c_restart(struct rt_i2c_bus_device *bus) +{ + SDA_H(bus); + SCL_H(bus); + rt_i2c_udelay(1); + + SDA_L(bus); + rt_i2c_udelay(1); + SCL_L(bus); +} + +static void rt_i2c_stop(struct rt_i2c_bus_device *bus) +{ + SDA_L(bus); + rt_i2c_udelay(1); + SCL_H(bus); + rt_i2c_udelay(1); + SDA_H(bus); + rt_i2c_udelay(1); +} + +rt_inline rt_bool_t rt_i2c_waitack(struct rt_i2c_bus_device *bus) +{ + rt_bool_t ack; + + SDA_H(bus); + rt_i2c_udelay(1); + + if (SCL_H(bus) < 0) + { + return -RT_ETIMEOUT; + } + ack = !GET_SDA(bus); + SCL_L(bus); + + return ack; +} + +static rt_int32_t rt_i2c_writeb(struct rt_i2c_bus_device *bus, rt_uint8_t data) +{ + rt_int32_t i; + rt_uint8_t bit; + + for (i = 7; i >= 0; i--) + { + SCL_L(bus); + bit = (data >> i) & 1; + SET_SDA(bus, bit); + rt_i2c_udelay(1); + if (SCL_H(bus) < 0) + { + return -RT_ETIMEOUT; + } + } + SCL_L(bus); + rt_i2c_udelay(1); + + return rt_i2c_waitack(bus); +} + +static rt_int32_t rt_i2c_readb(struct rt_i2c_bus_device *bus) +{ + rt_uint8_t i; + rt_uint8_t data = 0; + + SDA_H(bus); + rt_i2c_udelay(1); + for (i = 0; i < 8; i++) + { + data <<= 1; + if (SCL_H(bus) < 0) + { + return -RT_ETIMEOUT; + } + if (GET_SDA(bus)) + data |= 1; + SCL_L(bus); + rt_i2c_udelay(1); + } + + return data; +} + +static rt_size_t rt_i2c_send_bytes(struct rt_i2c_bus_device *bus, + struct rt_i2c_msg *msg) +{ + rt_int32_t ret; + rt_size_t bytes = 0; + const rt_uint8_t *ptr = msg->buf; + rt_int32_t count = msg->len; + rt_uint16_t ignore_nack = msg->flags & RT_I2C_IGNORE_NACK; + + while (count > 0) + { + ret = rt_i2c_writeb(bus, *ptr); + + if ((ret > 0) || (ignore_nack && (ret == 0))) + { + count --; + ptr ++; + bytes ++; + } + else if (ret == 0) + { + return 0; + } + else + { + return ret; + } + } + + return bytes; +} + +static rt_err_t rt_i2c_send_ack_or_nack(struct rt_i2c_bus_device *bus, int ack) +{ + if (ack) + SET_SDA(bus, 0); + rt_i2c_udelay(1); + if (SCL_H(bus) < 0) + { + return -RT_ETIMEOUT; + } + SCL_L(bus); + + return RT_EOK; +} + +static rt_size_t rt_i2c_recv_bytes(struct rt_i2c_bus_device *bus, + struct rt_i2c_msg *msg) +{ + rt_int32_t val; + rt_int32_t bytes = 0; /* actual bytes */ + rt_uint8_t *ptr = msg->buf; + rt_int32_t count = msg->len; + const rt_uint32_t flags = msg->flags; + + while (count > 0) + { + val = rt_i2c_readb(bus); + if (val >= 0) + { + *ptr = val; + bytes ++; + } + else + { + break; + } + + ptr ++; + count --; + if (!(flags & RT_I2C_NO_READ_ACK)) + { + val = rt_i2c_send_ack_or_nack(bus, count); + if (val < 0) + return val; + } + } + + return bytes; +} + +static rt_int32_t rt_i2c_send_address(struct rt_i2c_bus_device *bus, + rt_uint8_t addr, rt_int32_t retries) +{ + rt_int32_t i; + rt_err_t ret = 0; + + for (i = 0; i <= retries; i++) + { + ret = rt_i2c_writeb(bus, addr); + if (ret == 1 || i == retries) + break; + rt_i2c_stop(bus); + rt_i2c_udelay(1); + rt_i2c_start(bus); + } + + return ret; +} + +static rt_err_t rt_i2c_bit_send_address(struct rt_i2c_bus_device *bus, + struct rt_i2c_msg *msg) +{ + rt_uint16_t flags = msg->flags; + rt_uint16_t ignore_nack = msg->flags & RT_I2C_IGNORE_NACK; + + rt_uint8_t addr1, addr2; + rt_int32_t retries; + rt_err_t ret; + + retries = ignore_nack ? 0 : bus->retries; + + if (flags & RT_I2C_ADDR_10BIT) + { + addr1 = 0xf0 | ((msg->addr >> 7) & 0x06); + addr2 = msg->addr & 0xff; + + ret = rt_i2c_send_address(bus, addr1, retries); + if ((ret != 1) && !ignore_nack) + { + return -RT_EIO; + } + + ret = rt_i2c_writeb(bus, addr2); + if ((ret != 1) && !ignore_nack) + { + return -RT_EIO; + } + if (flags & RT_I2C_RD) + { + rt_i2c_restart(bus); + addr1 |= 0x01; + ret = rt_i2c_send_address(bus, addr1, retries); + if ((ret != 1) && !ignore_nack) + { + return -RT_EIO; + } + } + } + else + { + addr1 = msg->addr << 1; + if (flags & RT_I2C_RD) + addr1 |= 1; + ret = rt_i2c_send_address(bus, addr1, retries); + if ((ret != 1) && !ignore_nack) + return -RT_EIO; + } + + return RT_EOK; +} + +rt_err_t rt_i2c_control(struct rt_i2c_bus_device *bus, + rt_uint32_t cmd, + rt_uint32_t arg) +{ + return RT_EOK; +} + +rt_size_t rt_i2c_transfer(struct rt_i2c_bus_device *bus, + struct rt_i2c_msg msgs[], + rt_uint32_t num) +{ + struct rt_i2c_msg *msg; + rt_int32_t i, ret; + rt_uint16_t ignore_nack; + + rt_i2c_start(bus); + for (i = 0; i < num; i++) + { + msg = &msgs[i]; + ignore_nack = msg->flags & RT_I2C_IGNORE_NACK; + if (!(msg->flags & RT_I2C_NO_START)) + { + if (i) + { + rt_i2c_restart(bus); + } + ret = rt_i2c_bit_send_address(bus, msg); + if ((ret != RT_EOK) && !ignore_nack) + { + goto out; + } + } + if (msg->flags & RT_I2C_RD) + { + ret = rt_i2c_recv_bytes(bus, msg); + if (ret >= 1) + ; + if (ret < msg->len) + { + if (ret >= 0) + ret = -RT_EIO; + goto out; + } + } + else + { + ret = rt_i2c_send_bytes(bus, msg); + if (ret >= 1) + ; + if (ret < msg->len) + { + if (ret >= 0) + ret = -RT_ERROR; + goto out; + } + } + } + ret = i; + +out: + rt_i2c_stop(bus); + + return ret; +} + +rt_size_t rt_i2c_master_send(struct rt_i2c_bus_device *bus, + rt_uint16_t addr, + rt_uint16_t flags, + const rt_uint8_t *buf, + rt_uint32_t count) +{ + rt_err_t ret; + struct rt_i2c_msg msg; + + msg.addr = addr; + msg.flags = flags; + msg.len = count; + msg.buf = (rt_uint8_t *)buf; + + ret = rt_i2c_transfer(bus, &msg, 1); + + return (ret > 0) ? count : ret; +} + +rt_size_t rt_i2c_master_recv(struct rt_i2c_bus_device *bus, + rt_uint16_t addr, + rt_uint16_t flags, + rt_uint8_t *buf, + rt_uint32_t count) +{ + rt_err_t ret; + struct rt_i2c_msg msg; + RT_ASSERT(bus != RT_NULL); + + msg.addr = addr; + msg.flags = flags | RT_I2C_RD; + msg.len = count; + msg.buf = buf; + + ret = rt_i2c_transfer(bus, &msg, 1); + + return (ret > 0) ? count : ret; +} + +struct rt_i2c_bus_device *rt_i2c_bus_device_find(const char *bus_name) +{ + rt_size_t bus_num = sizeof(i2c_bus) / sizeof(i2c_bus[0]); + + for(int i = 0; i < bus_num; i++) + { + if(rt_strncmp(i2c_bus[i].config->name, bus_name, RT_NAME_MAX) == 0) + { + return &i2c_bus[i]; + } + } + return RT_NULL; +} + +int rt_i2c_core_init(void) +{ + rt_size_t bus_num = sizeof(i2c_bus) / sizeof(i2c_bus[0]); + + for(int i = 0; i < bus_num; i++) + { + i2c_bus[i].config = &i2c_config[i]; + rt_i2c_configure(&i2c_bus[i]); + } + + return RT_EOK; +} +INIT_COMPONENT_EXPORT(rt_i2c_core_init); + +#endif /* RT_USING_I2C */ diff --git a/rt-thread-version/rt-thread-nano/nano-ref/nano-device-i2c/demo_code/drv_i2c.h b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-i2c/demo_code/drv_i2c.h new file mode 100644 index 0000000000000000000000000000000000000000..444f7d2867364d5a4eff41ccf6fa8ea3cbcfcccb --- /dev/null +++ b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-i2c/demo_code/drv_i2c.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-04-20 RiceChen first version + */ + +#ifndef __DRV_I2C_H__ +#define __DRV_I2C_H__ + +#include +#include +#include "pin.h" +#include "i2c.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef RT_USING_I2C1 +#define RT_I2C1_SCL_PIN GET_PIN(B, 6) +#define RT_I2C1_SDA_PIN GET_PIN(B, 7) + +#define RT_I2C1_CONFIG \ + { \ + .name = "i2c1", \ + .scl = RT_I2C1_SCL_PIN, \ + .sda = RT_I2C1_SDA_PIN, \ + } +#endif + +#ifdef RT_USING_I2C2 +#define RT_I2C2_SCL_PIN GET_PIN(B, 8) +#define RT_I2C2_SDA_PIN GET_PIN(B, 9) + +#define RT_I2C2_CONFIG \ + { \ + .name = "i2c2", \ + .scl = RT_I2C2_SCL_PIN, \ + .sda = RT_I2C2_SDA_PIN, \ + } +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __DRV_GPIO_H__ */ + diff --git a/rt-thread-version/rt-thread-nano/nano-ref/nano-device-i2c/nano-device-i2c.md b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-i2c/nano-device-i2c.md new file mode 100644 index 0000000000000000000000000000000000000000..945891face7e351c11cdf440d36eb3a9e9dceb55 --- /dev/null +++ b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-i2c/nano-device-i2c.md @@ -0,0 +1,442 @@ +# 在 RT-Thread Studio 上使用 RT-Thread Nano 并使用 I2C 设备接口 + +本文介绍了如何在 RT-Thread Studio 上使用 RT-Thread Nano,并基于 BearPI-IOT STM32L431RCT6 的基础工程进行讲解如何使用 I2C 设备接口及相关软件包使用。 + +## 为什么需要设备接口 +1. RT-Thread 分为标准版本和 Nano 版本,其特点如下: + - RT-Thread 标准版:拥有设备驱动框架,软件包等组件,软件包都是基于设备驱动接口来实现。 + - RT-Thread Nano:仅仅只是一个 RTOS 内核。没有任何组件。 +2. Nano 是无法直接使用 RT-Thread 丰富软件包功能。 +3. Nano 是一个面向低资源的 MCU 等芯片,不可能增加如同标准版的设备驱动框架。 +4. Nano 需要一套统一设备驱动 API,屏蔽不同芯片的 HAL 层的区别。方便移植工程到不同的平台。 +4. Nano 需要一套设备驱动 API,可以方便使用丰富软件包组件。 + +## 准备工作 +1. 使用 RT-Thread Studio 建立一个 STM32L431RCT6 的 RT-Thread Nano 基础工程。 +2. 基础工程创建可参考:[在 RT-Thread Studio 上使用 RT-Thread Nano](../../nano-port-studio/an0047-nano-port-studio.md) + +## I2C 设备接口 + +1. 在 RT-Thread 标准版中,[I2C设备](../../../rt-thread-standard/programming-manual/device/i2c/i2c.md)驱动提供了一套设备管理接口来访问 I2C,用户程序可以直接使用该 API 操作 I2C 的功能,设备管理接口如下: + +| **函数** | **描述** | +| --------------- | ---------------------------------- | +| rt_device_find() | 根据 I2C 总线设备名称查找设备获取设备句柄 | +| rt_i2c_transfer() | 传输数据 | + +2. 由于 RT-Thread Nano 不使用设备驱动框架,所以没有对应的 rt_device_find() 这个 API 获取设备对象。但 RT-Thread 标准版实际为用户层提供了另外一套 API 给用户层使用。设备管理接口如下: + +| **函数** | **描述** | +| --------------- | ---------------------------------- | +| rt_i2c_bus_device_find() | 根据 I2C 总线设备名称查找设备获取设备句柄 | +| rt_i2c_transfer() | 传输数据 | +| rt_i2c_master_send() | 发送数据 | +| rt_i2c_master_recv() | 接收数据 | + +3. 对于 RT-Thread Nano,只需要适配如上这套 API,便可简单修改后使用 RT-Thread 丰富软件包功能。 + +## 适配 I2C 设备接口 + +1. 复制 RT-Thread 完整版工程中的 i2c.h 文件(路径:rt-thread\components\drivers\include\drivers\i2c.h)到我们准备好的 STM32L431RCT6 的 RT-Thread Nano 基础工程中。 + +2. 由于 RT-Thread Nano 没有设备驱动框架,所以我们要把 i2c.h 中有关完整版的内容去掉。整理完之后的 i2c.h 文件如下: + +``` C +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-04-20 RiceChen first version + */ + +#ifndef __I2C_H__ +#define __I2C_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RT_I2C_WR 0x0000 +#define RT_I2C_RD (1u << 0) +#define RT_I2C_ADDR_10BIT (1u << 2) /* this is a ten bit chip address */ +#define RT_I2C_NO_START (1u << 4) +#define RT_I2C_IGNORE_NACK (1u << 5) +#define RT_I2C_NO_READ_ACK (1u << 6) /* when I2C reading, we do not ACK */ +#define RT_I2C_NO_STOP (1u << 7) + +struct rt_i2c_config +{ + char *name; + rt_uint8_t scl; + rt_uint8_t sda; +}; + +struct rt_i2c_msg +{ + rt_uint16_t addr; + rt_uint16_t flags; + rt_uint16_t len; + rt_uint8_t *buf; +}; + +/*for i2c bus driver*/ +struct rt_i2c_bus_device +{ + struct rt_i2c_config *config; + rt_uint16_t flags; + rt_uint16_t addr; + struct rt_mutex lock; + rt_uint32_t timeout; + rt_uint32_t retries; + void *priv; +}; + +struct rt_i2c_bus_device *rt_i2c_bus_device_find(const char *bus_name); +rt_size_t rt_i2c_transfer(struct rt_i2c_bus_device *bus, + struct rt_i2c_msg msgs[], + rt_uint32_t num); +rt_err_t rt_i2c_control(struct rt_i2c_bus_device *bus, + rt_uint32_t cmd, + rt_uint32_t arg); +rt_size_t rt_i2c_master_send(struct rt_i2c_bus_device *bus, + rt_uint16_t addr, + rt_uint16_t flags, + const rt_uint8_t *buf, + rt_uint32_t count); +rt_size_t rt_i2c_master_recv(struct rt_i2c_bus_device *bus, + rt_uint16_t addr, + rt_uint16_t flags, + rt_uint8_t *buf, + rt_uint32_t count); + +rt_inline rt_err_t rt_i2c_bus_lock(struct rt_i2c_bus_device *bus, rt_tick_t timeout) +{ + return rt_mutex_take(&bus->lock, timeout); +} + +rt_inline rt_err_t rt_i2c_bus_unlock(struct rt_i2c_bus_device *bus) +{ + return rt_mutex_release(&bus->lock); +} + +int rt_i2c_core_init(void); + +#ifdef __cplusplus +} +#endif + +#endif + +``` + +3. 我们需要适配如上6个 I2C 设备 API ,参考实例:[drv_i2c.c](./demo_code/drv_i2c.c) 和 [drv_i2c.h](./demo_code/drv_i2c.h)。 + +| **函数** | **描述** | +| --------------- | ---------------------------------- | +| rt_i2c_core_init() | I2C 总线初始化 | +| rt_i2c_bus_device_find() | 根据 I2C 总线设备名称查找设备获取设备句柄 | +| rt_i2c_transfer() | 数据传输 | +| rt_i2c_control() | I2C 总线配置 | +| rt_i2c_master_send() | 发送数据 | +| rt_i2c_master_recv() | 接收数据 | + + +## 编写 I2C 设备使用示例 + +``` C +#include +#include + +#define AHT10_I2C_BUS_NAME "i2c1" /* 传感器连接的I2C总线设备名称 */ +#define AHT10_ADDR 0x38 /* 从机地址 */ +#define AHT10_CALIBRATION_CMD 0xE1 /* 校准命令 */ +#define AHT10_NORMAL_CMD 0xA8 /* 一般命令 */ +#define AHT10_GET_DATA 0xAC /* 获取数据命令 */ + +static struct rt_i2c_bus_device *i2c_bus = RT_NULL; /* I2C总线设备句柄 */ +static rt_bool_t initialized = RT_FALSE; /* 传感器初始化状态 */ + +/* 写传感器寄存器 */ +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; + rt_uint32_t buf_size = 1; + + buf[0] = reg; //cmd + if (data != RT_NULL) + { + buf[1] = data[0]; + buf[2] = data[1]; + buf_size = 3; + } + + msgs.addr = AHT10_ADDR; + msgs.flags = RT_I2C_WR; + msgs.buf = buf; + msgs.len = buf_size; + + /* 调用I2C设备接口传输数据 */ + if (rt_i2c_transfer(bus, &msgs, 1) == 1) + { + return RT_EOK; + } + else + { + return -RT_ERROR; + } +} + +/* 读传感器寄存器数据 */ +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; + + /* 调用I2C设备接口传输数据 */ + 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, RT_NULL); /* 发送命令 */ + rt_thread_mdelay(400); + read_regs(i2c_bus, 6, temp); /* 获取传感器数据 */ + + /* 湿度数据转换 */ + *cur_humi = (temp[1] << 12 | temp[2] << 4 | (temp[3] & 0xf0) >> 4) * 100.0 / (1 << 20); + /* 温度数据转换 */ + *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}; + + /* 查找I2C总线设备,获取I2C总线设备句柄 */ + i2c_bus = rt_i2c_bus_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) + { + /* 传感器初始化 */ + aht10_init(name); + } + if (initialized) + { + /* 读取温湿度数据 */ + read_temp_humi(&temperature, &humidity); + + rt_kprintf("read aht10 sensor humidity : %d.%d %%\n", (int)humidity, (int)(humidity * 10) % 10); + if( temperature >= 0 ) + { + rt_kprintf("read aht10 sensor temperature: %d.%d°C\n", (int)temperature, (int)(temperature * 10) % 10); + } + else + { + rt_kprintf("read aht10 sensor temperature: %d.%d°C\n", (int)temperature, (int)(-temperature * 10) % 10); + } + } + else + { + rt_kprintf("initialize sensor failed!\n"); + } +} +/* 导出到 msh 命令列表中 */ +MSH_CMD_EXPORT(i2c_aht10_sample, i2c aht10 sample); + +实例代码运行现象: + +``` C +msh >i2c_aht10_sample i2c1 +read aht10 sensor humidity : 90.0 % +read aht10 sensor temperature: 25.33°C +msh > +``` + + +## I2C 设备相关软件包使用 + +1. 我们使用[as7341软件包](http://packages.rt-thread.org/detail.html?package=as7341)来验证 I2C 设备 API。 + +2. 首先克隆 as7341 软件包到 STM32L431RCT6 的 RT-Thread Nano 工程。as7341 软件包链接:https://github.com/RiceChen/as7341.git + +3. 由于没有了 RT-Thread 标准版本的设备驱动框架,所以对软件包进行简单的修改: + - 在 as7341.h 中包含 drv_i2c.h 头文件。 + - 修改 as7341.c 中的测试用例。代码如下: + +``` C +static void as7341(int argc, char *argv[]) +{ + static as7341_device_t dev = RT_NULL; + + if (argc > 1) + { + if (!strcmp(argv[1], "probe")) + { + if (argc >= 3) + { + /* initialize the sensor when first probe */ + if (!dev || strcmp(dev->i2c->config->name, argv[2])) // 修改点1 + { + /* deinit the old device */ + if(dev) + { + rt_kprintf("Deinit as7341\n"); + as7341_deinit(dev); + } + + dev = as7341_init(argv[2], eSpm); + if(!dev) + { + rt_kprintf("as7341 probe failed, check input args\n"); + }else + { + rt_kprintf("as7341 probed, addr:0x%x\n", AS7341_ADDR) ; + } + } + } + else + { + as7341_usage(); + } + } + else if (!strcmp(argv[1], "read")) + { + if (dev) + { + if(!strcmp(argv[2], "spectral")) + { + MODE_ONE_DATA_t data1; + MODE_TOW_DATA_t data2; + + as7341_start_measure(dev, eF1F4ClearNIR); + data1 = as7341_read_spectral_data_one(dev); + rt_kprintf("F1(405-425nm): %d\n", data1.ADF1); + rt_kprintf("F2(435-455nm): %d\n", data1.ADF2); + rt_kprintf("F3(470-490nm): %d\n", data1.ADF3); + rt_kprintf("F4(505-525nm): %d\n", data1.ADF4); + + as7341_start_measure(dev, eF5F8ClearNIR); + data2 = as7341_read_spectral_data_tow(dev); + rt_kprintf("F5(545-565nm): %d\n", data2.ADF5); + rt_kprintf("F6(580-600nm): %d\n", data2.ADF6); + rt_kprintf("F7(620-640nm): %d\n", data2.ADF7); + rt_kprintf("F8(670-690nm): %d\n", data2.ADF8); + + rt_kprintf("Clear: %d\n", data2.ADCLEAR); + rt_kprintf("NIR: %d\n", data2.ADNIR); + } + else if(!strcmp(argv[2], "flicker")) + { + rt_uint8_t freq = 0; + freq = as7341_read_flicker_data(dev); + if(freq == 1) + { + rt_kprintf("Unknown frequency\n"); + } + else if(freq == 0) + { + rt_kprintf("No flicker\n"); + } + else + { + rt_kprintf("freq: %dHz\n", freq); + } + } + else + { + as7341_usage(); + } + + } + else + { + rt_kprintf("Please using 'as7341 probe ' first\n"); + } + } + else + { + as7341_usage(); + } + } + else + { + as7341_usage(); + } +} +``` + +3. 使用 as7341 软件包实例,编译烧录便可以在终端输入测试命令: + +``` C +msh >as7341 probe i2c1 +as7341 id: 0x24 +as7341 probed, addr:0x39 +msh > +msh >as7341 read spectral +F1(405-425nm): 1 +F2(435-455nm): 3 +F3(470-490nm): 4 +F4(505-525nm): 5 +F5(545-565nm): 7 +F6(580-600nm): 6 +F7(620-640nm): 7 +F8(670-690nm): 4 +Clear: 22 +NIR: 2 +msh > +``` + + diff --git a/rt-thread-version/rt-thread-nano/nano-ref/nano-device-pin/demo_code/drv_gpio.c b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-pin/demo_code/drv_gpio.c new file mode 100644 index 0000000000000000000000000000000000000000..beaea1281031fd78215b40075d7693afed512fbc --- /dev/null +++ b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-pin/demo_code/drv_gpio.c @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-08-21 RiceChen the first version + */ + +#include +#include "pin.h" +#include "drv_gpio.h" + +#ifdef RT_USING_PIN + +#define PIN_NUM(port, no) (((((port) & 0xFu) << 4) | ((no) & 0xFu))) +#define PIN_PORT(pin) ((uint8_t)(((pin) >> 4) & 0xFu)) +#define PIN_NO(pin) ((uint8_t)((pin) & 0xFu)) + +#define PIN_STPORT(pin) ((GPIO_TypeDef *)(GPIOA_BASE + (0x400u * PIN_PORT(pin)))) + +#define PIN_STPIN(pin) ((uint16_t)(1u << PIN_NO(pin))) + +#define __STM32_PORT_MAX 8u + +#define PIN_STPORT_MAX __STM32_PORT_MAX + +static const struct pin_irq_map pin_irq_map[] = +{ + {GPIO_PIN_0, EXTI0_IRQn}, + {GPIO_PIN_1, EXTI1_IRQn}, + {GPIO_PIN_2, EXTI2_IRQn}, + {GPIO_PIN_3, EXTI3_IRQn}, + {GPIO_PIN_4, EXTI4_IRQn}, + {GPIO_PIN_5, EXTI9_5_IRQn}, + {GPIO_PIN_6, EXTI9_5_IRQn}, + {GPIO_PIN_7, EXTI9_5_IRQn}, + {GPIO_PIN_8, EXTI9_5_IRQn}, + {GPIO_PIN_9, EXTI9_5_IRQn}, + {GPIO_PIN_10, EXTI15_10_IRQn}, + {GPIO_PIN_11, EXTI15_10_IRQn}, + {GPIO_PIN_12, EXTI15_10_IRQn}, + {GPIO_PIN_13, EXTI15_10_IRQn}, + {GPIO_PIN_14, EXTI15_10_IRQn}, + {GPIO_PIN_15, EXTI15_10_IRQn}, +}; + +static struct rt_pin_irq_hdr pin_irq_hdr_tab[] = +{ + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, + {-1, 0, RT_NULL, RT_NULL}, +}; +static uint32_t pin_irq_enable_mask = 0; + +#define ITEM_NUM(items) sizeof(items) / sizeof(items[0]) + +rt_base_t stm32_pin_get(const char *name) +{ + rt_base_t pin = 0; + int hw_port_num, hw_pin_num = 0; + int i, name_len; + + name_len = rt_strlen(name); + + if ((name_len < 4) || (name_len >= 6)) + { + return -RT_EINVAL; + } + if ((name[0] != 'P') || (name[2] != '.')) + { + return -RT_EINVAL; + } + + if ((name[1] >= 'A') && (name[1] <= 'Z')) + { + hw_port_num = (int)(name[1] - 'A'); + } + else + { + return -RT_EINVAL; + } + + for (i = 3; i < name_len; i++) + { + hw_pin_num *= 10; + hw_pin_num += name[i] - '0'; + } + + pin = PIN_NUM(hw_port_num, hw_pin_num); + + return pin; +} + +void rt_pin_write(rt_base_t pin, rt_base_t value) +{ + GPIO_TypeDef *gpio_port; + uint16_t gpio_pin; + + if (PIN_PORT(pin) < PIN_STPORT_MAX) + { + gpio_port = PIN_STPORT(pin); + gpio_pin = PIN_STPIN(pin); + + HAL_GPIO_WritePin(gpio_port, gpio_pin, (GPIO_PinState)value); + } +} + +int rt_pin_read(rt_base_t pin) +{ + GPIO_TypeDef *gpio_port; + uint16_t gpio_pin; + int value = PIN_LOW; + + if (PIN_PORT(pin) < PIN_STPORT_MAX) + { + gpio_port = PIN_STPORT(pin); + gpio_pin = PIN_STPIN(pin); + value = HAL_GPIO_ReadPin(gpio_port, gpio_pin); + } + + return value; +} + +void rt_pin_mode(rt_base_t pin, rt_base_t mode) +{ + GPIO_InitTypeDef GPIO_InitStruct; + + if (PIN_PORT(pin) >= PIN_STPORT_MAX) + { + return; + } + + /* Configure GPIO_InitStructure */ + GPIO_InitStruct.Pin = PIN_STPIN(pin); + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + + if (mode == PIN_MODE_OUTPUT) + { + /* output setting */ + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + } + else if (mode == PIN_MODE_INPUT) + { + /* input setting: not pull. */ + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_NOPULL; + } + else if (mode == PIN_MODE_INPUT_PULLUP) + { + /* input setting: pull up. */ + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_PULLUP; + } + else if (mode == PIN_MODE_INPUT_PULLDOWN) + { + /* input setting: pull down. */ + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_PULLDOWN; + } + else if (mode == PIN_MODE_OUTPUT_OD) + { + /* output setting: od. */ + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; + GPIO_InitStruct.Pull = GPIO_NOPULL; + } + + HAL_GPIO_Init(PIN_STPORT(pin), &GPIO_InitStruct); +} + +rt_inline rt_int32_t bit2bitno(rt_uint32_t bit) +{ + int i; + for (i = 0; i < 32; i++) + { + if ((0x01 << i) == bit) + { + return i; + } + } + return -1; +} + +rt_inline const struct pin_irq_map *get_pin_irq_map(uint32_t pinbit) +{ + rt_int32_t mapindex = bit2bitno(pinbit); + if (mapindex < 0 || mapindex >= ITEM_NUM(pin_irq_map)) + { + return RT_NULL; + } + return &pin_irq_map[mapindex]; +}; + +rt_err_t rt_pin_attach_irq(rt_int32_t pin, rt_uint32_t mode, void (*hdr)(void *args), void *args) +{ + rt_base_t level; + rt_int32_t irqindex = -1; + + if (PIN_PORT(pin) >= PIN_STPORT_MAX) + { + return -RT_ENOSYS; + } + + irqindex = bit2bitno(PIN_STPIN(pin)); + if (irqindex < 0 || irqindex >= ITEM_NUM(pin_irq_map)) + { + return RT_ENOSYS; + } + + level = rt_hw_interrupt_disable(); + if (pin_irq_hdr_tab[irqindex].pin == pin && + pin_irq_hdr_tab[irqindex].hdr == hdr && + pin_irq_hdr_tab[irqindex].mode == mode && + pin_irq_hdr_tab[irqindex].args == args) + { + rt_hw_interrupt_enable(level); + return RT_EOK; + } + if (pin_irq_hdr_tab[irqindex].pin != -1) + { + rt_hw_interrupt_enable(level); + return RT_EBUSY; + } + pin_irq_hdr_tab[irqindex].pin = pin; + pin_irq_hdr_tab[irqindex].hdr = hdr; + pin_irq_hdr_tab[irqindex].mode = mode; + pin_irq_hdr_tab[irqindex].args = args; + rt_hw_interrupt_enable(level); + + return RT_EOK; +} + +rt_err_t rt_pin_detach_irq(rt_int32_t pin) +{ + rt_base_t level; + rt_int32_t irqindex = -1; + + if (PIN_PORT(pin) >= PIN_STPORT_MAX) + { + return -RT_ENOSYS; + } + + irqindex = bit2bitno(PIN_STPIN(pin)); + if (irqindex < 0 || irqindex >= ITEM_NUM(pin_irq_map)) + { + return RT_ENOSYS; + } + + level = rt_hw_interrupt_disable(); + if (pin_irq_hdr_tab[irqindex].pin == -1) + { + rt_hw_interrupt_enable(level); + return RT_EOK; + } + pin_irq_hdr_tab[irqindex].pin = -1; + pin_irq_hdr_tab[irqindex].hdr = RT_NULL; + pin_irq_hdr_tab[irqindex].mode = 0; + pin_irq_hdr_tab[irqindex].args = RT_NULL; + rt_hw_interrupt_enable(level); + + return RT_EOK; +} + +rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint32_t enabled) +{ + const struct pin_irq_map *irqmap; + rt_base_t level; + rt_int32_t irqindex = -1; + GPIO_InitTypeDef GPIO_InitStruct; + + if (PIN_PORT(pin) >= PIN_STPORT_MAX) + { + return -RT_ENOSYS; + } + + if (enabled == PIN_IRQ_ENABLE) + { + irqindex = bit2bitno(PIN_STPIN(pin)); + if (irqindex < 0 || irqindex >= ITEM_NUM(pin_irq_map)) + { + return RT_ENOSYS; + } + + level = rt_hw_interrupt_disable(); + + if (pin_irq_hdr_tab[irqindex].pin == -1) + { + rt_hw_interrupt_enable(level); + return RT_ENOSYS; + } + + irqmap = &pin_irq_map[irqindex]; + + /* Configure GPIO_InitStructure */ + GPIO_InitStruct.Pin = PIN_STPIN(pin); + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + switch (pin_irq_hdr_tab[irqindex].mode) + { + case PIN_IRQ_MODE_RISING: + GPIO_InitStruct.Pull = GPIO_PULLDOWN; + GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; + break; + case PIN_IRQ_MODE_FALLING: + GPIO_InitStruct.Pull = GPIO_PULLUP; + GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; + break; + case PIN_IRQ_MODE_RISING_FALLING: + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; + break; + } + HAL_GPIO_Init(PIN_STPORT(pin), &GPIO_InitStruct); + + HAL_NVIC_SetPriority(irqmap->irqno, 5, 0); + HAL_NVIC_EnableIRQ(irqmap->irqno); + pin_irq_enable_mask |= irqmap->pinbit; + + rt_hw_interrupt_enable(level); + } + else if (enabled == PIN_IRQ_DISABLE) + { + irqmap = get_pin_irq_map(PIN_STPIN(pin)); + if (irqmap == RT_NULL) + { + return RT_ENOSYS; + } + + level = rt_hw_interrupt_disable(); + + HAL_GPIO_DeInit(PIN_STPORT(pin), PIN_STPIN(pin)); + + pin_irq_enable_mask &= ~irqmap->pinbit; + if ((irqmap->pinbit >= GPIO_PIN_5) && (irqmap->pinbit <= GPIO_PIN_9)) + { + if (!(pin_irq_enable_mask & (GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9))) + { + HAL_NVIC_DisableIRQ(irqmap->irqno); + } + } + else if ((irqmap->pinbit >= GPIO_PIN_10) && (irqmap->pinbit <= GPIO_PIN_15)) + { + if (!(pin_irq_enable_mask & (GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15))) + { + HAL_NVIC_DisableIRQ(irqmap->irqno); + } + } + else + { + HAL_NVIC_DisableIRQ(irqmap->irqno); + } + rt_hw_interrupt_enable(level); + } + else + { + return -RT_ENOSYS; + } + + return RT_EOK; +} + +rt_inline void pin_irq_hdr(int irqno) +{ + if (pin_irq_hdr_tab[irqno].hdr) + { + pin_irq_hdr_tab[irqno].hdr(pin_irq_hdr_tab[irqno].args); + } +} + +void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) +{ + pin_irq_hdr(bit2bitno(GPIO_Pin)); +} + +void EXTI0_IRQHandler(void) +{ + rt_interrupt_enter(); + HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); + rt_interrupt_leave(); +} + +void EXTI1_IRQHandler(void) +{ + rt_interrupt_enter(); + HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1); + rt_interrupt_leave(); +} + +void EXTI2_IRQHandler(void) +{ + rt_interrupt_enter(); + HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2); + rt_interrupt_leave(); +} + +void EXTI3_IRQHandler(void) +{ + rt_interrupt_enter(); + HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3); + rt_interrupt_leave(); +} + +void EXTI4_IRQHandler(void) +{ + rt_interrupt_enter(); + HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4); + rt_interrupt_leave(); +} + +void EXTI9_5_IRQHandler(void) +{ + rt_interrupt_enter(); + HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5); + HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6); + HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_7); + HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_8); + HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_9); + rt_interrupt_leave(); +} + +void EXTI15_10_IRQHandler(void) +{ + rt_interrupt_enter(); + HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_10); + HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_11); + HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12); + HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13); + HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_14); + HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15); + rt_interrupt_leave(); +} + +int rt_hw_pin_init(void) +{ +#if defined(__HAL_RCC_GPIOA_CLK_ENABLE) + __HAL_RCC_GPIOA_CLK_ENABLE(); +#endif + +#if defined(__HAL_RCC_GPIOB_CLK_ENABLE) + __HAL_RCC_GPIOB_CLK_ENABLE(); +#endif + +#if defined(__HAL_RCC_GPIOC_CLK_ENABLE) + __HAL_RCC_GPIOC_CLK_ENABLE(); +#endif + +#if defined(__HAL_RCC_GPIOE_CLK_ENABLE) + __HAL_RCC_GPIOE_CLK_ENABLE(); +#endif + + return RT_EOK; +} + +#endif /* RT_USING_PIN */ diff --git a/rt-thread-version/rt-thread-nano/nano-ref/nano-device-pin/demo_code/drv_gpio.h b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-pin/demo_code/drv_gpio.h new file mode 100644 index 0000000000000000000000000000000000000000..2d01509b02313d2897211cc64e859e6b0f8e0234 --- /dev/null +++ b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-pin/demo_code/drv_gpio.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-11-06 balanceTWK first version + * 2020-06-16 thread-liu add stm32mp1 + * 2020-09-01 thread-liu add GPIOZ + * 2020-09-18 geniusgogo optimization design pin-index algorithm + */ + +#ifndef __DRV_GPIO_H__ +#define __DRV_GPIO_H__ + +#include +#include +#include "pin.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define __STM32_PORT(port) GPIO##port##_BASE + +#define GET_PIN(PORTx,PIN) (rt_base_t)((16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA_BASE)/(0x0400UL) )) + PIN) + +struct pin_irq_map +{ + rt_uint16_t pinbit; + IRQn_Type irqno; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __DRV_GPIO_H__ */ + diff --git a/rt-thread-version/rt-thread-nano/nano-ref/nano-device-pin/nano-device-pin.md b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-pin/nano-device-pin.md new file mode 100644 index 0000000000000000000000000000000000000000..c038f1f2c46f46736246f4d87f703fd9f40c5387 --- /dev/null +++ b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-pin/nano-device-pin.md @@ -0,0 +1,197 @@ +# 在 RT-Thread Studio 上使用 RT-Thread Nano 并使用PIN设备接口 + +本文介绍了如何在 RT-Thread Studio 上使用 RT-Thread Nano,并基于BearPI-IOT STM32L431RCT6的基础工程进行讲解如何使用PIN设备接口及相关软件包使用。 + +## 为什么需要设备接口 +1. RT-Thread 分为标准版本和 Nano 版本,其特点如下: + - RT-Thread 标准版:拥有设备驱动框架,软件包等组件,软件包都是基于设备驱动接口来实现。 + - RT-Thread Nano:仅仅只是一个 RTOS 内核。没有任何组件。 +2. Nano 是无法直接使用 RT-Thread 丰富软件包功能。 +3. Nano 是一个面向低资源的 MCU 等芯片,不可能增加如同标准版的设备驱动框架。 +4. Nano 需要一套统一设备驱动 API ,屏蔽不同芯片的 HAL 层的区别。方便移植工程到不同的平台。 +4. Nano 需要一套设备驱动 API ,可以方便使用丰富软件包组件。 + +## 准备工作 +1. 使用 RT-Thread Studio 建立一个 STM32L431RCT6 的 RT-Thread Nano 基础工程。 +2. 基础工程创建可参考:[在 RT-Thread Studio 上使用 RT-Thread Nano](../../nano-port-studio/an0047-nano-port-studio.md) + +## PIN 设备接口 + +1. 在 RT-Thread 标准版中,[PIN设备](../../../rt-thread-standard/programming-manual/device/pin/pin.md)设备提供了一套设备管理接口来访问 GPIO,用户程序可以直接使用该 API 操作 GPIO 的功能,设备管理接口如下: + +| **函数** | **描述** | +| ---------------- | ---------------------- | +| rt_pin_get() | 获取引脚编号 | +| rt_pin_mode() | 设置引脚模式 | +| rt_pin_write() | 设置引脚电平 | +| rt_pin_read() | 读取引脚电平 | +| rt_pin_attach_irq() | 绑定引脚中断回调函数 | +| rt_pin_irq_enable() | 使能引脚中断 | +| rt_pin_detach_irq() | 脱离引脚中断回调函数 | + +2. RT-Thread 丰富软件包都是基于这套 API 进行开发适配,所以 Nano 也需要一套这样子的 API。在 PIN 设备接口,可以完全沿用标准版的这套API。 + +## 适配 PIN 设备接口 + +1. 复制 RT-Thread 完整版工程中的 pin.h 文件(路径:rt-thread\components\drivers\include\drivers\pin.h)到我们准备好的 STM32L431RCT6 的 RT-Thread Nano 基础工程中。 + +2. 由于 RT-Thread Nano 没有驱动框架,所以我们要把 pin.h 中有关完整版的内容去掉。整理完之后的 pin.h 文件如下: + +``` C +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-08-21 RiceChen the first version + */ + +#ifndef PIN_H__ +#define PIN_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PIN_LOW 0x00 +#define PIN_HIGH 0x01 + +#define PIN_MODE_OUTPUT 0x00 +#define PIN_MODE_INPUT 0x01 +#define PIN_MODE_INPUT_PULLUP 0x02 +#define PIN_MODE_INPUT_PULLDOWN 0x03 +#define PIN_MODE_OUTPUT_OD 0x04 + +#define PIN_IRQ_MODE_RISING 0x00 +#define PIN_IRQ_MODE_FALLING 0x01 +#define PIN_IRQ_MODE_RISING_FALLING 0x02 +#define PIN_IRQ_MODE_HIGH_LEVEL 0x03 +#define PIN_IRQ_MODE_LOW_LEVEL 0x04 + +#define PIN_IRQ_DISABLE 0x00 +#define PIN_IRQ_ENABLE 0x01 + +#define PIN_IRQ_PIN_NONE -1 + +struct rt_device_pin_mode +{ + rt_uint16_t pin; + rt_uint16_t mode; +}; +struct rt_device_pin_status +{ + rt_uint16_t pin; + rt_uint16_t status; +}; +struct rt_pin_irq_hdr +{ + rt_int16_t pin; + rt_uint16_t mode; + void (*hdr)(void *args); + void *args; +}; + +void rt_pin_mode(rt_base_t pin, rt_base_t mode); +void rt_pin_write(rt_base_t pin, rt_base_t value); +int rt_pin_read(rt_base_t pin); +rt_err_t rt_pin_attach_irq(rt_int32_t pin, rt_uint32_t mode, + void (*hdr)(void *args), void *args); +rt_err_t rt_pin_detach_irq(rt_int32_t pin); +rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint32_t enabled); +/* Get pin number by name,such as PA.0,P0.12 */ +rt_base_t rt_pin_get(const char *name); + +#ifdef __cplusplus +} +#endif + +#endif +``` + +3. 我们需要适配如上7个 PIN 设备 API ,参考实例:[drv_gpio.c](./demo_code/drv_gpio.c) 和 [drv_gpio.h](./demo_code/drv_gpio.h)。 + +## 编写 PIN 设备使用示例 + +``` C + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-08-21 RT-Thread first version + */ + +#include + +#define DBG_TAG "main" +#define DBG_LVL DBG_LOG +#include + +#include "drv_gpio.h" +#define LED0_PIN GET_PIN(C, 13) + +int main(void) +{ + LOG_D("Hello RT-Thread!"); + rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT); + + for(;;) + { + rt_pin_write(LED0_PIN, PIN_HIGH); + rt_thread_mdelay(500); + rt_pin_write(LED0_PIN, PIN_LOW); + rt_thread_mdelay(500); + } + + return RT_EOK; +} +``` + +- 实例代码运行现象:板载的 LED 进行周期性闪烁。 + +## PIN 设备相关软件包使用 + +1. 我们使用[LedBlink软件包](http://packages.rt-thread.org/detail.html?package=LedBlink)来验证PIN设备接口。 + +2. 首先克隆 LedBlink 软件包到 STM32L431RCT6 的 RT-Thread Nano 工程。LedBlink 软件包链接:https://github.com/aeo123/LedBlink.git + +3. 使用 LedBlink 软件包实例: + +``` C +#include + +#define DBG_TAG "main" +#define DBG_LVL DBG_LOG +#include + +#include "drv_gpio.h" +#include "ledblink.h" +#define LED0_PIN GET_PIN(C, 13) + +int main(void) +{ + int count = 1; + + LOG_D("Hello RT-Thread!"); + led_add_device(LED0_PIN); + + while (count++) + { + led_on(1); + rt_thread_mdelay(500); + led_off(1); + rt_thread_mdelay(500); + } + + return RT_EOK; +} +``` +4. LedBlink 软件包无需任何修改便可在 RT-Thread Nano 工程中使用。 + +5. 实例代码运行现象:板载的 LED 进行周期性闪烁。 +