【S3C2440 Linux 按键驱动】-【鹏哥教学】
SPI驱动程序(S3C2440)
2410_SPI接口与linux驱动以下先从下到上的进行分析:driver/spi下有两个底层相关的spi驱动程序:spi_s3c24xx.c和spi_s3c24xx_gpio.c其中spi_s3c24xx.c是基于s3c24xx下相应的spi接口的驱动程序,spi_s3c24xx_gpio.c允许用户指定3个gpio口接口,模拟标准的spi总线。
s3c2410自带了两个spi接口(spi0和spi1),在此我只研究基于s3c2410下spi接口的驱动程序spi_s3c24xx.c。
首先从spi驱动的检测函数进行分析:static int s3c24xx_spi_probe(struct platform_device *pdev){struct s3c24xx_spi *hw;struct spi_master *master;struct spi_board_info *bi;struct resource *res;int err = 0;int i;master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi)); if (master == NULL) {dev_err(&pdev->dev, "No memory for spi_master\n");err = -ENOMEM;goto err_nomem;}hw = spi_master_get_devdata(master);memset(hw, 0, sizeof(struct s3c24xx_spi));hw->master = spi_master_get(master);hw->pdata = pdev->dev.platform_data;hw->dev = &pdev->dev;if (hw->pdata == NULL) {dev_err(&pdev->dev, "No platform data supplied\n");err = -ENOENT;goto err_no_pdata;}platform_set_drvdata(pdev, hw);//dev_set_drvdata(&pdev->dev, hw) init_completion(&hw->done);hw->bitbang.master = hw->master;hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;hw->bitbang.chipselect = s3c24xx_spi_chipsel;hw->bitbang.txrx_bufs = s3c24xx_spi_txrx;hw->bitbang.master->setup = s3c24xx_spi_setup;dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang);res = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (res == NULL) {dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");err = -ENOENT;goto err_no_iores;}hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1, pdev->name);if (hw->ioarea == NULL) {dev_err(&pdev->dev, "Cannot reserve region\n");err = -ENXIO;goto err_no_iores;}hw->regs = ioremap(res->start, (res->end - res->start)+1);if (hw->regs == NULL) {dev_err(&pdev->dev, "Cannot map IO\n");err = -ENXIO;goto err_no_iomap;}hw->irq = platform_get_irq(pdev, 0);if (hw->irq < 0) {dev_err(&pdev->dev, "No IRQ specified\n");err = -ENOENT;goto err_no_irq;}err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw);if (err) {dev_err(&pdev->dev, "Cannot claim IRQ\n");goto err_no_irq;}hw->clk = clk_get(&pdev->dev, "spi");if (IS_ERR(hw->clk)) {dev_err(&pdev->dev, "No clock for device\n");err = PTR_ERR(hw->clk);goto err_no_clk;}clk_enable(hw->clk);writeb(0xff, hw->regs + S3C2410_SPPRE);writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN);writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON);s3c2410_gpio_setpin(S3C2410_GPE13, 0);s3c2410_gpio_setpin(S3C2410_GPE12, 0);s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_SPICLK0); s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_SPIMOSI0); s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_SPIMISO0);if (!hw->pdata->set_cs) {s3c2410_gpio_setpin(hw->pdata->pin_cs, 1);s3c2410_gpio_cfgpin(hw->pdata->pin_cs, S3C2410_GPIO_OUTPUT); }err = spi_bitbang_start(&hw->bitbang);if (err) {dev_err(&pdev->dev, "Failed to register SPI master\n");goto err_register;}dev_dbg(hw->dev, "shutdown=%d\n", hw->bitbang.shutdown);bi = &hw->pdata->board_info[0];for (i = 0; i < hw->pdata->board_size; i++, bi++) {dev_info(hw->dev, "registering %s\n", bi->modalias);bi->controller_data = hw;spi_new_device(master, bi);}return 0;err_register:clk_disable(hw->clk);clk_put(hw->clk);err_no_clk:free_irq(hw->irq, hw);err_no_irq:iounmap(hw->regs);err_no_iomap:release_resource(hw->ioarea);kfree(hw->ioarea);err_no_iores:err_no_pdata:spi_master_put(hw->master);;err_nomem:return err;}struct spi_master * __init_or_modulespi_alloc_master(struct device *dev, unsigned size) {struct spi_master *master;if (!dev)return NULL;master = kzalloc(size + sizeof *master, SLAB_KERNEL); if (!master)return NULL;class_device_initialize(&master->cdev);master->cdev.class = &spi_master_class;master->cdev.dev = get_device(dev);spi_master_set_devdata(master, &master[1]);return master;}int spi_bitbang_start(struct spi_bitbang *bitbang){int status;if (!bitbang->master || !bitbang->chipselect)return -EINVAL;INIT_WORK(&bitbang->work, bitbang_work, bitbang);spin_lock_init(&bitbang->lock);spi_new_device INIT_LIST_HEAD(&bitbang->queue);if (!bitbang->master->transfer)bitbang->master->transfer = spi_bitbang_transfer;//spi数据的传输就是通过调用这个方法来实现的if (!bitbang->txrx_bufs) {bitbang->use_dma = 0;bitbang->txrx_bufs = spi_bitbang_bufs;if (!bitbang->master->setup) {if (!bitbang->setup_transfer)bitbang->setup_transfer =spi_bitbang_setup_transfer;bitbang->master->setup = spi_bitbang_setup;bitbang->master->cleanup = spi_bitbang_cleanup;}} else if (!bitbang->master->setup)return -EINVAL;bitbang->busy = 0;bitbang->workqueue = create_singlethread_workqueue(bitbang->master->cdev.dev->bus_id);if (bitbang->workqueue == NULL) {status = -EBUSY;goto err1;}status = spi_register_master(bitbang->master);if (status < 0)goto err2;return status;err2:destroy_workqueue(bitbang->workqueue);err1:return status;}int __init_or_modulespi_register_master(struct spi_master *master){static atomic_t dyn_bus_id = ATOMIC_INIT((1<<16) - 1);struct device *dev = master->cdev.dev;int status = -ENODEV;int dynamic = 0;if (!dev)return -ENODEV;if (master->bus_num < 0) {master->bus_num = atomic_dec_return(&dyn_bus_id); dynamic = 1;}snprintf(master->cdev.class_id, sizeof master->cdev.class_id, "spi%u", master->bus_num);status = class_device_add(&master->cdev);//注册设备if (status < 0)goto done;dev_dbg(dev, "registered master %s%s\n", master->cdev.class_id, dynamic ? " (dynamic)" : "");scan_boardinfo(master);status = 0;done:return status;}static void __init_or_modulescan_boardinfo(struct spi_master *master){struct boardinfo *bi;struct device *dev = master->cdev.dev;down(&board_lock);list_for_each_entry(bi, &board_list, list) {struct spi_board_info *chip = bi->board_info;unsigned n;for (n = bi->n_board_info; n > 0; n--, chip++) {if (chip->bus_num != master->bus_num)continue;if (chip->chip_select >= master->num_chipselect&& master->num_chipselect) {dev_dbg(dev, "cs%d > max %d\n",chip->chip_select,master->num_chipselect);continue;}(void) spi_new_device(master, chip);}}up(&board_lock);}int __initspi_register_board_info(struct spi_board_info const *info, unsigned n)struct boardinfo *bi;bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);if (!bi)return -ENOMEM;bi->n_board_info = n;memcpy(bi->board_info, info, n * sizeof *info);down(&board_lock);list_add_tail(&bi->list, &board_list);up(&board_lock);return 0;}struct spi_device *__init_or_modulespi_new_device(struct spi_master *master, struct spi_board_info *chip){struct spi_device *proxy;//这个结构很重要,以后就是通过这个结构来操作实际的spi设备的struct device *dev = master->cdev.dev;int status;if (!spi_master_get(master))return NULL;proxy = kzalloc(sizeof *proxy, GFP_KERNEL);if (!proxy) {dev_err(dev, "can't alloc dev for cs%d\n",chip->chip_select);goto fail;}proxy->master = master;proxy->chip_select = chip->chip_select;proxy->max_speed_hz = chip->max_speed_hz;proxy->mode = chip->mode;proxy->irq = chip->irq;proxy->modalias = chip->modalias;snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id,"%s.%u", master->cdev.class_id,chip->chip_select);proxy->dev.parent = dev;proxy->dev.bus = &spi_bus_type;proxy->dev.platform_data = (void *) chip->platform_data; proxy->controller_data = chip->controller_data;proxy->controller_state = NULL;proxy->dev.release = spidev_release;status = master->setup(proxy);if (status < 0) {dev_dbg(dev, "can't %s %s, status %d\n","setup", proxy->dev.bus_id, status);goto fail;}status = device_register(&proxy->dev);//真正注册原始设备if (status < 0) {dev_dbg(dev, "can't %s %s, status %d\n","add", proxy->dev.bus_id, status);goto fail;}dev_dbg(dev, "registered child %s\n", proxy->dev.bus_id); return proxy;fail:spi_master_put(master);kfree(proxy);return NULL;}static int s3c24xx_spi_setup(struct spi_device *spi){int ret;if (!spi->bits_per_word)spi->bits_per_word = 8;if ((spi->mode & SPI_LSB_FIRST) != 0)return -EINVAL;ret = s3c24xx_spi_setupxfer(spi, NULL);if (ret < 0) {dev_err(&spi->dev, "setupxfer returned %d\n", ret);return ret;}dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n",__FUNCTION__, spi->mode, spi->bits_per_word,spi->max_speed_hz);return 0;}static int s3c24xx_spi_setupxfer(struct spi_device *spi,struct spi_transfer *t){struct s3c24xx_spi *hw = to_hw(spi);unsigned int bpw;unsigned int hz;unsigned int div;bpw = t ? t->bits_per_word : spi->bits_per_word;hz = t ? t->speed_hz : spi->max_speed_hz;if (bpw != 8) {dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw);return -EINVAL;}div = clk_get_rate(hw->clk) / hz;div = (div / 2) - 1;//求出预分频值if (div < 0)div = 1;if (div > 255)div = 255;dev_dbg(&spi->dev, "setting pre-scaler to %d (hz %d)\n", div, hz);writeb(div, hw->regs + S3C2410_SPPRE);//设置预分频值spin_lock(&hw->bitbang.lock);if (!hw->bitbang.busy) {hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);//修改时钟,先禁用spi}spin_unlock(&hw->bitbang.lock);return 0;}static void s3c24xx_spi_chipsel(struct spi_device *spi, int value){struct s3c24xx_spi *hw = to_hw(spi);unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;unsigned int spcon;switch (value) {case BITBANG_CS_INACTIVE:if (hw->pdata->set_cs)hw->pdata->set_cs(hw->pdata, value, cspol);elses3c2410_gpio_setpin(hw->pdata->pin_cs, cspol ^ 1);break;case BITBANG_CS_ACTIVE:spcon = readb(hw->regs + S3C2410_SPCON);if (spi->mode & SPI_CPHA)spcon |= S3C2410_SPCON_CPHA_FMTB;elsespcon &= ~S3C2410_SPCON_CPHA_FMTB;if (spi->mode & SPI_CPOL)spcon |= S3C2410_SPCON_CPOL_HIGH;elsespcon &= ~S3C2410_SPCON_CPOL_HIGH;spcon |= S3C2410_SPCON_ENSCK;writeb(spcon, hw->regs + S3C2410_SPCON);if (hw->pdata->set_cs)hw->pdata->set_cs(hw->pdata, value, cspol);elses3c2410_gpio_setpin(hw->pdata->pin_cs, cspol);break;}}好了,至此spi主控制器(驱动)和板上spi设备注册完毕,以后要使用spi来传输数据的话,只要先获得spi设备交道了(就好像你要操作一个文件,先要获取文件句柄一样,明白吧^_^)原文地址/luofuchong/archive/2007/09/24/33942.html~~~~~~~~~~~~~~~~~~``任何SPI都有4种模式2008-11-06 22:17void McBSPObj::McBSP0Init(void)//McBSP从设备SPI硬件配置{//PCR设置过程(FSM=0,CLKM=0)McBSP0->SPSA = PCR;McBSP1->SPSD = (0 << PCR_XIOEN) //发送非通用I/O模式位| (0 << PCR_RIOEN) //接收非通用I/O模式位| (0 << PCR_FSXM) //外部发送帧同步脉冲(外部片选)| (0 << PCR_FSRM) //外部接收帧同步脉冲(外部片选)| (0 << PCR_CLKXM) //外部发送时钟(外部时钟源)| (0 << PCR_CLKRM) //外部接收时钟(外部时钟源)#if SPIMODE == 0//SPI设置过程00(0--FS高电平有效,0--CLK上升沿收发数据)| (0 << PCR_FSXP) //发送帧同步脉冲极性(高电平有效)| (0 << PCR_FSRP) //接收帧同步脉冲极性(高电平有效)| (0 << PCR_CLKXP) //发送时钟极性(上升沿发送数据)| (0 << PCR_CLKRP);//接收时钟极性(上升沿接收数据)#endif#if SPIMODE == 1//SPI设置过程01(0--FS高电平有效,1--CLK下降沿收发数据)| (0 << PCR_FSXP) //发送帧同步脉冲极性(高电平有效)| (0 << PCR_FSRP) //接收帧同步脉冲极性(高电平有效)| (1 << PCR_CLKXP) //发送时钟极性(下降沿发送数据)| (1 << PCR_CLKRP);//接收时钟极性(下降沿接收数据)#endif#if SPIMODE == 2//SPI设置过程10(1--FS低电平有效,0--CLK上升沿收发数据)| (1 << PCR_FSXP) //发送帧同步脉冲极性(低电平有效)| (1 << PCR_FSRP) //接收帧同步脉冲极性(低电平有效)| (0 << PCR_CLKXP) //发送时钟极性(上升沿发送数据)| (0 << PCR_CLKRP);//接收时钟极性(上升沿接收数据)#endif#if SPIMODE == 3//SPI设置过程11(1--FS低电平有效,1--CLK下降沿收发数据)| (1 << PCR_FSXP) //发送帧同步脉冲极性(低电平有效)| (1 << PCR_FSRP) //接收帧同步脉冲极性(低电平有效)| (1 << PCR_CLKXP) //发送时钟极性(下降沿发送数据)| (1 << PCR_CLKRP);//接收时钟极性(下降沿接收数据)#endif//MCR1设置过程(RMCM=0)McBSP0->SPSA = MCR1;//McBSP0->SPSD = (0 << MCR1_RMCM) //允许接收多通道选择(0)| (0x00 << MCR1_RPBBLK)| (0x00 << MCR1_RPABLK)| (0x00 << MCR1_RCBLK);//MCR2设置过程(XMCM=0)McBSP0->SPSA = MCR2;//McBSP0->SPSD = (0x00 << MCR2_XMCM) //允许发送多通道选择(00b) | (0x00 << MCR2_XPBBLK)| (0x00 << MCR2_XPABLK)| (0x00 << MCR2_XCBLK);//RCERA设置过程McBSP0->SPSA = RCERA;//McBSP0->SPSD = 0;//RCERB设置过程McBSP0->SPSA = RCERB;//McBSP0->SPSD = 0;//XCERA设置过程McBSP0->SPSA = XCERA;//McBSP0->SPSD = 0;//XCERB设置过程McBSP0->SPSA = XCERB;//McBSP0->SPSD = 0;//XCR1设置过程McBSP0->SPSA = XCR1;//McBSP0->SPSD = (0x00 << XCR1_XFRLEN1) //每帧1个字(每帧中断的次数1!!!) // | (0x02 << XCR1_XWDLEN1);//每字16位长(每次中断的字节数2!!!)| (0x00 << XCR1_XWDLEN1);//每字8位长(每次中断的字节数2!!!)//XCR2设置过程McBSP0->SPSA = XCR2;McBSP0->SPSD = (0 << XCR2_XPHASE) //单相帧(其他设置都为0)| (0x00 << XCR2_XCOMPAND)//发送数据从最高位(MSB)开始| (0x00 << XCR2_XDATDLY);//同步后延迟0位数据//RCR1设置过程McBSP0->SPSA = RCR1;McBSP0->SPSD = (0x00 << RCR1_RFRLEN1) //每帧1个字(每帧中断的次数1!!!) // | (0x02 << RCR1_RWDLEN1);//每字16位长(每次中断的字节数2!!!)| (0x00 << RCR1_RWDLEN1);//每字8位长(每次中断的字节数2!!!)//RCR2设置过程McBSP0->SPSA = RCR2;McBSP0->SPSD = (0 << RCR2_RPHASE) //单相帧(其他设置都为0)| (0x00 << RCR2_RCOMPAND)//接收数据从最高位(MSB)开始| (0x00 << RCR2_RDATDLY);//同步后延迟0位数据//SRGR1设置过程McBSP0->SPSA = SRGR1;McBSP0->SPSD = (0x00 << SRGR1_CLKGDV);//1//SRGR2设置过程McBSP0->SPSA = SRGR2;McBSP0->SPSD = (0 << SRGR2_FSGM)| (1 << SRGR2_CLKSM)//由CPU时钟产生的采样率时钟1| (0 << SRGR2_CLKSP)//0| (1 << SRGR2_GSYNC)//| (0x0f << SRGR2_FPER);//0x0f//SPCR1设置过程(CLKSTP=1Xb,RINTM=00b)McBSP0->SPSA = SPCR1;McBSP0->SPSD = (0x00 << SPCR1_RINTM) //接收中断模式00(每帧接收1次中? | (0 << SPCR1_DLB) //禁止回送| (1 << SPCR1_DXENA) //DX使能| (0x00 << SPCR1_RJUST) //接收符号不扩展| (0x02 << SPCR1_CLKSTP);//SPI模式时钟开始于上升沿(无延迟)//SPCR2设置过程(XINTM=02b)McBSP0->SPSA = SPCR2;McBSP0->SPSD = (0x02 << SPCR2_XINTM)//发送中断模式02| (1 << SPCR2_XEMPTY) //发送移位寄存器空| (1 << SPCR2_XRDY); //发送准备好//SPCR1复位过程McBSP0->SPSA = SPCR1;McBSP0->SPSD|= (1 << SPCR1_RRST);//接收器复位//SPCR2复位过程McBSP0->SPSA = SPCR2;McBSP0->SPSD|= (1 << SPCR2_XRST)//发送器复位| (1 << SPCR2_GRST)//采样率发生器复位| (1 << SPCR2_FRST);//帧同步发生器复位//清除允许BXINT0中断过程// SREG->IFR = (1 << IFR_BXINT0);//清除BXINT0中断标志// SREG->IMR |= (1 << IMR_BXINT0);//允许BXINT0中断//清除允许BRINT0中断过程SREG->IFR = (1 << IFR_BRINT0);//清除BRINT0中断标志SREG->IMR |= (1 << IMR_BRINT0);//允许BRINT0中断}void McBSPObj::McBSP1Init(void)//GPIO配置{McBSP1->SPSA = SPCR1;McBSP1->SPSD = 0;McBSP1->SPSA = SPCR2;McBSP1->SPSD = 0;McBSP1->SPSA = PCR;//设置收发。
linux在TQ2440上移植6--完善串口驱动
1、s3c2440有3个串口,其中串口2在linux-2.6.35.3里作了红外模式,我们要把它改为普通的串口用。
2、在smdk2440平台第100行修改线控制寄存器 ULCONnvi arch/arm/mach-s3c2440/mach-smdk2440.c/* IR port */[2] = {.hwport = 2,.flags = 0,.ucon = 0x3c5,.ulcon = 0x03,.ufcon = 0x51,}3、增加UART2 收发引脚功能vi drivers/serial/samsung.cdbg("s3c24xx_serial_startup ok\n");/* the port reset code should have done the correct* register setup for the port controls */if (port->line == 2){s3c2410_gpio_cfgpin(S3C2410_GPH6, S3C2410_GPH6_TXD2);s3c2410_gpio_pullup(S3C2410_GPH6, 1);s3c2410_gpio_cfgpin(S3C2410_GPH7, S3C2410_GPH7_RXD2); s3c2410_gpio_pullup(S3C2410_GPH7, 1);}a. 上面的函数s3c2410...cfg , pull都定义在arch/arm/mach-s3c2410/include/mach/gpio-fns.h中所以要在drivers/serial/samsung.c 中添加头文件 #include<mach/gpio-fns.h>b. S3C2410_GPH6, 7 也需要自己定义在arch/arm/mach-s3c2410/include/mach/gpio-nrs.h中#define S3C2410_GPH6 S3C2410_GPH(6)#define S3C2410_GPH7 S3C2410_GPH(7)同样也要在drivers/serial/samsung.c 中添加头文件 #include<mach/gpio-nrs.h>c. S3C2410_GPH6_TXD2S3C2410_GPH7_RXD2定义在arch/arm/mach-s3c2410/include/mach/regs-gpio.h同样也要在drivers/serial/samsung.c 中添加头文件 #include<mach/regs-gpio.h>4、下面还有一个重要的数据结构第889行static struct uart_driver s3c24xx_uart_drv = {.owner = THIS_MODULE,.dev_name = "s3c2410_serial", //这个是串口设备的名称,必须和文件系统的inittab里控制台的名称相对应.nr = CONFIG_SERIAL_SAMSUNG_UARTS,.cons = S3C24XX_SERIAL_CONSOLE,.driver_name = S3C24XX_SERIAL_NAME,.major = S3C24XX_SERIAL_MAJOR,.minor = S3C24XX_SERIAL_MINOR,};5、make menuconfigDdvices Drivers --->Character devices --->Serial Drivers --->1、s3c2440有3个串口,其中串口2在linux-2.6.35.3里作了红外模式,我们要把它改为普通的串口用。
精品课件-基于S3C2440的嵌入式Linux开发-第5章
默 认值 0x0
未 定义 0x0 —
第5章 S3C2440 I/O接口Linux驱动及应用实例
GPCCON GPC15 GPC14 GPC13 GPC12 GPC11 GPC10 GPC9 GPC8 GPC7 GPC6 GPC5 GPC4 GPC3 GPC2 GPC1 GPC0
表5-9 端口C控制寄存器位定义
第5章 S3C2440 I/O接口Linux驱动及应用实例
表5-7 端口B上拉寄存器位定义
GPBUP GPB[10:0]
位 [10:0]
描述
0=允许端口 B 相应引脚的上拉功能 1=禁止上拉功能
第5章 S3C2440 I/O接口Linux驱动及应用实例
3.端口C控制寄存器(GPCCON,GPCDAT,GPCUP) 端口C控制寄存器各位定义如表5-8~表5-11所示。 4.端口D控制寄存器(GPDCON,GPDDAT,GPDUP) 端口D控制寄存器各位定义如表5-12~表5-15所示。
第5章 S3C2440 I/O接口Linux驱动及应用实例
5.1 GPIO接口基础 5.2 S3C2440 GPIO接口硬件及寄存器 5.3 S3C2440 GPIO驱动及LED应用程序分析 5.4 S3C2440 LED应用程序设计例程
第5章 S3C2440 I/O接口Linux驱动及应用实例
5.1 GPIO接口基础
其中,volatile关键字是嵌入式系统开发的一个重要特点。 上述表达式拆开分析,(volatile unsigned long *)0x48000000是把0x48000000强制转换成volatile unsigned long类型的指针,暂记为p,那么就是#define A *p,即A为p 指针指向位置的内容。这里通过内存寻址访问到寄存器A,可 以进行读/写操作。
基于S3C2440和Linux的嵌入式驱动程序设计的开题报告
基于S3C2440和Linux的嵌入式驱动程序设计的开题报告一、题目意义S3C2440是指三星公司开发的一款嵌入式微处理器,其性能稳定、功耗低、体积小巧,因此广泛应用于各种嵌入式设备中。
而Linux是目前应用最广泛的开源操作系统之一,其优秀的稳定性和可扩展性,使其成为嵌入式设备的首选操作系统之一。
本课题旨在基于S3C2440和Linux,设计开发一种嵌入式驱动程序,以满足嵌入式设备在使用过程中对于驱动程序的需求。
二、研究内容和目标本课题研究内容主要包括以下方面:1.设计S3C2440与Linux的嵌入式开发环境,包括编译器、调试器和开发板等。
2.研究嵌入式驱动程序的设计原理,包括驱动程序框架、驱动程序接口和驱动程序逻辑等。
3.设计并实现S3C2440和Linux下的嵌入式驱动程序,包括对设备的初始化、操作、控制和数据传输等。
4.测试驱动程序的正确性和稳定性,以及对系统的性能进行优化。
本课题的研究目标是:1.设计开发一种基于S3C2440和Linux的嵌入式驱动程序,使其可以良好地与各种设备进行交互,完成设备的配置和数据传输等相关操作。
2.使得驱动程序的设计和实现更具有可重用性和可扩展性,以适应不同的应用需求。
3.保证驱动程序的稳定性和正确性,通过对系统的性能进行优化,提高系统的响应速度和效率。
三、研究方法和技术路线本课题主要采用以下研究方法和技术路线:1.文献调研法:通过查阅相关的文献,了解嵌入式驱动程序的设计原理和实现方法。
2.实验法:通过实验,测试驱动程序的性能和稳定性,并对系统进行优化。
3.程序设计法:通过程序设计,实现嵌入式驱动程序,并改进其可重用性和可扩展性。
本课题的技术路线如下:1.搭建基于S3C2440和Linux的嵌入式开发环境。
2.设计嵌入式驱动程序的框架和接口,并实现设备的初始化、操作、控制和数据传输等相关操作。
3.进行驱动程序的调试和测试,优化系统的性能和稳定性。
四、预期成果及意义本课题的预期成果包括:1.设计开发一种基于S3C2440和Linux的嵌入式驱动程序,能够满足嵌入式设备在使用过程中对于驱动程序的需求。
基于S3C2440嵌入式Linux的伺服电机控制
t c f g 0 1 = ( 5 0 -1 ) ; / / 设 置预 分频 率为 5 0
赫 兹
t c f gl &= ̄ S3 C2 4 1 o _ TCFGI _ _ M UX 1 _ M ASK ;
1 . 1 伺 服 电机的 驱动 与控 制 直 流 伺服 电机 速度 由P WM 的 占空 比来 决定 , 占空 比越 大 电机 转 速 越 大 , 设 置
作。
2. 3主 函数
主 函 数 中循 环 检 测 有 无 按 键 按 下 , 并
2 详细设计
2 . 1 P W M 驱 动
判 断是 哪个 按 键 , 调 用i o c t l 函数 调用 各设 备
高 新 技 术
S C I E N C E &T E C H N 0 L O G Y .
墨圆
基于 ¥ 3 C2 4 4 0嵌 入 式 L i n u x的伺 服 电机控 制
刘 亚 茹 王 贵 山
( 国 防科学 技术 大学 机 电工 程 与 自动化学 院 湖南 长沙
4 1 0 0 0 3 )
c md ) ; //设 置 引脚 状态 o u t — t a b l e [ a r g ] 为 需要 设 置状态 的 引脚 ,
主 函数 主 要 完 成 的 工 作 为 检 测 判 断 按 图 1 嵌入式 L i n u x 的硬件设备驱动过程
下的按键 , 并 调用相应的i o c t l 函数 进 行 操 c md 为 状 态值 。
摘 要: 介 绍一 种基 于¥ 3 C2 4 4 0 微 处理 器 的伺服 电机控 制 方案 。 AR M微 处理 器几乎 已经 深入 到工 业控 制 、 无 线通 讯 、 网络 应 用、 消 费类 电子 产品 , 威像 和安 全 产品各个 领 域 。 本文 实现 基于¥ 3 C 2 4 4 0 的L i f l U X 的 伺服 电机 的按键 控 制 , 控 制 伺服 电机转 动方 向及速 度 , 并通 过 L ED指示 灯 . 蜂 鸣 器等表 征 伺服 电机 的工 作状 态 。 关键 词 : AR M ¥ 3 C 2 4 4 0 L i n u x 伺服 电机 中图分类 号 : T P 2 7 4 文献标识码 : A 文 章编 号 : I 6 7 2 — 3 7 9 I ( 2 o I 3 ) 1 0 ( a ) 一0 0 0 7 — 0 I
【总结】S3C2440PLL设置详解总结
S3C2440 PLL设置详解CPU上电几毫秒后,晶振输出稳定,FCLK=Fin(晶振频率),CPU开始执行指令。
但实际上,FCLK可以高于Fin,为了提高系统时钟,需要用软件来启用PLL。
这就需要设置CLKDIVN,MPLLCON,UPLLCON这3个寄存器。
CLKDIVN寄存器用于设置FCLK,HCLK,PCLK三者的比例,MPLLCON用于设置主频FCLK,UPLLCON用于设置USB时钟UCLK。
S3C2440APLL源有两个,一个是MPLL,另一个是UPLL. MPLL用于CPU用外设,UPLL只用于USB.,包括CPU的FCLK,AHB总线外设的HCLK以及APB总线外设的PCLK。
S3C2440A包含两个锁相环(PLL):MPLL提供给FCLK、HCLK和PCLK,UPLL 专用于USB模块(48MHz)。
时钟控制逻辑可以不使用PLL来减慢时钟,并且可以由软件连接或断开各外设模块的时钟,以降低功耗。
S3C2440A的主时钟源由外部时钟(EXTCLK)或者外部晶振(XTIPll)提供,输入时钟源由模式控制引脚OM3和OM2控制选择,在复位信号的上升沿参考OM3和OM2的引脚将OM[3:2]的状态在内部锁定,如图1所示图1 引导启动时的时钟源选择选择不同输入时钟源时连接方式如图2所示:图2 时钟连接参考通过在片内集成的2个锁相环:MPLL和UPLL,可对输入的Fin=12MHz的晶振频率进行倍频。
S3C2440使用了三个倍频因子MDIV、PDIV和SDIV来设置倍频,通过寄存器MPLLCON和UPLLCON可分别设置各自的倍频因子。
其中MPLLCON寄存器用于设置处理器内核时钟主频FCLK,其输入输出频率间的关系为FCLK=MPLL=(2*m*Fin)/(p*2^s)其中m=(MDIV+8), p=(PDIV+2), s=SDIV。
其中UPLLCON寄存器用于产生48MHz或96MHz,提供USB时钟(UCLK),其输入输出频率间的关系为UCLK=UPLL=(m * Fin) / (p * 2^s)其中m=(MDIV+8), p=(PDIV+2), s=SDIV。
嵌入式Linux初级实验s3c2410 中
第三篇基础实验篇本篇内容Linux设备驱动概述★LED实例★按键中断实例★数码管实例★4*4键盘实例★LCD实例★触摸屏实例★本篇目标了解Linux设备驱动的相关概念及开发基础★掌握简单字符设备驱动的程序结构及设计流程★学习嵌入式Linux中断机制及其驱动程序结构★学习数码管的显示原理及其驱动程序的设计方法★熟悉键盘驱动原理,学会为自己的系统添加键盘设备驱动程序★了解移植LCD显示设备驱动及触摸屏输入设备驱动的过程★本篇实例实例一:LED驱动及测试实例★实例二:按键中断驱动及测试实例★实例三:数码管实例★实例四:4*4键盘实例★实例五:LCD驱动移植实例★实例六:触摸屏驱动移植实例★第8章Linux设备驱动概述在前一篇中,我们介绍了开发嵌入式Linux的基本过程,本章开篇在前一篇的基础上进行设备驱动程序的开发,使得目标板上的硬件资源为板上系统所用,这也是所有设备驱动的巨大贡献。
本章将带领你走进Linux设备驱动开发的世界。
本章首先介绍的是设备驱动的作用及其分类,不同驱动程序的特点等,然后介绍驱动模块的加载和卸载方式等。
8.1 设备驱动的角色任何计算机系统的运行都是系统中软硬件相辅相成的结果,没有硬件的软件是空中楼阁,而没有软件的硬件则只是一堆的电子元器件而已。
硬件是底层基础,是所有软件得以运行的平台,程序最终会实现为硬件上的逻辑电路;软件则是具体应用的实现,根据不同的业务需求而设计。
硬件一般是固定的,软件则很灵活,可以适应各种复杂多变的应用。
从某种程度上来看,计算机系统的软硬件相互成就了对方。
但是,软硬件之间同样存在着悖论,那就是软件和硬件不应该互相渗透入对方的领地。
为尽可能快速地完成设计,应用软件工程师不想也不必关心硬件,而硬件工程师也难有足够的闲暇和能力来顾及软件。
譬如,应用软件工程师在调用套接字发送和接收数据包的时候,他不必关心网卡上的中断、寄存器、存储空间、I/O端口、片选以及其他任何硬件词汇;在使用scanf()函数获取输入的时候,他不用知道底层究竟是怎样把终端设备的操作转化成程序输入的。
s3c2440触摸屏驱动(针对android版)
s3c2440触摸屏驱动(针对android版)s3c2440 触摸屏驱动(针对android版)和原来的触摸屏驱动区别不是很⼤,增加了report函数来将事件发送到应⽤层。
驱动结构:很简单的字符设备+平台设备驱动,总的结构来说,主要四个部分构成:proberemoveresumesuspend⼯作机制则是注册设备,然后发⽣ts按下事件后产⽣ts中断以及adc中断,获得按下坐标。
没有读写函数,重点就是在两个中断处理函数上。
1,平台设备架构部分分析:probe函数:流程:ts基址的重映射->获得并启动时钟->ADCCON、ADCDLY、ADCTSC的初始化->初始化input设备完善ts结构体->建⽴ts_filter_chain->申请中断->注册input设备(2.6.27后为event0不再是ts0)。
static int __init s3c2410ts_probe(struct platform_device *pdev){int rc;struct s3c2410_ts_mach_info *info;struct input_dev *input_dev;int ret = 0;dev_info(&pdev->dev,"Starting\n");info =(struct s3c2410_ts_mach_info*)pdev->dev.platform_data;//获得平台设备数据if(!info){dev_err(&pdev->dev,"Hm... too bad: no platform data forts\n");return-EINVAL;}#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUGprintk(DEBUG_LVL "Entering s3c2410ts_init\n");#endifadc_clock = clk_get(NULL,"adc");if(!adc_clock){dev_err(&pdev->dev,"failed to get adc clock source\n");return-ENOENT;}clk_enable(adc_clock);#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUGprintk(DEBUG_LVL "got and enabled clock\n");#endifbase_addr = ioremap(S3C2410_PA_ADC,0x20);//将PA_ADC寄存器重映射到内存上if(base_addr ==NULL){dev_err(&pdev->dev,"Failed to remap register block\n"); ret =-ENOMEM;goto bail0;}/* If we acutally are a S3C2410: Configure GPIOs */if(!strcmp(pdev->name,"s3c2410-ts"))s3c2410_ts_connect();//初始化相关gpio⼝if((info->presc & 0xff)> 0)writel(S3C2410_ADCCON_PRSCEN |S3C2410_ADCCON_PRSCVL(info->presc&0xFF),base_addr + S3C2410_ADCCON);elsewritel(0, base_addr+S3C2410_ADCCON);/* Initialise registers */if((info->delay & 0xffff)> 0)writel(info->delay & 0xffff, base_addr + S3C2410_ADCDLY);writel(WAIT4INT(0), base_addr + S3C2410_ADCTSC);/* Initialise input stuff */memset(&ts, 0,sizeof(struct s3c2410ts));input_dev = input_allocate_device();if(!input_dev){dev_err(&pdev->dev,"Unable to allocate the input device\n");ret =-ENOMEM;}//初始化input设备ts.dev = input_dev;ts.dev->evbit[0]= BIT_MASK(EV_SYN)| BIT_MASK(EV_KEY)|BIT_MASK(EV_ABS);ts.dev->keybit[BIT_WORD(BTN_TOUCH)]= BIT_MASK(BTN_TOUCH);input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0);input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0);ts.dev->name = s3c2410ts_name;ts.dev->id.bustype = BUS_RS232;ts.dev->id.vendor = 0xDEAD;ts.dev->id.product = 0xBEEF;ts.dev->id.version = S3C2410TSVERSION;ts.state = TS_STATE_STANDBY;//设置ts状态为就绪ts.event_fifo = kfifo_alloc(TS_EVENT_FIFO_SIZE, GFP_KERNEL, NULL);//为event队列申请内存空间if(IS_ERR(ts.event_fifo)){ret =-EIO;goto bail2;}/* create the filter chain set up for the 2 coordinates we produce */ts.chain = ts_filter_chain_create(pdev, info->filter_config, 2);//针对android的,建⽴filter_chainif(IS_ERR(ts.chain))goto bail2;ts_filter_chain_clear(ts.chain);/* Get irqs */if(request_irq(IRQ_ADC, stylus_action, IRQF_SAMPLE_RANDOM,"s3c2410_action", ts.dev)){dev_err(&pdev->dev,"Could not allocate ts IRQ_ADC !\n"); iounmap(base_addr);goto bail3;}if(request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM, "s3c2410_action", ts.dev)){dev_err(&pdev->dev,"Could not allocate ts IRQ_TC !\n"); free_irq(IRQ_ADC, ts.dev);iounmap(base_addr);ret =-EIO;goto bail4;}dev_info(&pdev->dev,"Successfully loaded\n");/* All went ok, so register to the input system */rc = input_register_device(ts.dev);if(rc){ret =-EIO;goto bail5;}return 0;bail5:free_irq(IRQ_TC, ts.dev);free_irq(IRQ_ADC, ts.dev);clk_disable(adc_clock);iounmap(base_addr);disable_irq(IRQ_TC);bail4:disable_irq(IRQ_ADC);bail3:ts_filter_chain_destroy(ts.chain);kfifo_free(ts.event_fifo);bail2:input_unregister_device(ts.dev);bail1:iounmap(base_addr);bail0:return ret;}remove:就是probe的逆运算,static int s3c2410ts_remove(struct platform_device *pdev) {disable_irq(IRQ_ADC);disable_irq(IRQ_TC);free_irq(IRQ_TC,ts.dev);free_irq(IRQ_ADC,ts.dev);if(adc_clock){clk_disable(adc_clock);clk_put(adc_clock);adc_clock =NULL;}input_unregister_device(ts.dev);iounmap(base_addr);ts_filter_chain_destroy(ts.chain);kfifo_free(ts.event_fifo);return 0;}resume与suspend函数可有可⽆,完成触摸屏的激活和挂起,2,中断处理分析:三种模式转换过程:等待down中断模式->x,y连续坐标转换模式->等待up中断模式->等待down中断模式->..两个中断的发⽣:触摸屏按下,发⽣ts中断,开始ad转换,ad转换结束,发⽣adc中断。
【IT专家】基于S3C2440的嵌入式Linux驱动——AT24C02(EEPROM I2C接口)驱动解读
本文由我司收集整编,推荐下载,如有疑问,请与我司联系基于S3C2440的嵌入式Linux驱动——AT24C02(EEPROM I2C接口)驱动解读本文将介绍Linux中AT24C02驱动。
AT24C02是一种EEPROM,使用I2C接口来访问。
在开发板中,使用I2C控制器0和AT24C02连接,这里就不给出原理图了,如需要,可以搜索TQ2440开发板的原理图。
目标平台:TQ2440 CPU:s3c2440 内核版本:2.6.32 本文所有的代码均位于内核源码:linux/drivers/misc/eeprom/at24.c中。
1. 模块注册和注销 static int __init at24_init(void) /* 将io_limit向下圆整到最近的2的幂*/ io_limit = rounddown_pow_of_two(io_limit); return i2c_add_driver( at24_driver); /* i2c 驱动注册*/module_init(at24_init);static void __exit at24_exit(void) i2c_del_driver( at24_driver);module_exit(at24_exit);MODULE_DESCRIPTION(“Driver for most I2C EEPROMs”);MODULE_AUTHOR(“David Brownell and Wolfram Sang”);MODULE_LICENSE(“GPL”); 注册函数很简单。
io_limit为写入时允许一次写入的最大字节,该参数为驱动模块参数,可由用户设置,默认值为128字节。
首先对io_limit向下圆整到最近的2的幂,接着直接调用了i2c_add_driver来注册一个i2c驱动。
注销函数更简单。
注销之前注册的i2c驱动。
2. 设备驱动绑定熟悉I2C驱动架构的可能会知道I2C驱动的match函数,该函数将使用id表(struct i2c_device_id)和i2c设备(struct i2c_client)进行匹配,判断。
S3C2440中断处理机制PPT教学课件
2020/12/10
Eint8-23
ARM920T
reset Undef SWI Pabort Dabort
IRQ FIQ
1
中断申请模式设置寄存器, 确定一个中断是申请ARM 核的普通IRQ处理还是快速 的FIQ处理
MODE
中断优先级设置寄存器,确 定多个中断发生时,哪个 中断事件先执行响应
子中断屏蔽寄存器,不让中断事件
SUBMASK 上报给SRCPND
2
设置具体的外部 中断滤波方式
Eint0-3 Eint4-7 EINTFLT0
EINTFLT1 EINTFLT2
Eint8-23
EXTINT0 EXTINT1 EXTINT2
设置外部中断源0-7的触发模式, 高、低、上升沿、下降沿等等。
3
2020/12/10
4
PPT教学课件
谢谢观看
Hale Waihona Puke Thank You For Watching
5
设置外部中断源8-15的触发模式, 高、低、上升沿、下降沿、是否有 数字滤波等。
设置外部中断源16-23的触发模式, 高、低、上升沿、下降沿、是否有 数字滤波等。
EINTMASK
中断屏蔽寄存器,是否屏蔽外部中 断源
外部中断源申请寄存器,确定发生
EXTPEND 了什么外部中断(含外部中断源4-23)
2020/12/10
Priority
2020/12/10
SRCPND INTPND
中断源申请寄存器,确定发生了什 么中断(含外部中断源0-3)
中断处理寄存器,确定是哪个中断 等待ARM核的IRQ中断处理
SUBSRCPND
基于S3C2440嵌入式Linux的伺服电机控制
基于S3C2440嵌入式Linux的伺服电机控制摘要:介绍一种基于S3C2440微处理器的伺服电机控制方案。
ARM微处理器几乎已经深入到工业控制、无线通讯、网络应用、消费类电子产品、成像和安全产品各个领域。
本文实现基于S3C2440的Linux的伺服电机的按键控制,控制伺服电机转动方向及速度,并通过LED指示灯、蜂鸣器等表征伺服电机的工作状态。
关键词:ARM S3C2440 Linux 伺服电机Linux操作系统将设备都看成文件,以操作文件的方式访问设备,应用程序不能直接操作硬件,而使用统一的接口函数调用驱动程序,驱动程序位于内核中,一方面完成对底层硬件的控制;另一方面将控制底层硬件的函数以标准文件访问的方式提供给上层应用程序。
因此,对伺服电机的控制需要编写不同模块的驱动程序,再调用执行。
运行程序时将程序在交叉编译环境下编译,将可执行程序移植进ARM运行。
1 总体设计方案按键控制直流伺服电机包括按键检测、电机驱动、状态只是三个主要部分,因此,整体控制也分为三个模块:一是伺服电机的驱动与控制;二是指示灯蜂鸣器等的状态控制;三是主函数检测按键执行相应操作。
1.1 伺服电机的驱动与控制直流伺服电机速度由PWM的占空比来决定,占空比越大电机转速越大,设置PWM占空比可以改变电机转动速度。
编写PWM驱动,设置PWM频率、占空比,在主函数中调用ioctl函数改变占空比。
S3c2440A有5个16位的定时器,这里使用了定时器1,并设置PWM频率为10 kHz。
1.2 LED指示灯和蜂鸣器的控制指示灯和蜂鸣器的控制通过引脚输出高低电平信号进行控制,并且直流电机的方向也是用同样的方式控制。
编写引脚信号输出驱动,在主函数中调用ioctl函数改变各引脚状态。
1.3 主函数主函数主要完成的工作为检测判断按下的按键,并调用相应的ioctl函数进行操作。
2 详细设计2.1 PWM驱动本驱动通过读写寄存器设置PWM工作方式,设置占空比,在主函数通过调用函数传入不同参数设置预期的占空比。
基于S3C2440处理器的网卡驱动开发及实现
分成员: c.调用check—mem—region()检测110地址空 间。然后调用request—mem—region0申请以 dev一>base—addr为起始地址的1 6个连续的110地 址空间; d.通过cs8900一read0探测网卡CS8900A。读取 ID信息: e.设置CS8900A的INTRQ0作为中断信号输出 引脚: f.将MAC地址写入CS8900A的lA寄存器中: g.通过register—netdev()将CS8900A注册到 Linux全局网络设备链表中: 2.3.2打开(或关闭)网络设备 系统响应ifconfig命令时,打开(关闭)一个网络 接口。ifconfig命令开始会调用ioctI(SIOCSIFADDR) 来将地址赋予接口。响应SIOCSIFADDR由内核来完 成,与设备无关。接着,ifconfig命令会调用 ioctI(SIOCSIFFLAGS)设置dev->flag的IFF—UP位来 打开设备,这个调用会使设备的open方法得到调用。 (当ifconfig调用ioctI(SIOCSIFFLAGS)清除dev-> flag的IFF—UP位时,设备的stop方法将被调用) 实例中利用cs8900一start()函数打开网络设备, 主要完成的工作: a.通过set—irq_typeof司内核注册网络设备的中
★hard—header:该函数(在hard—start—xmit前被调 用)根据先前检索到的源和目标硬件地址建立硬件头; eth—header:是以太网类型接口的默认函数; 2.3网络驱动程序的编写及实现原理 Linux网络系统各个层次之间的数据传送都是通 过套接字缓冲区sk_buff完成的,sk-bu仟数据结构 是各层协议数据处理的对象o sk.bu仟<linux/
S3C2440按键中断调试心得
看完中断控制系统后决定写一个按键中断程序,四个按键对应四个外部中断,每个按键按下时对于led亮。
主函数中我们需要做以下工作:1,配置I/O口为外部中断模式(rGPGCON),配置led引脚为输出(rGPBCON),灭led(rGPBDAT)。
这些工作我们可以直接#define一下然后在主函数中引用。
如GPG0,1,2,4分别对于EINT8,11,13,14。
需要配置为外部中断模式,我们可以这样写:#define GPG0_eint (2<<(0*2))#define GPG1_eint (2<<(1*2))#define GPG2_eint (2<<(2*2))#define GPG4_eint (2<<(4*2))然后主函数中这样写:rGPGCON=GPG0_eint | GPG1_eint | GPG2_eint | GPG4_eint;为什么要用“|”符号,是因为我们要保证1能被正常写进相应位。
其他寄存器的操作相信大家也能写出来了吧。
2,初始化MMU,直接调用函数MMU_Init();为什么调用这个函数我也不是很明白,似乎需要进行地址映射什么的,大家自己百度之就ok,相信学到后面再回过来看这个初始化,应该问题不大了。
3,清寄存器相应位。
S3C2440支持60个中断源,其中有EINT0-EINT23这24个外部中断,EINT0-3为系统保留,EINT4-7为子中断,对应中断源为EINT4_7,EINT8-23,对应中断源为EINT8_23;其他的为内部中断,内部中断也分带子中断和不带子中断。
外部中断(0-3)需要清rSRCPND,rINTPND,直接往相应位写1即可清除。
也可以直接调用函数ClearPending(bit);括号内的bit为对应的中断值。
如若中断为EINT8-23,则这样写:ClearPending(EINT8-23);。
带子中断的也需要清子中断。
S3C2440开发板上实现按键点亮LED驱动开发的详细过程01
文章记录了作者在S3C2440开发板上实现按键点亮LED驱动开发的详细过程,还记录了一些容易出现的错误,以及怎么解决这些错误。
一、驱动开发流程Linux驱动开发不同于应用程序的开发。
驱动开发是直接和硬件打交道的,通过对硬件的操作给应用程序提供一些接口函数,使得应用程序能够“间接”的控制硬件来工作。
对于按键点亮LED的驱动开发流程如下。
二、驱动开发具体步骤1、查看开发板TQ2440底板原理图,找到按键和LED模块,如下图:图-2 按键和LED电路图从上图我们可以清楚地看到K1~K4对应的管脚是ENT1~ENT4,LED1~LED4对应的管脚是nLED_1~nLED_4.2、查看TQ2440_核心板原理图,找到对应的CPU管脚,如下图:图-3 按键和LED对应CPU管脚电路图3、查看s3c2440芯片手册,查看CPU管脚的模式,如下图从上图我们可以看出按键对应的CPU管脚GPF0~GPF4都是占两位(如:GPF0[1:0])。
按键是一种中断,要想让按键工作在中断模式下,就要设置GPF0~GPF4(GPF3除外)管脚都设置在中断模式下,即为10。
对于LED对应的CPU管脚GPB5~GPB8也是占两位。
要想让LED工作,就要让LED工作在输出模式下,即对应管脚设置为01.4、编写按键点亮LED驱动程序/*调用内核头文件,和应用程序调用的头文件不一样*/#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <asm/irq.h>#include <linux/interrupt.h>#include <asm/uaccess.h>#include <asm/arch/regs-gpio.h>#include <asm/hardware.h>#include <linux/device.h>#include <linux/poll.h>#define DEVICE_NAME "tope-buttons" //自定义驱动称为“tope-buttons”。
关于Linux下S3C2440 RTC实时时钟驱动配置与修改
关于Linux 下S3C2440 RTC 实时时钟驱动配置与修改
Linux 下对S3C2440 RTC 的支持非常完善,我们只需要做简单的修改,即可使用RTC1、vi arch/arm/mach-s3c2440/mach-smdk2440.c
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_rtc,//这里我们添加上RTC 平台设备,默认是没添加的
};
2、make zImage
3、使用与测试
Linux 下的时间分为两种,系统时间与硬件时间。
我们一般看到的时间就是系统时间,比如现在是11:45 am。
硬件时间指的是主板上CMOS 中的时间或RTC 中的时间,这两个时间并不
是总是同步的,一般情况下大约11 分钟同步一次。
linux 系统开机时,从CMOS/rtc 中读取当前时间,作为系统时间,从此以后
系统时间独立tick,此时如果你用date 命令修改了系统时间,硬件时间是不受
影响的,就是说如果此时关机,下次的系统时间还是不对。
要想将系统时间保
存到硬件时间,可以是用hwclock 或者clock 命令,hwclock 就是hardware clock 的意思。
一个完整的修改linux 系统时间的过程如下所示。
linux下S3C2440–I2C驱动学习之四“i2c
linux下S3C2440–I2C驱动学习之四“i2c我们进入I2C驱动的最后一个小节,在这个小节里,我们主要探讨 i2c_algorithm 数据结构和i2c-core.c 的一些主要函数及其作用。
i2c_algorithm 结构体struct i2c_algorithm {int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write,u8 command, int size, union i2c_smbus_data *data);u32 (*functionality) (struct i2c_adapter *);};一个i2c 适配器上的i2c 总线通信方法由其驱动程序提供的i2c_algorithm 数据结构描述,由algo 指针指向。
i2c_algorithm 数据结构即为i2c_adapter 数据结构与具体i2c 适配器的总线通信方法的中间层,正是这个中间层使得上层的i2c 框架代码与与具体i2c 适配器的总线通信方法无关,从而实现了i2c 框架的可移植性和重用性。
当安装具体i2c 适配器的驱动程序时由相应驱动程序实现具体的i2c_algorithm 数据结构,其中的函数指针指向操作具体i2c 适配器的代码。
master_xfer/smbus_xfer 指针指向i2c 适配器驱动程序模块实现的i2c 通信协议或者smbus 通信协议。
在用户进程通过i2c-dev 提供的/dev/i2c/%d 设备节点访问i2c 设备时,最终是通过调用master_xfer 或者smbus_xfer 指向的方法完成的。
i2c-core.ci2c.h 和i2c-core.c 为i2c 框架的主体,提供了核心数据结构的定义、i2c 适配器驱动和设备驱动的注册、注销管理,i2c 通信方法上层的、与具体适配器无关的代码、检测设备地址的上层代码等;i2c-dev.c 用于创建i2c 适配器的/dev/i2c/%d 设备节点,提供i2c 设备访问方法等。
嵌入式Linux之我行——S3C2440上MMCSD卡驱动实例开发讲解(二)-内核、驱动开发篇-
嵌入式Linux之我行——S3C2440上MMCSD卡驱动实例开发讲解(二)-内核、驱动开发篇-嵌入式Linux之我行,主要讲述和总结了本人在学习嵌入式linux 中的每个步骤。
一为总结经验,二希望能给想入门嵌入式Linux的朋友提供方便。
如有错误之处,谢请指正。
•共享资源,欢迎转载:一、开发环境•主机:VMWare--Fedora 9•开发板:Mini2440--64MB Nand, Kernel:2.6.30.4•编译器:arm-linux-gcc-4.3.2上接:S3C2440上MMC/SD卡驱动实例开发讲解(一)6. s3cmci_ops SDI主机控制器操作接口函数功能分析:mmc_host_ops结构体定义了对host主机进行操作的各种方法,其定义在Core核心层的host.h中,也就是Core核心层对Host主机层提供的接口函数。
这里各种方法的函数原型如下:从各函数原型上看,他们都将mmc_host结构体作为参数,所以我在刚开始的时候就说过mmc_host结构体是MMC/SD卡驱动中比较重要的数据结构。
可以这样说,他是Core层与Host层进行数据交换的载体。
那么,这些接口函数何时会被调用呢?答案可以在Core 层的core.c和sd.c中找到,我们可以看到如下部分代码:{......//导致s3cmci_card_present被调用if(host->ops->get_cd && host->ops->get_cd(host)== 0)goto out;......}static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard){....../* Check if read-only switch is active.*/if(!oldcard){ //导致s3cmci_get_ro被调用if(!host->ops->get_ro || host->ops->get_ro(host) < 0){printk(KERN_WARNING "%s: host does not ""support reading read-only ""switch. assuming write-enable.\n",mmc_hostname(host));}else{好了,我们开始分析每个接口函数的具体实现吧,从简单的开始吧。
S3C2440按键中断LED控制(详细注解)
按键与LED灯,四个中断控制四个灯LED引脚连接按键引脚连接://===================================================================== =// 开发板:GT2440// 工程名称:KEY_EINT// 功能描述:外部中断0 2 11 19 控制四个LED小灯//===================================================================== =/******************************************************************************4 个用户按键四个输入引脚:EINT0 -----( GPF0 )----INPUT---K1EINT2 -----( GPF2 )----INPUT---K2EINT11 -----( GPG3 )----INPUT---K3EINT19 -----( GPG11 )----INPUT---K4******************************************************************************/ #include "def.h"#include "2440addr.h"#include "2440lib.h"#include "string.h"#include "uart.h"#define LED1ON 0xFDE //LED1点亮值为0x7DE(低电平点亮)#define LED2ON 0xFBE#define LED3ON 0xF7E#define LED4ON 0xEFE#define LEDOFF 0xFFF //LED熄灭值为0xFFFvoid __irq EintHandler0(void);void __irq EintHandler2(void);void __irq EintHandler8_23(void);void Main(void){memcpy((unsigned char *)0x0,(unsigned char *)0x30000000,0x1000);SetSysFclk(FCLK_400M); //设置系统时钟400MChangeClockDivider(2, 1); //设置分频1:4:8CalcBusClk(); //计算总线频Uart_Select(0);Uart_Init(0,115200);Uart_Printf("KEY EINT TEST\n");Uart_Printf("Use Eint 0,2,11,19 falling edge\n");rGPBCON = (rGPBCON | 0xFFFFFFF) & 0xFFFd57FC; //GPB5--GPB8设置为output rGPBUP = rGPBUP & 0xFE1F; //使能GPB5 6 7 8上拉电阻rGPBDAT = 0x7FE; //GPB5 6 7 8位初始化为1/***********************************************************//GPF0 GPF2设置为EINT[0],EINT[2] 10 = EINT[2] 10 = EINT[0]//设置GPGCON的3,11为中断功能,分别对应EINT[11] EINT[19]*************************************************************/rGPFCON = (rGPFCON|0xFFFF)&0xFFFFFFEE;rGPGCON = (rGPGCON|0xFFFFFFFF)&0xFFBFFFBF;/*************************************************************** START:外部中断控制寄存器3位一个设置触发沿, 设置外部中断0 ,2下降沿触发外部中断控制寄存器3位设置一个触发沿, 设置外部中断11下降沿触发外部中断控制寄存器3位设置一个触发沿, 设置外部中断19下降沿触发01x = 下降沿触发*****************************************************************/ rEXTINT0 &= ~(7<<0 | 7<<8 );rEXTINT0 |= (2<<0 | 2<<8) ;rEXTINT1 &= ~(7<<12 );rEXTINT1 |= (2<<12) ;rEXTINT2 &= ~(7<<12 );rEXTINT2 |= (2<<12) ;/*************************************************************** END*****************************************************************//********************************************************//外部中断0~3保留为0 不用使能EINTPEND和EINTMASK rEINTPEND |= (1<<0|1<<2); //clear eint 4 rEINTMASK &= ~(1<<0|1<<2); //enable eint************************************************************//********************************************************//外部中断11,19需要对EINTPEND(外部中断挂起寄存器)清零并对EINTMASK(外部中断屏蔽寄存器)清零使能EINTPEND写1清零使能中断,EINTMASK清零使能中断************************************************************/ rEINTPEND |= (1<<11|1<<19);rEINTMASK &= ~(1<<11|1<<19);/***********************************************************清源挂起(SRCPND)寄存器和中断挂起(INTPND)寄存器,可以直接操作寄存器这两个函数在2440addr.h中定义*************************************************************/ ClearPending(BIT_EINT0);ClearPending(BIT_EINT2);ClearPending(BIT_EINT8_23);/***********************************************************pISR_EINT0,2中断的入口地址,定义在2440addr.inc中2440addr.inc文件内定义了用于汇编的s3c2440寄存器变量和地址*************************************************************/ pISR_EINT0 =(unsigned)EintHandler0;pISR_EINT2 =(unsigned)EintHandler2;pISR_EINT8_23 =(unsigned)EintHandler8_23;/*********************************************************** 使能或禁止中断,这是个宏定义,其原型为rINTMSK &= ~(bit)这里的操作就是取消了屏蔽,中断就开始了*************************************************************/ EnableIrq(BIT_EINT0);EnableIrq(BIT_EINT2);EnableIrq(BIT_EINT8_23);while(1);}//外部中断0,服务函数void __irq EintHandler0(void){if(rINTPND==BIT_EINT0){ClearPending(BIT_EINT0);Uart_Printf("KEY To LED1 ON \n");rGPBDAT = LED1ON;Delay(500);rGPBDAT = LEDOFF;}}//外部中断2,服务函数void __irq EintHandler2(void){if(rINTPND==BIT_EINT2){ClearPending(BIT_EINT2);Uart_Printf("KEY To LED2 ON \n");rGPBDAT = LED2ON;Delay(500);rGPBDAT = LEDOFF;}}//外部中断11,19服务函数void __irq EintHandler8_23(void){if(rINTPND==BIT_EINT8_23){ClearPending(BIT_EINT8_23);if(rEINTPEND&(1<<11)){Uart_Printf("KEY To LED3 ON \n");rGPBDAT = LED3ON;Delay(500);rGPBDAT = LEDOFF;rEINTPEND |= 1<< 11;//写1清零该位}if(rEINTPEND&(1<<19)){Uart_Printf("KEY To LED4 ON \n");rGPBDAT = LED4ON;Delay(500);rGPBDAT = LEDOFF;rEINTPEND |= 1<< 19;//写1清零该位}}}。
linux和qtopia下的矩阵键盘驱动程序
linux和qtopia下的矩阵键盘驱动程序基于s3c2440和linux,实现了3*4的矩阵键盘驱动。
功能:延时消抖,重复按键,多键齐按(??)更详细的说明文档:“基于S3C24440和嵌入式Linux的矩阵键盘设计”,电子技术,2008,45(5):21-23/*********************************************************** s3c2440-keyboard.c** keyboard driver for S3C2440 based PDA*** History:2007/04/30*************************************************************/#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/miscdevice.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/poll.h>#include <linux/spinlock.h>#include <asm/irq.h>#include <asm/arch/irq.h>#include <asm/arch/irqs.h>#include <asm/arch/clocks.h>#include <asm/hardware.h>#include <asm/arch/S3C2440.h>#define DEVICE_NAME "s3c2440-kb" //键盘设备名static int kbMajor = 0; //默认的主设备号#define MAX_KB_BUF 10 //循环队列的尺寸typedef struct {unsigned int keyStatus;int irq;// int timeCount;u_short buf[MAX_KB_BUF]; /* 循环队列*/unsigned int head, tail; /* 循环队列的读写指针*/spinlock_t lock; /*锁*/} KB_DEV;static KB_DEV kbdev;#define KEY_UP 0 //按键弹起#define KEY_DOWN 1 //按键按下#define NO_KEY_DOWN 2 //没有键按下#define EINT1_DOWN 0#define EINT3_DOWN 1#define EINT8_DOWN 2#define NO_EINT_DOWN 3/*循环队列操作*/#define BUF_HEAD (kbdev.buf[kbdev.head])#define BUF_TAIL (kbdev.buf[kbdev.tail])#define INCBUF(x) if((++x)==MAX_KB_BUF) x=0/*定时器设置*/#define KB_TIMER_DELAY (HZ/50) /*HZ表示每秒产生的时钟滴答数,定时器超时为20ms*/#define REPEAT_START_DELAY (HZ) /* 自动重复开始延时:1秒*/ #define REPEAT_DELAY (HZ/2) /*自动重复延时:0.5秒*/static struct timer_list kb_timer;static struct timer_list repeat_timer;spinlock_t repeat_lock;static int timeCount =1;static int TIME_OUT =5;/*键盘矩阵*/static u_short keyboard_code_map[4][3]={{1 , 2 , 3 },{4 , 5 , 6 },{7 , 8 , 9 },{10, 11, 12}}; //每个按键对应的键盘码static u_short pre_keyboard_code = 0; //上次扫描得到的按键值static u_short curr_keyboard_code = 0;//当前扫描得到的按键值static u_short snap_keyboard_code[4][3]={{0 , 0 , 0 },{0 , 0 , 0 },{0 , 0 , 0 },{0, 0, 0}}; //临时按键值#define DETECTION_THROLD 3static int requestIrq();static int s3c2440_kb_release(struct inode *inode, struct file *filp);/*----------------------------------------------------* func: 初始化GPJCON寄存器,将GPJ9,GPJ10,GPJ11,GPJ12* 配置成output管腿*------------------------------------------------------*/static void init_gpjcon(){//GPJ9,GPJ10,GPJ11,GPJ12------>outputGPJCON &= 0xFC03FFFF;GPJCON |= 0x01540000;}/*----------------------------------------------------* func: 向GPJ9,GPJ10,GPJ11,GPJ12输出value* param:* value: 输出值*------------------------------------------------------*///static inline void output_giop(int value ) //往所有行输出{value &= 0x0000000F;value <<= 9;GPJDAT &= 0xE1FF;GPJDAT |= value;udelay(2);}/*----------------------------------------------------* func: 判断eint当前是否是低电平* param:* irq: 当前引发中断的eint的irq号* return:* EINT1_DOWN: eint1上是低电平* EINT3_DOWN: eint3上是低电平* EINT8_DOWN: eint8上是低电平* NO_EINT_DOWN:eint上不是低电平------------------------------------------------------*/int get_eint_value(int irq){u_int IOValue;IOValue = GPFDAT ;if( (irq == 1) && (( IOValue & 0x00000002)==0) ) {return EINT1_DOWN;}if((irq ==3 ) && (( IOValue & 0x00000008)==0) ) {return EINT3_DOWN;}IOValue = GPGDAT ;if((irq ==36) && (( IOValue & 0x0000001)==0) ) {return EINT8_DOWN;}return NO_EINT_DOWN;}/*----------------------------------------------------* func: 扫描键盘,判断哪一个键被按下* param:* x: 得到按键的行号* y: 得到按键的列号* return:* KEY_DOWN: 键盘上有键被按下* NO_KEY_DOWN: 键盘上没有键被按下------------------------------------------------------*/static inline int scan_keyboard(int* x,int* y) {int matrix_row,matrix_col,matrix_col_status; output_giop(0xF); //往所有行输出1//判断按键在哪一行matrix_row=matrix_col=-1;output_giop(0xE);//在第1行上输出1,其余行输出0 matrix_col_status = get_eint_value(kbdev.irq);if(matrix_col_status != NO_EINT_DOWN){matrix_row = 0;matrix_col = matrix_col_status;goto scanend;}output_giop(0xD);//在第2行上输出1,其余行输出0matrix_col_status = get_eint_value(kbdev.irq);if(matrix_col_status != NO_EINT_DOWN){matrix_row=1;matrix_col = matrix_col_status;goto scanend;}output_giop(0xB);//在第3行上输出1,其余行输出0 matrix_col_status =get_eint_value(kbdev.irq);if(matrix_col_status != NO_EINT_DOWN){matrix_row=2;matrix_col = matrix_col_status;goto scanend;}output_giop(0x7);//在第4行上输出1,其余行输出0 matrix_col_status =get_eint_value(kbdev.irq);if(matrix_col_status != NO_EINT_DOWN){matrix_row=3;matrix_col = matrix_col_status;goto scanend;}scanend:output_giop(0);if(matrix_row >=0 ){snap_keyboard_code[matrix_row][matrix_col_status]=snap_keyboard_code[matrix_row][matrix_col_status] + 1;if(snap_keyboard_code[matrix_row][matrix_col]>=DETECTION_THROLD) {*x=matrix_row;*y=matrix_col;curr_keyboard_code = keyboard_code_map[matrix_row][matrix_col]; return KEY_DOWN;}}return NO_KEY_DOWN;}/*----------------------------------------------------* func: 判断本次按键是否与上次按键相同* param:** return:* 0: 相同* 1: 不同------------------------------------------------------*/static inline int key_changed(){return (pre_keyboard_code == curr_keyboard_code)? 0 : 1; }/*----------------------------------------------------* func: 将按键对应的键盘码保存到循环队列中* param:* keyValue: 按键的对应的键盘码* return:*------------------------------------------------------*/static inline void save_key_to_queue(u_short keyValue) {if (kbdev.keyStatus == KEY_DOWN){BUF_HEAD = keyValue;INCBUF(kbdev.head);//wake_up_interruptible(&(kbdev.wq));}}/*----------------------------------------------------* func: 重复按键定时器处理程序,如果一直按住某键,则将该键的键盘码定时存到循环队列中* param:* data: 无参数* return:*------------------------------------------------------*/static inline void repeat_timer_handler(unsigned long data){spin_lock_irq(&(repeat_lock));if(kbdev.keyStatus ==KEY_DOWN){repeat_timer.expires = jiffies + REPEAT_DELAY;//设置自动重复延时add_timer(&repeat_timer);//将定时器加入队列if(pre_keyboard_code != 0){//printk("repeat save keyvalue\n %d",pre_keyboard_code);save_key_to_queue(pre_keyboard_code);//将按键值存入循环队列}}else//如果按键弹起{//del_timer(&repeat_timer);// printk("del repeat timer\n");}spin_unlock_irq(&(repeat_lock));}/*----------------------------------------------------* func: 使能中断* param:* return:*------------------------------------------------------*///使能中断static inline void enableIrq(){//清除SRCPND寄存器中eint1 eint2 eint8相应位SRCPND = 0x0000002A;//使能中断enable_irq(IRQ_EINT1);enable_irq(IRQ_EINT3);enable_irq(IRQ_EINT8);}/*----------------------------------------------------* func: 键盘定时扫描程序,如果得到稳定键码,将键码存* 入循环队列;如果没有,则延时20ms后继续扫描* param:* data: 无参数* return:*------------------------------------------------------*/static inline void kb_timer_handler(unsigned long data){int x,y;spin_lock_irq(&(kbdev.lock));x = y = 0;if(scan_keyboard(&x,&y) == KEY_DOWN){// printk("snap_keyboard_code=%d, %d, %d, %d\n",snap_keyboard_code[0][1],snap_keyboard_code[1][1],snap_keyboard_c ode[2][1],snap_keyboard_code[3][1]);kbdev.keyStatus =KEY_DOWN;if(key_changed()){pre_keyboard_code = curr_keyboard_code;save_key_to_queue(pre_keyboard_code);//printk("KEY_DOWN:%d x=%d y =%d\n",timeCount,x,y);//设置自动重复开始延时定时器/*repeat_timer.expires = jiffies + REPEAT_START_DELAY;add_timer(&repeat_timer);*/}timeCount=1;memset(snap_keyboard_code,0,12*sizeof(u_short));//curr_keyboard_code =0;kb_timer.expires = jiffies + KB_TIMER_DELAY;add_timer(&kb_timer);}else{//printk("snap_keyboard_code=%d, %d, %d, %d\n",snap_keyboard_code[3][0],snap_keyboard_code[3][1],snap_keyboard_c ode[3][2],snap_keyboard_code[3][3]);kb_timer.expires = jiffies + KB_TIMER_DELAY;add_timer(&kb_timer);//printk("timeCount:%d\n",timeCount);if (timeCount==TIME_OUT) //扫描5次后仍然没有得到稳定键值{//复位计数器timeCount=1;kbdev.keyStatus =KEY_UP;//使能中断enableIrq();//关闭定时器del_timer(&kb_timer);del_timer(&repeat_timer);//printk("enable irq \n\n\n");curr_keyboard_code = 0;pre_keyboard_code= 0 ;memset(snap_keyboard_code,0,12*sizeof(u_short));elsetimeCount++;}spin_unlock_irq(&(kbdev.lock));}/*----------------------------------------------------* func: 从循环队列中读取按键的键码* param:* return: 按键的键码*------------------------------------------------------*/static inline int kbRead(){u_short keyvalue;spin_lock_irq(&(kbdev.lock));keyvalue = BUF_TAIL;INCBUF(kbdev.tail );spin_unlock_irq(&(tsdev.lock));return keyvalue;}/*----------------------------------------------------* func: 对应文件读的函数,如果循环队列中有键码, 则将键码拷贝到用户空间的buffer中* param:** return:* 返回从循环队列中读取的键码的字节数**------------------------------------------------------*/static ssize_tS3C2440_kb_read(struct file *filp, char *buffer, size_t count, loff_t * ppos) {u_short keyvalue;if(kbdev.head == kbdev.tail){return 0;}else{keyvalue = kbRead();count = sizeof(keyvalue);/*将数据拷贝到用户空间*/copy_to_user(buffer,&(keyvalue),count);return count;}}/*----------------------------------------------------* func: 与打开文件对应的open函数,初始化全局变量和定* 时器以及请求中断* param:*** return:*------------------------------------------------------*/static int S3C2440_kb_open(struct inode *inode, struct file *filp) {kbdev.keyStatus = KEY_UP;kbdev.head=kbdev.tail = 0;kbdev.lock = SPIN_LOCK_UNLOCKED;repeat_lock = SPIN_LOCK_UNLOCKED;output_giop(0);//初始化定时器init_timer(&kb_timer);kb_timer.function = kb_timer_handler;//初始化重复按键定时器init_timer(&repeat_timer);repeat_timer.function = repeat_timer_handler;/*if(requestIrq() !=0)return -1;*/enableIrq();MOD_INC_USE_COUNT;return 0;}static struct file_operations kb_fops = {owner: THIS_MODULE,open: S3C2440_kb_open,read: S3C2440_kb_read,release: s3c2440_kb_release,};/*----------------------------------------------------* func: 中断处理程序,关闭中断开启键盘扫描定时器* param:*** return:*------------------------------------------------------*/static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) {spin_lock_irq(&kbdev.lock);//禁止所有中断disable_irq(IRQ_EINT1);disable_irq(IRQ_EINT3);disable_irq(IRQ_EINT8);kbdev.irq = irq;//printk("irq=%d\n",kbdev.irq);//启动定时器kb_timer.expires = jiffies + KB_TIMER_DELAY;add_timer(&kb_timer);repeat_timer.expires = jiffies + REPEAT_START_DELAY;add_timer(&repeat_timer);spin_unlock_irq(&kbdev.lock);}/*----------------------------------------------------* func: 初始化eint中断相关寄存器,安装中断处理程序* param:*** return:*------------------------------------------------------*/static int requestIrq(){int ret;/* Enable interrupt *///==================================================// irq: Linux中断号,与硬件中断号不同// handle: 中断处理程序// flag: SA_INTERRUPT指示这是个快速中断处理程序// dev_id: 用于共享的中断信号线,通常设置成NULL//===================================================ret =set_external_irq(IRQ_EINT1,EXT_FALLING_EDGE,GPIO_PULLUP_DIS); if(ret)goto eint_failed ;ret = request_irq(IRQ_EINT1, keyboard_interrupt, SA_INTERRUPT, DEVICE_NAME, NULL);if(ret)goto eint1_failed;ret =set_external_irq(IRQ_EINT8,EXT_FALLING_EDGE,GPIO_PULLUP_DIS); //EXT_LOWLEVELif(ret)goto eint_failed;ret = request_irq(IRQ_EINT8, keyboard_interrupt, SA_INTERRUPT, DEVICE_NAME, NULL);if(ret)goto eint8_failed;ret =set_external_irq(IRQ_EINT3,EXT_FALLING_EDGE,GPIO_PULLUP_DIS); if(ret)goto eint_failed;ret = request_irq(IRQ_EINT3, keyboard_interrupt, SA_INTERRUPT, DEVICE_NAME, NULL);if(ret)goto eint3_failed;return 0;eint3_failed:free_irq(IRQ_EINT3, keyboard_interrupt);eint8_failed:free_irq(IRQ_EINT8, keyboard_interrupt);eint1_failed:free_irq(IRQ_EINT1, keyboard_interrupt);eint_failed:printk(DEVICE_NAME ": IRQ Requeset Error\n");return ret;}static int s3c2440_kb_release(struct inode *inode, struct file *filp){/*注销设备*/// unregister_chrdev(kbMajor, DEVICE_NAME);/*释放中断*//*free_irq(IRQ_EINT1,NULL);free_irq(IRQ_EINT8,NULL);free_irq(IRQ_EINT3,NULL);MOD_DEC_USE_COUNT;*/return 0;}/*----------------------------------------------------* func: 初始化键盘驱动,注册字符设备* param:*** return:>=0 : 初始化键盘驱动成功<0: 失败*------------------------------------------------------*/static int __init s3c2440_kb_init(void){int ret;/*初始化管腿配置*/init_gpjcon();output_giop(0);/*注册设备*/ret = register_chrdev(99, DEVICE_NAME, &kb_fops); if(ret < 0) {printk(DEVICE_NAME " can't get major number\n"); return ret;}kbMajor = ret;printk("%s: major number=99\n",DEVICE_NAME);requestIrq();//暂时禁止所有中断,等到open时再打开disable_irq(IRQ_EINT1);disable_irq(IRQ_EINT3);disable_irq(IRQ_EINT8);return 0;}/*----------------------------------------------------* func: 注销字符设备,释放中断* param:*** return:*------------------------------------------------------*/static void __exit s3c2440_kb_exit(void){/*注销设备*/unregister_chrdev(kbMajor, DEVICE_NAME); printk("exit\n");/*释放中断*/free_irq(IRQ_EINT1,NULL);free_irq(IRQ_EINT8,NULL);free_irq(IRQ_EINT3,NULL);}module_init(s3c2440_kb_init);module_exit(s3c2440_kb_exit);//EXPORT_SYMBOL(s3c2440_kb_init);//EXPORT_SYMBOL(s3c2440_kb_exit);如果将此驱动和qtopia程序结合起来,需要修改qt源代码的qkeyboard_qws.cpp文件,添加对矩阵键盘的支持class QWSHPCButtonsHandler : public QWSKeyboardHandler {Q_OBJECTpublic:QWSHPCButtonsHandler();virtual ~QWSHPCButtonsHandler();bool isOpen() { return buttonFD > 0; }private slots:void readKeyboardData();private:QString terminalName;int buttonFD;struct termios newT, oldT;QSocketNotifier *notifier;};QWSHPCButtonsHandler::QWSHPCButtonsHandler() : QWSKeyboardHandler(){#ifdef QT_QWS_HPCterminalName = "/dev/keyboard";buttonFD = -1;notifier = 0;if ((buttonFD = open(terminalName, O_RDONLY | O_NDELAY, 0)) < 0) { qWarning("Cannot open %s\n", tin1());return;}notifier = new QSocketNotifier( buttonFD, QSocketNotifier::Read, this ); connect( notifier, SIGNAL(activated(int)),this,SLOT(readKeyboardData()) );#endif}QWSHPCButtonsHandler::~QWSHPCButtonsHandler(){#ifdef QT_QWS_HPCif ( buttonFD > 0 ) {::close( buttonFD );buttonFD = -1;}#endif}void QWSHPCButtonsHandler::readKeyboardData(){#ifdef QT_QWS_HPC//-----------port form ButtonDetect-begin-----------int tempint,i;unsigned short buffer[1];tempint=0;int current_press=-1;do{tempint=read(buttonFD,buffer,1);if(tempint > 0 ){// printf("\nCurrent Press Buttom %d \n",buffer[0]);current_press = (int)buffer[1];goto set_hpckey;}}while(tempint >0);//-----------port form ButtonDetect-end-------------set_hpckey:int k=(-1);switch(current_press) {case 3: k=Qt::Key_Up; break; //case 11: k=Qt::Key_Down; break; //case 8: k=Qt::Key_Left; break; //case 6: k=Qt::Key_Right; break; //case 7: k=Qt::Key_Enter; break; // Entercase 4: k=Qt::Key_Escape; break; // Enter default: k=(-1); break;}if ( k >= 0 ){qwsServer->processKeyEvent( 0, k, 0, 1, false ); }#endif}。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
{ .minor = MISC_DYNAMIC_MINOR,//次设备号 //动态分配设备的次设备号 //(注:使用 MISC_DYNAMIC_MINOR 这个宏有一个限制,次设备号不允许超过 63。) .name = DEVICE_NAME,//驱动名字 .fops = &dev_fops,//文件操作函数结构体
/*按键结构体实际内容*/ static struct dev_description button_irqs[] = {
{IRQ_EINT8 , S3C2410_GPG(0) , S3C2410_GPG0_EINT8 , 0, "KEY0"}, {IRQ_EINT11, S3C2410_GPG(3) , S3C2410_GPG3_EINT11 , 1, "KEY1"}, {IRQ_EINT13, S3C2410_GPG(5) , S3C2410_GPG5_EINT13 , 2, "KEY2"}, {IRQ_EINT14, S3C2410_GPG(6) , S3C2410_GPG6_EINT14 , 3, "KEY3"}, {IRQ_EINT15, S3C2410_GPG(7) , S3C2410_GPG7_EINT15 , 4, "KEY4"}, {IRQ_EINT19, S3C2410_GPG(11), S3C2410_GPG11_EINT19, 5, "KEY5"}, };
/*定义设备结构体*/ //( 这里的设备指的是按键) struct dev_description {
int irq;//按键对应的中断号 int pin;//按键所对应的 GPIO 端口 int pin_setting;//按键对应的引脚描述,实际并未用到,保留 int number;//定义键值,以传递给应用层/用户态 char *name;//每个按键的名称 };
/*****************************************************************************************/ //定义结构体//
/*****************************************************************************************/
#include <linux/miscdevice.h>//与 misc 混杂设备有关
#include <linux/ioctl.h>//与 ioctl 有关 #include <linux/gpio.h>//与 gpio 有关 #include <mach/regs-gpio.h>//与 gpio 有关 #include <linux/interrupt.h>//与 interrupt 有关 #include <linux/irq.h>//与 irq 有关 #include <asm/irq.h>//与 irq 有关
//本驱动模块加载后,会自动在/dev 目录里创建该名字(My_Buttons)的设备文件。
/*定义运算*/ #define Min(num1, num2) ((num1)<(num2) ? (num1):(num2))//定义比较获取最小值的宏 Min
/*定义变量*/ static volatile char wait_flag = 0;//定义:等待标识符,并初始化为:0 (0.等待,1.不等待) static volatile int key_values[6];//定义:6 个按键值变量 /*注:按键按下时为 0,弹起时为非 0 (注意:非 0 但并不一定是 1!具体请参阅 s3c2410_gpio_getpin() 的 相关资料!!!)*/
wake_up_interruptible(&button_wait_queue);// 唤醒列入等待队列中的进程( 让其能够响应)
wait_flag = 1;//取消等待 }
//mdelay(10);//延时 10ms 去抖 }
return IRQ_RETVAL(IRQ_HANDLED);// }
/*-----------------------------//【杨鹏(AdvancyYP)@制作】//-----------------------------*/
/* 名称 :
按键
类型 : 驱动模块
处理器型号: ARM9-S3C2440
*/
/*-----------------------------//【杨鹏(AdvancyYP)@制作】//-----------------------------*/ /*定义名称*/ #define DEVICE_NAME "MY_BUTTONS"//定义设备名称为:My_Buttons
/*定义并赋值文件操作结构体*/ static struct file_operations dev_fops = {
.owner = THIS_MODULE, .open = Buttons_Open,//open()函数的接口 .release = Buttons_Close,//close()函数的接口 .read = Buttons_Read,//read()函数的接口 };
{
key_values[button_irqs->number]=s3c2410_gpio_getpin(button_irqs->pin);//获取管脚值(注:对应的 管脚值会赋给对应的数组元素)
/*注:管脚接地时获取到的值为 0,接高电平时为非 0 (注意:非 0 但并不一定是 1!具体请参阅 s3c2410_gpio_getpin() 的相关资料!!!)*/
};
/*****************************************************************************************/ //定义及初始化等待的队列//
/*****************************************************************************************/ static DECLARE_WAIT_QUEUE_HEAD(button_wait_queue); //静态方式定义及初始化等待的队列头:button_wait_queue
struct dev_description *button_irqs = (struct dev_description *)dev_id; //将 dev_id 定义为和 dev_description 相同的结构体指针,定义 button_irqs 为和 dev_description 相同的结 构体指针, //并且与 dev_id 的地址进行对接 //即:request_irq 的 dev_id 参数会传递给该中断服务程序的 dev_id. //因此也可以将驱动程序的设备结构体通过 dev_id 传递给中断服务程序()
/*****************************************************************************************/ // 按键打开函数//
/*****************************************************************************************/ static int Buttons_Open(struct inode *inode , struct file *filp) {
static int Buttons_Open(struct inode * inode , struct file * filp);//按键打开函数 static int Buttons_Close(struct inode *inode, struct file *file);//按键关闭函数 static signed int Buttons_Read(struct file *filp, char __user *buffer, size_t size, loff_t *p);//按键读数据函数
S3C2440 Linux 按键驱动 解析
【说明】 本程序所采用的测试板为【友善之臂 mini2440】开发板,但适用于 TQ2440、TE2440
和 OK2440 等目前几乎所有主流的 S3C2440 开发板的学习!
【硬件资源】:
/*****************************************************************************************/ /************************************【驱动程序解析】*************************************/ /*****************************************************************************************/ #include <linux/kernel.h>//与内核有关 #include <linux/module.h>//与模块有关 #include <linux/types.h>//与类定义有关 #include <mach/hardware.h>//与硬件设备有关 #include <linux/platform_device.h>//与描述平台设备有关 #include <linux/sched.h>//与进程有关 #include <linux/fs.h>//与文件函数有关 #include <asm/uaccess.h>//与进程空间和内核空间交换数据 #include <linux/delay.h>//与延时函数有关 #include <linux/cdev.h>//与字符设备驱动有关