硬件环境
RDC2022 纪念版开发板
点亮 LED 灯
使用过 ART-Pi-smart 的应该都是从灵魂点灯开始的,关于 ART-Pi-smart 上的点灯程序驱动,应该都是使用的 rt_device_control rt_device_write 的形式来使用的,当然这种方式在 D1s 上也是可以的
下面就是一段 D1s 上的点灯程序,通过用户态编译,烧进开发板后即可运行
#include
#include
#define GET_PIN(PORTx, PIN) (32 * (PORTx - 1) + (PIN & 31))
#define LED_PIN GET_PIN(7, 15) // PG15
/* 由于用户态没有定义这两个结构体,所以这里要重新定义一下,这里要和内核中的定义一直,否则将可能无法运行*/
struct rt_device_pin_mode
{
rt_base_t pin;
rt_uint8_t mode;
};
struct rt_device_pin_status
{
rt_base_t pin;
rt_uint8_t status;
};
int main()
{
rt_device_t pin_dev;
struct rt_device_pin_mode pin_mode;
struct rt_device_pin_status pin_status;
pin_dev = rt_device_find("pin");
if (pin_dev == RT_NULL)
{
rt_kprintf("not find pin device!n");
return -1;
}
rt_device_open(pin_dev, RT_DEVICE_OFLAG_RDWR);
pin_mode.pin = LED_PIN;
pin_mode.mode = 0;
rt_device_control(pin_dev, 0, (void *)&pin_mode);
pin_status.pin = LED_PIN;
while (1)
{
pin_status.status = 0;
rt_device_write(pin_dev, 0, (void *)&pin_status, sizeof(pin_status));
rt_thread_mdelay(200);
pin_status.status = 1;
rt_device_write(pin_dev, 0, (void *)&pin_status, sizeof(pin_status));
rt_thread_mdelay(200);
}
rt_device_close(pin_dev);
return 0;
}
通过了解上述程序,可以发现 smart 用户态程序和 rt-thread 基本没有区别,这是由于为了方便用户上手 smart 已经将 rt-thread 部分 api 对接到了用户态中,当然这种开发方式并不适用于所有的 rt-thread api,有些还没有对接完成(就例如下文中提到的按键回调的绑定,由于用户态地址和内核态地址不同的原因,内核态并不能直接调用用户态的函数)
这样的话,又引出了另一种的开发方式,就是像 linux 一样,一切皆文件的形式来去开发,用户态方面使用 DevFS 设备管理 去开发设备,并且官方只推荐这种方式进行设备开发
下面就是使用了 DevFS 进行开发的程序
这里分为两个部分
内核态设备注册
用户态驱动
LED 内核态设备注册
#include
#include
#include
#include
#include
#define GET_PIN(PORTx, PIN) (32 * (PORTx - 1) + (PIN & 31))
#define USER_LED GET_PIN(7, 15) // PG15
static uint8_t is_init;
static uint8_t led_state;
static uint8_t led_state_old;
static rt_device_t device;
static void drv_led_init(void)
{
rt_pin_mode(USER_LED, PIN_MODE_OUTPUT);
rt_pin_write(USER_LED, PIN_HIGH);
}
/* Open the led device, and initialize the hardware the first time you open it */
static int drv_led_open(struct dfs_fd fd)
{
if (!is_init)
{
is_init = 1;
/ Initialize the hardware /
drv_led_init();
}
/ Increase reference count /
device->ref_count ++;
return 0;
}
/ Close the led device, and reset the hardware when the device is no longer in use */
static int drv_led_close(struct dfs_fd fd)
{
/ Reduced reference count /
device->ref_count --;
/ Reset the hardware when the device is no longer in use /
if (device->ref_count == 0)
{
/ ... /
is_init = 0;
}
return 0;
}
/ Read led state */
static int drv_led_write(struct dfs_fd *fd, const void buf, size_t count)
{
if( (int )buf == 0)
rt_pin_write(USER_LED, PIN_HIGH);
else
rt_pin_write(USER_LED, PIN_LOW);
return 1;
}
/
Realize the fops variables.
/
static struct dfs_file_ops drv_led_fops =
{
drv_led_open,
drv_led_close,
RT_NULL,
RT_NULL,
drv_led_write,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
};
/
Key device initialization function.
/
static int rt_hw_led_init(void)
{
rt_err_t ret;
/ 1. Allocates memory for device structures, Use the calloc function /
device = rt_calloc(1, sizeof(struct rt_device));
if (device == RT_NULL)
{
return -1;
}
/ 2. Set to miscellaneous device /
device->type = RT_Device_Class_Miscellaneous;
/ 3. register a led device /
ret = rt_device_register(device, "led0", RT_DEVICE_FLAG_RDONLY);
if (ret != RT_EOK)
{
rt_free(device);
return ret;
}
/ 4. set fops /
device->fops = &drv_led_fops;
return ret;
}
/ Using below macro to export this function, the function will be called automatically after kernel startup */
INIT_DEVICE_EXPORT(rt_hw_led_init);
LED 用户态驱动
#include
#include
#include
#include
#define LED0_DEVICE_PATH "/dev/led0"
int main(void)
{
int led_fd;
int value = 0;
int count = 300;
led_fd = open(LED0_DEVICE_PATH, O_RDWR);
if (led_fd < 0)
{
printf("open device failedn");
return -1;
}
while (1)
{
value = 1;
write(led_fd, &value, 1);
sleep(1);
value = 0;
write(led_fd, &value, 1);
sleep(1);
}
close(led_fd);
return 0;
}
按键控制 LED
上面我们写好了 LED 的内核设备注册和用户态代码,可是 LED 灯只能随时间闪动,是不受我们控制的,那么现在我们要使用按键来去控制 LED 等了
下面,我们来书写按键的内核设备注册代码
KEY 内核态设备注册
这里我们使用了 poll 来实现监测按键是否按下,有兴趣的可以查一下 poll 机制
#include
#include
#include
#include
#include
#define GET_PIN(PORTx, PIN) (32 * (PORTx - 1) + (PIN & 31))
#define USER_KEY GET_PIN(7, 13) // PG13
static uint8_t is_init;
static uint8_t key_state;
static uint8_t key_state_old;
static rt_device_t device;
void irq_callback()
{
/* enter interrupt */
key_state = rt_pin_read(USER_KEY);
rt_interrupt_enter();
rt_wqueue_wakeup(&(device->wait_queue), (void )POLLIN);
/ leave interrupt /
rt_interrupt_leave();
}
static void drv_key_init(void)
{
key_state = 1;
key_state_old = 1;
rt_pin_mode(USER_KEY, PIN_MODE_INPUT);
rt_pin_attach_irq(USER_KEY, PIN_IRQ_MODE_RISING_FALLING, irq_callback, RT_NULL);
rt_pin_irq_enable(USER_KEY, PIN_IRQ_ENABLE);
}
/ Open the key device, and initialize the hardware the first time you open it */
static int drv_key_open(struct dfs_fd fd)
{
if (!is_init)
{
is_init = 1;
/ Initialize the hardware /
drv_key_init();
}
/ Increase reference count /
device->ref_count ++;
return 0;
}
/ Close the key device, and reset the hardware when the device is no longer in use */
static int drv_key_close(struct dfs_fd fd)
{
/ Reduced reference count /
device->ref_count --;
/ Reset the hardware when the device is no longer in use /
if (device->ref_count == 0)
{
/ ... /
is_init = 0;
}
return 0;
}
/ Read key state */
static int drv_key_read(struct dfs_fd *fd, void *buf, size_t count)
{
*(int )buf = !rt_pin_read(USER_KEY);
return 1;
}
/ Use poll to check the state of the key */
static int drv_key_poll(struct dfs_fd *fd, struct rt_pollreq req)
{
int mask = 0;
int flags = 0;
/ only support POLLIN /
flags = fd->flags & O_ACCMODE;
if (flags == O_RDONLY || flags == O_RDWR)
{
/ Add to wait queue, suspend the current thread /
rt_poll_add(&(device->wait_queue), req);
/ If the key is pressed, mark a POLLIN event /
if (key_state != key_state_old)
{
key_state_old = key_state;
mask |= POLLIN;
}
}
return mask;
}
/
- Realize the fops variables.
/
static struct dfs_file_ops drv_key_fops =
{
drv_key_open,
drv_key_close,
RT_NULL,
drv_key_read,
RT_NULL,
RT_NULL,
RT_NULL,
RT_NULL,
drv_key_poll,
};
/ - Key device initialization function.
/
static int rt_hw_key_init(void)
{
rt_err_t ret;
/ 1. Allocates memory for device structures, Use the calloc function /
device = rt_calloc(1, sizeof(struct rt_device));
if (device == RT_NULL)
{
return -1;
}
/ 2. Set to miscellaneous device /
device->type = RT_Device_Class_Miscellaneous;
/ 3. register a key device /
ret = rt_device_register(device, "key0", RT_DEVICE_FLAG_RDONLY);
if (ret != RT_EOK)
{
rt_free(device);
return ret;
}
/ 4. set fops /
device->fops = &drv_key_fops;
return ret;
}
/ Using below macro to export this function, the function will be called automatically after kernel startup */
INIT_DEVICE_EXPORT(rt_hw_key_init);
按键控制用户态驱动
上述完成了 KEY LED 的内核设备驱动注册,那么我们内核部分的操作就完成了,下面只需要在用户态对这两个设备进行 read write 等操作,就可以实现对按键和LED灯的控制了
#include
#include
#include
#include
#define KEY0_DEVICE_PATH "/dev/key0"
#define LED0_DEVICE_PATH "/dev/led0"
int main(void)
{
int key_fd,led_fd;
int value = 0;
struct pollfd fds[1];
/* Open the device by non-blocking mode /
key_fd = open(KEY0_DEVICE_PATH, O_RDONLY | O_NONBLOCK);
led_fd = open(LED0_DEVICE_PATH, O_RDWR);
if (key_fd < 0 || led_fd < 0)
{
printf("open device failedn");
return -1;
}
/ Add the key_fd to monitor /
fds[0].fd = key_fd;
fds[0].events = POLLIN;
while (1)
{
/ Monitor button status, timeout 1S */
if (poll(fds, 1, 1000) > 0 && fds[0].revents & POLLIN)
{
read(key_fd, &value, 1);
write(led_fd, &value, 1);
}
}
close(key_fd);
close(led_fd);
return 0;
}
评论
查看更多