头文件:
#ifndef AT91_PWM_H #define AT91_PWM_H #define PWMC_BASE 0xfffb8000 /**************** pwm模式寄存器偏移0x00 *****************/ #define PWM_MR_OFF 0x00000000 ///< PWM Mode Register offset. #define PWM_MR (*((volatile unsigned long *)(PWMC_BASE + PWM_MR_OFF))) ///< PWM Mode Register. #define PWM_MR_DIVA_MASK 0x000000FF ///< PWM Mode Divide factor A Mask. #define PWM_MR_DIVA_SHIFT 0 ///< PWM Mode Divide factor A LSB. #define PWM_MR_DIVB_MASK 0x00FF0000 ///< PWM Mode Divide factor B Mask. #define PWM_MR_DIVB_SHIFT 16 ///< PWM Mode Divide factor B LSB. #define PWM_MR_PREA_MASK 0x00000F00 ///< PWM Mode prescaler A Mask. #define PWM_MR_PREA_SHIFT 8 ///< PWM Mode prescaler A LSB. #define PWM_MR_PREB_MASK 0x0F000000 ///< PWM Mode prescaler B Mask. #define PWM_MR_PREB_SHIFT 24 ///< PWM Mode prescaler B LSB. #define PWM_MR_PRE_MCK 0 ///< PWM Mode prescaler set to MCK. #define PWM_MR_PRE_MCK_DIV2 1 ///< PWM Mode prescaler set to MCK/2. #define PWM_MR_PRE_MCK_DIV4 2 ///< PWM Mode prescaler set to MCK/4. #define PWM_MR_PRE_MCK_DIV8 3 ///< PWM Mode prescaler set to MCK/8. #define PWM_MR_PRE_MCK_DIV16 4 ///< PWM Mode prescaler set to MCK/16. #define PWM_MR_PRE_MCK_DIV32 5 ///< PWM Mode prescaler set to MCK/32. #define PWM_MR_PRE_MCK_DIV64 6 ///< PWM Mode prescaler set to MCK/64. #define PWM_MR_PRE_MCK_DIV128 7 ///< PWM Mode prescaler set to MCK/128. #define PWM_MR_PRE_MCK_DIV256 8 ///< PWM Mode prescaler set to MCK/256. #define PWM_MR_PRE_MCK_DIV512 9 ///< PWM Mode prescaler set to MCK/512. #define PWM_MR_PRE_MCK_DIV1024 10 ///< PWM Mode prescaler set to MCK/1024. #define PWM_CHID_MASK 0x0000000F #define PWM_CHID0 0 #define PWM_CHID1 1 #define PWM_CHID2 2 #define PWM_CHID3 3 /*********************** pwm使能寄存器偏移0x04 *****************/ #define PWM_ENA_OFF 0x00000004 ///< PWM Enable Register offset. #define PWM_ENA (*((volatile unsigned long *)(PWMC_BASE + PWM_ENA_OFF))) ///< PWM Enable Register. /*********************** pwm去使能寄存器0x08 *****************/ #define PWM_DIS_OFF 0x00000008 ///< PWM Disable Register offset. #define PWM_DIS (*((volatile unsigned long *)(PWMC_BASE + PWM_DIS_OFF))) ///< PWM Disable Register. /********************* pwm状态寄存器0x0C *****************/ #define PWM_SR_OFF 0x0000000C ///< PWM Status Register offset. #define PWM_SR (*((volatile unsigned long *)(PWMC_BASE + PWM_SR_OFF))) ///< PWM Status Register. /********************** pwm中断使能寄存器0x10 *****************/ #define PWM_IER_OFF 0x00000010 ///< PWM Interrupt Enable Register offset. #define PWM_IER (*((volatile unsigned long *)(PWMC_BASE + PWM_IER_OFF))) ///< PWM Interrupt Enable Register. /********************** pwm中断去使能寄存器0x14 *****************/ #define PWM_IDR_OFF 0x00000014 ///< PWM Interrupt Disable Register offset. #define PWM_IDR (*((volatile unsigned long *)(PWMC_BASE + PWM_IDR_OFF))) ///< PWM Interrupt Disable Register. /********************** pwm中断掩码寄存器0x18 *****************/ #define PWM_IMR_OFF 0x00000018 ///< PWM Interrupt Mask Register offset. #define PWM_IMR (*((volatile unsigned long *)(PWMC_BASE + PWM_IMR_OFF))) ///< PWM Interrupt Mask Register. /********************** pwm中断状态寄存器0x1c *****************/ #define PWM_ISR_OFF 0x0000001C ///< PWM Interrupt Status Register offset. #define PWM_ISR (*((volatile unsigned long *)(PWMC_BASE + PWM_ISR_OFF))) ///< PWM Interrupt Status Register. /********************** pwm通道模式寄存器 0x200+ch_num*0x20+0x00 *****************/ #define PWM_CH0_OFF 0x00000200 ///< PWM Channel 0 registers offset. #define PWM_CH1_OFF 0x00000220 ///< PWM Channel 1 registers offset. #define PWM_CH2_OFF 0x00000240 ///< PWM Channel 2 registers offset. #define PWM_CH3_OFF 0x00000260 ///< PWM Channel 3 registers offset. #define PWM_CMR_OFF 0x00000000 ///< PWM Channel Mode Register offset. #define PWM_CMR0 (*((volatile unsigned long *)(PWMC_BASE + PWM_CMR_OFF + PWM_CH0_OFF))) ///< PWM Channel 0 Mode Register. #define PWM_CMR1 (*((volatile unsigned long *)(PWMC_BASE + PWM_CMR_OFF + PWM_CH1_OFF))) ///< PWM Channel 1 Mode Register. #define PWM_CMR2 (*((volatile unsigned long *)(PWMC_BASE + PWM_CMR_OFF + PWM_CH2_OFF))) ///< PWM Channel 2 Mode Register. #define PWM_CMR3 (*((volatile unsigned long *)(PWMC_BASE + PWM_CMR_OFF + PWM_CH3_OFF))) ///< PWM Channel 3 Mode Register. #define PWM_CPRE_MCK_MASK 0x0000000F ///< PWM Mode prescaler mask. #define PWM_CPRE_MCK 0 ///< PWM Mode prescaler set to MCK. #define PWM_CPRE_MCK_DIV2 1 ///< PWM Mode prescaler set to MCK/2. #define PWM_CPRE_MCK_DIV4 2 ///< PWM Mode prescaler set to MCK/4. #define PWM_CPRE_MCK_DIV8 3 ///< PWM Mode prescaler set to MCK/8. #define PWM_CPRE_MCK_DIV16 4 ///< PWM Mode prescaler set to MCK/16. #define PWM_CPRE_MCK_DIV32 5 ///< PWM Mode prescaler set to MCK/32. #define PWM_CPRE_MCK_DIV64 6 ///< PWM Mode prescaler set to MCK/64. #define PWM_CPRE_MCK_DIV128 7 ///< PWM Mode prescaler set to MCK/128. #define PWM_CPRE_MCK_DIV256 8 ///< PWM Mode prescaler set to MCK/256. #define PWM_CPRE_MCK_DIV512 9 ///< PWM Mode prescaler set to MCK/512. #define PWM_CPRE_MCK_DIV1024 10 ///< PWM Mode prescaler set to MCK/1024. #define PWM_CPRE_CLKA 11 ///< PWM Mode prescaler set to CLKA. #define PWM_CPRE_CLKB 12 ///< PWM Mode prescaler set to CLKB. #define PWM_CALG 8 ///< PWM Mode channel alignment. #define PWM_CPOL 9 ///< PWM Mode channel polarity. #define PWM_CPD 10 ///< PWM Mode channel update period. #define PWM_CDTY_OFF 0x00000004 ///< PWM Channel Duty Cycle Register offset. #define PWM_CDTY0 (*((volatile unsigned long *)(PWMC_BASE + PWM_CDTY_OFF + PWM_CH0_OFF))) ///< PWM Channel 0 Duty Cycle Register. #define PWM_CDTY1 (*((volatile unsigned long *)(PWMC_BASE + PWM_CDTY_OFF + PWM_CH1_OFF))) ///< PWM Channel 1 Duty Cycle Register. #define PWM_CDTY2 (*((volatile unsigned long *)(PWMC_BASE + PWM_CDTY_OFF + PWM_CH2_OFF))) ///< PWM Channel 2 Duty Cycle Register. #define PWM_CDTY3 (*((volatile unsigned long *)(PWMC_BASE + PWM_CDTY_OFF + PWM_CH3_OFF))) ///< PWM Channel 3 Duty Cycle Register. #define PWM_CPRD_OFF 0x00000008 ///< PWM Channel Period Register offset. #define PWM_CPRD0 (*((volatile unsigned long *)(PWMC_BASE + PWM_CPRD_OFF + PWM_CH0_OFF))) ///< PWM Channel 0 Period Register. #define PWM_CPRD1 (*((volatile unsigned long *)(PWMC_BASE + PWM_CPRD_OFF + PWM_CH1_OFF))) ///< PWM Channel 1 Period Register. #define PWM_CPRD2 (*((volatile unsigned long *)(PWMC_BASE + PWM_CPRD_OFF + PWM_CH2_OFF))) ///< PWM Channel 2 Period Register. #define PWM_CPRD3 (*((volatile unsigned long *)(PWMC_BASE + PWM_CPRD_OFF + PWM_CH3_OFF))) ///< PWM Channel 3 Period Register. #define PWM_CCNT_OFF 0x0000000C ///< PWM Channel Counter Register offset. #define PWM_CCNT0 (*((volatile unsigned long *)(PWMC_BASE + PWM_CCNT_OFF + PWM_CH0_OFF))) ///< PWM Channel 0 Counter Register.00204 #define PWM_CCNT1 (*((volatile unsigned long *)(PWMC_BASE + PWM_CCNT_OFF + PWM_CH1_OFF))) ///< PWM Channel 1 Counter Register. #define PWM_CCNT2 (*((volatile unsigned long *)(PWMC_BASE + PWM_CCNT_OFF + PWM_CH2_OFF))) ///< PWM Channel 2 Counter Register. #define PWM_CCNT3 (*((volatile unsigned long *)(PWMC_BASE + PWM_CCNT_OFF + PWM_CH3_OFF))) ///< PWM Channel 3 Counter Register. #define PWM_CUPD_OFF 0x00000010 ///< PWM Channel Update Register offset. #define PWM_CUPD0 (*((volatile unsigned long *)(PWMC_BASE + PWM_CUPD_OFF + PWM_CH0_OFF))) ///< PWM Channel 0 Update Register. #define PWM_CUPD1 (*((volatile unsigned long *)(PWMC_BASE + PWM_CUPD_OFF + PWM_CH1_OFF))) ///< PWM Channel 1 Update Register. #define PWM_CUPD2 (*((volatile unsigned long *)(PWMC_BASE + PWM_CUPD_OFF + PWM_CH2_OFF))) ///< PWM Channel 2 Update Register. #define PWM_CUPD3 (*((volatile unsigned long *)(PWMC_BASE + PWM_CUPD_OFF + PWM_CH3_OFF))) ///< PWM Channel 3 Update Register. extern void PWMC_ConfigureChannel( unsigned char channel, unsigned int prescaler, unsigned int alignment, unsigned int polarity); extern void PWMC_ConfigureClocks (unsigned int clka, unsigned int clkb, unsigned int mck); void PWMC_SetPeriod(unsigned char channel, unsigned short period); void PWMC_SetDutyCycle(unsigned char channel, unsigned short duty); void PWMC_EnableChannel(unsigned char channel); void PWMC_DisableChannel(unsigned char channel); void PWMC_EnableChannelIt(unsigned char channel); void PWMC_DisableChannelIt(unsigned char channel); unsigned short FindClockConfiguration(unsigned int frequency, unsigned int mck); #define trace_DEBUG 0 #define trace_INFO 1 #define trace_WARNING 2 #define trace_ERROR 3 #define trace_FATAL 4 #define trace_LEVEL 0 #define trace_CONFIGURE(mode, baudrate, mck) { \ const Pin pinsDbgu[] = {PINS_DBGU}; \ PIO_Configure(pinsDbgu, PIO_LISTSIZE(pinsDbgu)); \ DBGU_Configure(mode, baudrate, mck); \ } #define trace_LOG(level, ...) { \ if (level >= trace_LEVEL) { \ printk(__VA_ARGS__); \ } \ } #define ASSERT(condition, ...) { \ if (!(condition)) { \ printk(__VA_ARGS__); \ while (1); \ } \ } #define SANITY_ERROR "Sanity check failed at %s:%d\n\r" #define SANITY_CHECK(condition) ASSERT(condition, SANITY_ERROR, __FILE__, __LINE__) typedef struct { int pwm_freq; //frequency in Hz int pwm_duty; //duty cycle in percent. ex. 50 is 50% int pwm_pulse; //1 for PWM positive pulse, 0 for negative pulse } at91_pwm_arg; #endif /* AT91_PWM_H */
#include "at91_pwm.h" /// PWM frequency in Hz. #define PWM_FREQUENCY 20 /// Maximum duty cycle value. #define MAX_DUTY_CYCLE 50 #define MIN_DUTY_CYCLE 0 #define BOARD_MCK (102400000) //100 MHz #define at91_pwm_read(reg) __raw_readl(reg) #define at91_pwm_write(reg, val) __raw_writel((val), reg) //------------------------------------------------------------------------------ /// Finds a prescaler/divisor couple to generate the desired frequency from /// MCK. /// Returns the value to enter in PWMC_MR or 0 if the configuration cannot be /// met. /// \param frequency Desired frequency in Hz. /// \param mck Master clock frequency in Hz. //------------------------------------------------------------------------------ unsigned short FindClockConfiguration(unsigned int frequency, unsigned int mck) { unsigned int divisors[11] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024}; unsigned char divisor = 0; unsigned int prescaler; //if (frequency < mck) if (frequency < mck) { printk("frequency set error!\n"); return 0; } // Find prescaler and divisor values prescaler = (mck / divisors[divisor]) / frequency; while ((prescaler > 255) && (divisor < 11)) { divisor++; prescaler = (mck / divisors[divisor]) / frequency; } // Return result if (divisor < 11) { printk(trace_DEBUG, "-D- Found divisor=%d and prescaler=%d for freq=%dHz\n\r",divisors[divisor], prescaler, frequency); return prescaler | (divisor << 8); } else { return 0; } } //------------------------------------------------------------------------------ // Global functions //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ /// Configures PWM a channel with the given parameters. /// The PWM controller must have been clocked in the PMC prior to calling this /// function. /// \param channel Channel number. /// \param prescaler Channel prescaler. /// \param alignment Channel alignment. /// \param polarity Channel polarity. //------------------------------------------------------------------------------ void PWMC_ConfigureChannel( unsigned char channel, unsigned int prescaler, unsigned int alignment, unsigned int polarity) { #if 0 SANITY_CHECK(prescaler < PWM_CPRE_CLKB); SANITY_CHECK((alignment & ~(1 << PWM_CALG)) == 0); SANITY_CHECK((polarity & ~(1<<PWM_CPOL)) == 0); #endif //if (prescaler > PWM_CPRE_CLKB) if (prescaler > PWM_CPRE_CLKB) { printk("PWMC_ConfigureChannel: prescaler > AT91C_PWMC_CPRE_MCKB\n"); return; } if (alignment & ~(1 << PWM_CALG)) { printk("PWMC_ConfigureChannel: (alignment & ~AT91C_PWMC_CALG) != 0\n"); return; } if (polarity & ~(1<<PWM_CPOL)) { printk("PWMC_ConfigureChannel: (polarity & ~AT91C_PWMC_CPOL) != 0\n"); return; } // Disable channel //PWM_DIS |= (1 << channel); at91_pwm_write(PWM_DIS, (1 << channel)); // Configure channel //PWM_CMR0 = prescaler | alignment | polarity; at91_pwm_write(PWM_CMR0, prescaler | alignment | polarity); } //------------------------------------------------------------------------------ /// Configures PWM clocks A & B to run at the given frequencies. This function /// finds the best MCK divisor and prescaler values automatically. /// \param clka Desired clock A frequency (0 if not used). /// \param clkb Desired clock B frequency (0 if not used). /// \param mck Master clock frequency. //------------------------------------------------------------------------------ void PWMC_ConfigureClocks(unsigned int clka, unsigned int clkb, unsigned int mck) { unsigned int mode = 0; unsigned int result; #if 0 // Clock A if (clka != 0) { result = FindClockConfiguration(clka, mck); ASSERT(result != 0, "-F- Could not generated the desired PWM frequency (%dHz)\n\r", clka); mode |= result; } // Clock B if (clkb != 0) { result = FindClockConfiguration(clkb, mck); ASSERT(result != 0, "-F- Could not generated the desired PWM frequency (%dHz)\n\r", clkb); mode |= (result << 16); } // Configure clocks trace_LOG(trace_DEBUG, "-D- Setting PWMC_MR = 0x%08X\n\r", mode); //PWM_MR = mode; #endif mode = 0xA2; //CLKA = BOARD_MCK/1024/2 = 50K Hz // Configure clocks printk("-D- Setting PWMC_MR = 0x%08X\n\r", mode); at91_pwm_write(PWM_MR, mode); } //------------------------------------------------------------------------------ /// Sets the period value used by a PWM channel. This function writes directly /// to the CPRD register if the channel is disabled; otherwise, it uses the /// update register CUPD. /// \param channel Channel number. /// \param period Period value. //------------------------------------------------------------------------------ void PWMC_SetPeriod(unsigned char channel, unsigned short period) { int ret; ret = at91_pwm_read(PWM_SR); // If channel is disabled, write to CPRD if ((ret & (1 << channel)) == 0) { //PWM_CPRD0 = period; at91_pwm_write(PWM_CPRD0, period); } // Otherwise use update register else { //PWM_CMR0 |= (1 << PWM_CPD); //PWM_CUPD0 = period; ret = at91_pwm_read(PWM_CMR0); ret |= PWM_CPD; at91_pwm_write(PWM_CMR0, ret); at91_pwm_write(PWM_CUPD0, period); } } //------------------------------------------------------------------------------ /// Sets the duty cycle used by a PWM channel. This function writes directly to /// the CDTY register if the channel is disabled; otherwise it uses the /// update register CUPD. /// Note that the duty cycle must always be inferior or equal to the channel /// period. /// \param channel Channel number. /// \param duty Duty cycle value. //------------------------------------------------------------------------------ void PWMC_SetDutyCycle(unsigned char channel, unsigned short duty) { //SANITY_CHECK(duty <= PWM_CPRD0); int ret; ret = at91_pwm_read(PWM_CPRD0); if (duty > ret) { printk("SANITY_CHECK(duty[%x] = AT91C_BASE_PWMC->PWMC_CH[%d].PWMC_CPRDR[%x])\n", duty, channel, ret); return; } //ASSERT(duty > 0, "-F- Duty cycle value 0 is not permitted on SAM7S chips.\n\r"); //ASSERT((duty > 1) || (PWM_CMR0 & (1 << PWM_CALG)), "-F- Duty cycle value 1 is not permitted in left-aligned mode on SAM7S chips.\n\r"); // If channel is disabled, write to CDTY ret = at91_pwm_read(PWM_SR); if ((ret & (1 << channel)) == 0) { //PWM_CDTY0 = duty; at91_pwm_write(PWM_CDTY0, duty); } // Otherwise use update register else { //PWM_CMR0 &= ~(1 << PWM_CPD); //PWM_CUPD0 = duty; ret = at91_pwm_read(PWM_CMR0); ret &= ~(1 << PWM_CPD); at91_pwm_write(PWM_CMR0, ret); at91_pwm_write(PWM_CUPD0, duty); } } //------------------------------------------------------------------------------ /// Enables the given PWM channel. This does NOT enable the corresponding pin; /// this must be done in the user code. /// \param channel Channel number. //------------------------------------------------------------------------------ void PWMC_EnableChannel(unsigned char channel) { //PWM_ENA |= 1 << channel; at91_pwm_write(PWM_ENA, (1 << channel)); } //------------------------------------------------------------------------------ /// Disables the given PWM channel. /// \param channel Channel number. //------------------------------------------------------------------------------ void PWMC_DisableChannel(unsigned char channel) { //PWM_DIS |= 1 << channel; at91_pwm_write(PWM_DIS, (1 << channel)); } //------------------------------------------------------------------------------ /// Enables the period interrupt for the given PWM channel. /// \param channel Channel number. //------------------------------------------------------------------------------ void PWMC_EnableChannelIt(unsigned char channel) { //PWM_IER |= 1 << channel; at91_pwm_write(PWM_IER, (1 << channel)); } //------------------------------------------------------------------------------ /// Disables the period interrupt for the given PWM channel. /// \param channel Channel number. //------------------------------------------------------------------------------ void PWMC_DisableChannelIt(unsigned char channel) { PWM_IDR |= 1 << channel; at91_pwm_write(PWM_IDR, (1 << channel)); }pwm驱动文件;
#include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> /* everything... */ #include <linux/cdev.h> /* cdev...*/ #include <linux/gpio.h> #include <linux/proc_fs.h> #include <asm/uaccess.h> #include "at91_pwm.h" #include "at91_pwm.c" #include <mach/at91_pmc.h> #define DEVICE_NAME "mypwm" static int led_major = 0; static int led_minor = 0; struct cdev *led_device; void PWMC_EnablePMC(void) { at91_sys_write(AT91_PMC_PCER, (1 < AT91SAM9G45_ID_PWMC)); } static int pwm_configure(int channel, at91_pwm_arg *config) { int prescaler, period, duty; if (config->pwm_freq < 2 || config->pwm_freq > 100000) { printk("PWM Frequency should be: 1Hz freq 100KHz\n"); config->pwm_freq = 2; } prescaler = PWM_CPRE_MCK_DIV1024; period = (BOARD_MCK/1024)/(config->pwm_freq); //100KHz if (config->pwm_duty == 0) { config->pwm_duty = 50; } //duty = at91_pwm.pwm_arg[channel].pwm_duty = (period * config->pwm_duty) / 100; duty = (period * config->pwm_duty) / 100; PWMC_ConfigureChannel(channel, prescaler, 0, 0); PWMC_SetPeriod(channel, period); PWMC_SetDutyCycle(channel, duty); return 0; } static int pwm_start(int channel) { PWMC_EnableChannelIt(channel); PWMC_EnableChannel(channel); return 0; } static int pwm_stop(int channel) { PWMC_DisableChannelIt(channel); PWMC_DisableChannel(channel); return 0; } static int pwm_open(struct inode *inode,struct file *filp) { #if 0 int minor = MINOR(inode->i_rdev); // printk("pwm_open %d\n", minor); if (minor >= PWM_MAX) { return -1; } // printk("pwm_base%d = %x\n", minor, (unsigned int)at91_pwm.pwm_base); filp->private_data = at91_pwm.pwm_base; #endif //at91_set_B_periph(AT91_PIN_PD24, 1); /* enable PWM0 */ //at91_set_gpio_output(AT91_PIN_PD24, 0); //at91_set_gpio_value(AT91_PIN_PD24, 1); PWMC_EnablePMC(); PWMC_ConfigureClocks(PWM_FREQUENCY * MAX_DUTY_CYCLE, 0, BOARD_MCK); return 0; } static int pwm_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { //int ch = MINOR(inode->i_rdev); int ch = 0; if (arg > 4) { return -1; } #if 0 if (ch >= PWM_MAX) { printk("pwm_ioctl ch %d, not exist!\n", ch); return -1; } // printk("pwm_ioctl ch = %d\n", ch); #endif switch (cmd) { case 0: pwm_start(ch); break; case 1: pwm_stop(ch); break; case 2: //at91_pwm.pwm_arg[ch] = *(at91_pwm_arg *)arg; pwm_configure(ch, (at91_pwm_arg *)arg); break; default: printk("pwm_ioctl cmd, Not Support!\n"); break; } return 0; } static struct file_operations pwm_fops = { .open = pwm_open, .ioctl = pwm_ioctl }; /**************************** pwm test ***************************/ #if 0 static void PWM_Set_Freq(int arg) { at91_set_gpio_output(AT91_PIN_PD15, 0); //设置PD15为输出模式 at91_set_gpio_value(AT91_PIN_PD15, 0); //设置PD15输出为0,表示点亮 at91_set_B_periph(AT91_PIN_PD24, 1); /* enable PWM0 */ PWMC_ConfigureChannel(0, 1, 2, 3); PWMC_ConfigureClocks(1000, 500, 10); PWMC_SetPeriod(0, 1); PWMC_SetDutyCycle(0, 50); } static void PWM_Stop(void) { at91_set_gpio_output(AT91_PIN_PD24, 0); at91_set_gpio_value(AT91_PIN_PD24, 1); return ; } static int myled_open(struct inode *inode, struct file *filp) { PWMC_EnableChannel(0);//这一步在测试驱动的时候总是报内存操作错误 at91_set_B_periph(AT91_PIN_PD24, 1);/* enable PWM0 */ return 0; } static int myled_release(struct inode *inode, struct file *filp) { PWMC_DisableChannel(0); //这一步在测试驱动的时候总是报内存操作错误 return 0; } static int myled_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { if (arg > 4) { return -1; } switch (cmd) { case 0: if (arg == 0) return -1; PWM_Set_Freq(arg); break; //通过cmd命令控制PD15 case 1: PWM_Stop(); break; } return 0; } struct file_operations led_fops = { .owner = THIS_MODULE, .open = myled_open, .release = myled_release, .ioctl = myled_ioctl, }; #endif static void __exit my_led_exit(void) { dev_t devno = MKDEV(led_major, led_minor); //通过主次设备号获得设备号 if (led_device) { cdev_del(led_device); //删除设备号 kfree(led_device); //释放设备号空间 led_device = NULL; //设置为空, 防止再次被调用 } unregister_chrdev_region(devno, 1); //卸载主设备号 return; } static int __init my_led_init(void) { int result = 0; //用来返回结果值 dev_t dev = 0; //设备号 /***********************分配主次设备号******************/ result = alloc_chrdev_region(&dev, led_minor, 1, DEVICE_NAME); //自动分配主设备号 led_major = MAJOR(dev); //得到主设备号 if (result < 0) { printk(KERN_WARNING "wfet_kb: can't get major %d\n", led_major); return result; } /*********************注册字符设备*************************/ led_device = kmalloc(sizeof(struct cdev), GFP_KERNEL); //字符设备的全局结构占用的内存要用kmalloc或kzalloc申请 if(!led_device) { result = -ENOMEM; unregister_chrdev_region(dev, 1); return result; } memset(led_device, 0, sizeof(struct cdev)); cdev_init(led_device, &pwm_fops); //字符设备初始化 led_device->owner = THIS_MODULE; result = cdev_add(led_device, dev, 1); //字符设备添加 if(result) { printk(KERN_NOTICE "Error %d adding LED device, major_%d", result, MAJOR(dev)); kfree(led_device); //释放空间 unregister_chrdev_region(dev, 1); //注销设备号 return result; } return 0; } module_init(my_led_init) module_exit(my_led_exit) MODULE_LICENSE("GPL");
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <termios.h> #include <errno.h> #include <fcntl.h> #include <sys/ioctl.h> #include "at91_pwm.h" #define DEV_PWM0 "/dev/mypwm" int main(int argc, void *argv[]) { int fd0 = -1; int i; at91_pwm_arg arg; #if 1 //PWM0 Test printf (" AT91 PWM0 Test ...\n"); fd0 = open(DEV_PWM0, O_RDWR); if (fd0 == 0) { printf("open pwm device error! %d\n", fd0); return -1; } arg.pwm_freq = 1000; arg.pwm_duty = 50; arg.pwm_pulse = 1; ioctl(fd0, 2, &arg); printf("---->2<----ioctl pwm device error! %d\n"); ioctl(fd0, 0, 1); printf("---->0<----ioctl pwm device error! %d\n"); for (i=0; i<3; i++) { sleep(1); } ioctl(fd0, 1, 0); printf("---->1<----ioctl pwm device error! %d\n"); close(fd0); #endif //PWM0 }