[OK210開發板體驗]功能篇(5)Linux字符驅動之PWM蜂鳴器驅動

原創 2015-12-14 10:05:00 [OK210開發板體驗]功能篇(5)Linux字符驅動之PWM蜂鳴器驅動
前面進行了OK210開發板體驗的入門篇介紹,算是初步入門,分別包含:
[OK210開發板體驗]入門篇(1):開箱驗板
[OK210開發板體驗]入門篇(2):板載資源
[OK210開發板體驗]入門篇(3):開發環境(軟件安裝,開發環境,燒寫系統)
[OK210開發板體驗]入門篇(4):編程入門(NFS登錄,驅動入門)
[OK210開發板體驗]功能篇(1):Linux字符驅動之Led
[OK210開發板體驗]功能篇(2):Linux字符驅動之Key按鍵
[OK210開發板體驗]功能篇(3):Linux Input子系統之Key按鍵
[OK210開發板體驗]功能篇(4):Linux字符驅動之DS18B20

今天是功能篇的第五篇:Linux字符驅動之PWM蜂鳴器,本節主要分3部分:硬件分析,軟件基礎,驅動編程。
一、硬件分析
[OK210開發板體驗]的第二篇:板載資源中,簡單分析了蜂鳴器的
功能和作用。其實對蜂鳴器的操作,主要是通過S5PV210的PWM來實現的,因為在OK210上,連接的是一個無源蜂鳴器,必須通過外部的驅動信號,才能控制其的“鳴叫”。
首先從OK210的底板原理圖中可知,OK210開發板上的DS18B20連接通過一個三極管組成的放大電路連接到了核心板的XpwmTOUT[0]引腳上,如下圖所示:
 
XpwmTOUT[0]引腳由S5PV210用戶手冊,可知,該引腳位于GPD0[0]引腳上,默認為GPI,即當作通用輸入端口使用,如下圖所示:
 
但的第一功能名為TOUT_0,繼續查閱,可知,該功能可作為PWM輸出使用,如下所示,
 

所以,我們要對蜂鳴器進行操作,就是通過對XpwmTOUT[0]引腳的設置,即將其設置為TOUT_0功能,通過配置PWM的波形來實現蜂鳴器的鳴叫。
二、軟件基礎
如上所述,無源蜂鳴器沒有自帶震蕩電路,必須外部提供2-5Khz左右的方波,才能驅動其發聲,而要想產生方波,就會用到S5PV210的PWM模塊。
S5PV210共有5個32bit的PWM定時器,其中定時器0、1、2、3有PWM功能,定時器4沒有輸出引腳。這些定時器都可產生中斷。每個定時器可選擇輸入時鐘為PCLK或SCLK_PWM。對PWM的操作,主要通過幾個寄存器來完成,操作步驟如下:
1、設置TCFG0寄存器:配置定時器的一級分頻值
2、設置TCFG1寄存器:配置定時器的二級分頻值
3、設置TCNTBn寄存器:遞減計數器緩沖寄存器
4、設置TCMPBn寄存器:比較緩沖寄存器
5、設置TCON寄存器:
(1)手動更新on(執行后,CPU會把TCNTBn的值加載到遞減計數器中)
(2)手動更新off、自動重載、啟動定時器
不過在Linux內核中,三星公司和飛凌公司已經為我們配置好了對這些PWM模塊的使用,具體參見源碼目錄下
 arch/arm/plat-s3c/的pwm.c文件,在驅動編程中,主要使用pwm_config()、pwm_enable()、pwm_disable(pwm4buzzer)這三個函數。另外,也可參見另一篇博文【S5PV210 PWM】。
三 驅動編程
有圖有真相,按照如下運行后,即可聽到“鳴叫”。

1 驅動程序
  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/init.h>
  4. #include <linux/platform_device.h>
  5. #include <linux/fb.h>
  6. #include <linux/backlight.h>
  7. #include <linux/err.h>
  8. #include <linux/pwm.h>
  9. #include <linux/slab.h>
  10. #include <linux/miscdevice.h>
  11. #include <linux/delay.h>
  12. #include <mach/gpio.h>
  13. #include <mach/regs-gpio.h>
  14. #include <plat/gpio-cfg.h>

  15. #define DEVICE_NAME                                "pwm"
  16. #define PWM_IOCTL_SET_FREQ                1
  17. #define PWM_IOCTL_STOP                        0
  18. #define NS_IN_1HZ                                (1000000000UL)
  19. #define BUZZER_PWM_ID                        0
  20. #define BUZZER_PMW_GPIO                        S5PV210_GPD0(0)

  21. static struct pwm_device *pwm4buzzer;
  22. static struct semaphore lock;

  23. static void pwm_set_freq(unsigned long freq) {
  24.         int period_ns = NS_IN_1HZ / freq;
  25.         pwm_config(pwm4buzzer, period_ns / 2, period_ns);
  26.         pwm_enable(pwm4buzzer);
  27. }

  28. static void pwm_stop(void) {
  29.         pwm_config(pwm4buzzer, 0, NS_IN_1HZ / 100);
  30.         pwm_disable(pwm4buzzer);
  31. }


  32. static int my_pwm_open(struct inode *inode, struct file *file) {
  33.  , ; &, nbsp;     if (!down_trylock(&lock))
  34.                 return 0;
  35.         else
  36.                 return -EBUSY;
  37. }

  38. static int my_pwm_close(struct inode *inode, struct file *file) {
  39.         up(&lock);
  40.         return 0;
  41. }

  42. static long my_pwm_ioctl(struct file *filep, unsigned int cmd,
  43.                 unsigned long arg)
  44. {
  45.         switch (cmd) {
  46.                 case PWM_IOCTL_SET_FREQ:
  47.                         if (arg == 0)
  48.                                 return -EINVAL;
  49.                         pwm_set_freq(arg);
  50.                         break;

  51.                 case PWM_IOCTL_STOP:
  52.                 default:
  53.                         pwm_stop();
  54.                         break;
  55.         }

  56.         return 0;
  57. }


  58. static struct file_operations my_pwm_ops = {
  59.         .owner                        = THIS_MODULE,
  60.         .open                        = my_pwm_open,
  61.         .release                = my_pwm_close,
  62.         .unlocked_ioctl        = my_pwm_ioctl,
  63. };

  64. static struct miscdevice my_misc_dev = {
  65.         .minor = MISC_DYNAMIC_MINOR,
  66.         .name = DEVICE_NAME,
  67.         .fops = &my_pwm_ops,
  68. };

  69. static int __init my_pwm_dev_init(void) {
  70.         int ret;
  71.     printk(DEVICE_NAME " my_pwm_dev_init\n");
  72.         ret = gpio_request(BUZZER_PMW_GPIO, DEVICE_NAME);
  73.         if (ret) {
  74.                 printk("request GPIO %d for pwm failed\n", BUZZER_PMW_GPIO);
  75.                 return ret;
  76.         }

  77.         gpio_set_value(BUZZER_PMW_GPIO, 0);
  78.         s3c_gpio_cfgpin(BUZZER_PMW_GPIO, S3C_GPIO_OUTPUT);
  79.         pwm4buzzer = pwm_request(BUZZER_PWM_ID, DEVICE_NAME);
  80.         if (IS_ERR(pwm4buzzer)) {
  81.                 printk("request pwm %d for %s failed\n", BUZZER_PWM_ID, DEVICE_NAME);
  82.                 return -ENODEV;
  83.         }

  84.         pwm_stop();
  85.         s3c_gpio_cfgpin(BUZZER_PMW_GPIO, S3C_GPIO_SFN(2));
  86.         gpio_free(BUZZER_PMW_GPIO);
  87.         init_MUTEX(&lock);
  88.         ret = misc_register(&my_misc_dev);
  89.         return ret;
  90. }

  91. static void __exit my_pwm_dev_exit(void) {
  92.     printk(DEVICE_NAME " my_pwm_dev_exit\n");
  93.         pwm_stop();
  94.         misc_deregister(&my_misc_dev);
  95. }

  96. module_init(my_pwm_dev_init);
  97. module_exit(my_pwm_dev_exit);

  98. MODULE_LICENSE("GPL");
  99. MODULE_AUTHOR("gjianw217@163.com");
  100. MODULE_DESCRIPTION("PWM Driver");
復制代碼
2 應用程序
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <sys/ioctl.h>
  7. #include <fcntl.h>
  8. #define PWM_IOCTL_SET_FREQ 1
  9. #define PWM_IOCTL_STOP 0

  10. int main(int argc ,char* argv[])
  11. {
  12.    int m_fd=0;//
  13.    m_fd = open("/dev/pwm", O_RDONLY);
  14.    int freq=1000;
  15.    if(argc>1)
  16.    freq=atoi(argv[1]);
  17.    printf("%d \t\t",freq);
  18.    ioctl(m_fd, PWM_IOCTL_STOP);
  19.    ioctl(m_fd, PWM_IOCTL_SET_FREQ,freq);
  20.    getchar();
  21.    ioctl(m_fd, PWM_IOCTL_STOP);
  22.    close(m_fd);
  23.    return 0;
  24. }
復制代碼
3 Makefile文件
  1. #pwm Makefile
  2. ARCH=arm
  3. CROSS_COMPILE=/home/ok210/arm-2009q3/bin/arm-none-linux-gnueabi-
  4. APP_COMPILE=/home/ok210/arm-2009q3/bin/arm-none-linux-gnueabi-
  5. #obj-m := app-drv.o
  6. obj-m := pwm-drv.o
  7. #KDIR := /path/to/kernel/linux/
  8. KDIR := /home/ok210/android-kernel-samsung-dev/
  9. PWD := $(shell pwd)
  10. default:
  11.         make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
  12. app:pwm-app.c
  13.         $(APP_COMPILE)gcc -o app pwm-app.c
  14. clean:
  15.         $(MAKE) -C $(KDIR) M=$(PWD) clean
復制代碼

相關產品 >

  • OKMX6UL-C開發板

    飛凌嵌入式專注imx6系列imx6ul開發板、飛思卡爾imx6ul核心板等ARM嵌入式核心控制系統研發、設計和生產,i.mx6UL系列產品現已暢銷全國,作為恩智浦imx6ul,imx6ul開發板,i.mx6提供者,飛凌嵌入式提供基于iMX6 iMX6UL解決方案定制。

    了解詳情
    OKMX6UL-C開發板
  • OKMX6ULL-C開發板

    40*29mm,雙網雙CAN,8路串口| i.MX6ULL開發板是基于NXP i.MX6ULL設計開發的的一款Linux開發板 ,主頻800MHz,體積小,其核心板僅40*29mm,采用板對板連接器,適應場景豐富。 了解詳情
    OKMX6ULL-C開發板

推薦閱讀 換一批 換一批