AT9G45的pwm,头文件
#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 AT91C_PWMC_CPRE_MCKB 0xC /**< (PWMC_CH) */ #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__) #endif /* AT91_PWM_H */
#include "at91_pwm.h" 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; SANITY_CHECK(frequency < mck); // 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) { trace_LOG(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) { SANITY_CHECK(prescaler < PWM_CPRE_CLKB); SANITY_CHECK((alignment & ~(1 << PWM_CALG)) == 0); SANITY_CHECK((polarity & ~(1<<PWM_CPOL)) == 0); // Disable channel PWM_DIS |= (1 << channel); // Configure channel 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; // 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; } //------------------------------------------------------------------------------ /// 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) { // If channel is disabled, write to CPRD if ((PWM_SR & (1 << channel)) == 0) { PWM_CPRD0 = period; } // Otherwise use update register else { PWM_CMR0 |= (1 << PWM_CPD); 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); 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 if ((PWM_SR & (1 << channel)) == 0) { PWM_CDTY0 = duty; } // Otherwise use update register else { PWM_CMR0 &= ~(1 << PWM_CPD); 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; } //------------------------------------------------------------------------------ /// Disables the given PWM channel. /// \param channel Channel number. //------------------------------------------------------------------------------ void PWMC_DisableChannel(unsigned char channel) { 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; } //------------------------------------------------------------------------------ /// Disables the period interrupt for the given PWM channel. /// \param channel Channel number. //------------------------------------------------------------------------------ void PWMC_DisableChannelIt(unsigned char channel) { PWM_IDR |= 1 << channel; }
#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" #define DEVICE_NAME "mypwm" static int led_major = 0; static int led_minor = 0; struct cdev *led_device; 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, }; 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, &led_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 "at91_ioctl.h" int main(int argc, char** argv) { int fp; fp = open("/dev/mypwm", 0); if(fp < 0) { printf("Open error!\n"); return -1; } ioctl(fp, atoi(argv[1])); close(fp); return 0; }