硬件環(huán)境
RDC2022 紀(jì)念版開發(fā)板
點亮 LED 燈
使用過 ART-Pi-smart 的應(yīng)該都是從靈魂點燈開始的,關(guān)于 ART-Pi-smart 上的點燈程序驅(qū)動,應(yīng)該都是使用的 rt_device_control rt_device_write 的形式來使用的,當(dāng)然這種方式在 D1s 上也是可以的
下面就是一段 D1s 上的點燈程序,通過用戶態(tài)編譯,燒進開發(fā)板后即可運行
#include
#include
#define GET_PIN(PORTx, PIN) (32 * (PORTx - 1) + (PIN & 31))
#define LED_PIN GET_PIN(7, 15) // PG15
/* 由于用戶態(tài)沒有定義這兩個結(jié)構(gòu)體,所以這里要重新定義一下,這里要和內(nèi)核中的定義一直,否則將可能無法運行*/
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;
}
通過了解上述程序,可以發(fā)現(xiàn) smart 用戶態(tài)程序和 rt-thread 基本沒有區(qū)別,這是由于為了方便用戶上手 smart 已經(jīng)將 rt-thread 部分 api 對接到了用戶態(tài)中,當(dāng)然這種開發(fā)方式并不適用于所有的 rt-thread api,有些還沒有對接完成(就例如下文中提到的按鍵回調(diào)的綁定,由于用戶態(tài)地址和內(nèi)核態(tài)地址不同的原因,內(nèi)核態(tài)并不能直接調(diào)用用戶態(tài)的函數(shù))
這樣的話,又引出了另一種的開發(fā)方式,就是像 linux 一樣,一切皆文件的形式來去開發(fā),用戶態(tài)方面使用 DevFS 設(shè)備管理 去開發(fā)設(shè)備,并且官方只推薦這種方式進行設(shè)備開發(fā)
下面就是使用了 DevFS 進行開發(fā)的程序
這里分為兩個部分
內(nèi)核態(tài)設(shè)備注冊
用戶態(tài)驅(qū)動
LED 內(nèi)核態(tài)設(shè)備注冊
#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 用戶態(tài)驅(qū)動
#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 的內(nèi)核設(shè)備注冊和用戶態(tài)代碼,可是 LED 燈只能隨時間閃動,是不受我們控制的,那么現(xiàn)在我們要使用按鍵來去控制 LED 等了
下面,我們來書寫按鍵的內(nèi)核設(shè)備注冊代碼
KEY 內(nèi)核態(tài)設(shè)備注冊
這里我們使用了 poll 來實現(xiàn)監(jiān)測按鍵是否按下,有興趣的可以查一下 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);
按鍵控制用戶態(tài)驅(qū)動
上述完成了 KEY LED 的內(nèi)核設(shè)備驅(qū)動注冊,那么我們內(nèi)核部分的操作就完成了,下面只需要在用戶態(tài)對這兩個設(shè)備進行 read write 等操作,就可以實現(xiàn)對按鍵和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;
}
評論