硬件環境
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;
}
評論
查看更多