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 */
at9g45库文件:
#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;
}
at9g45,驱动文件:
#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");
at9g45测试文件:
#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;
}