Thank you James, it works now! getpagesize() returns 4096, this matches the
previous map_size which is a nice sanity check. I like that this would
dynamically change with different modules as some are 1 or 2k.
The difference that made this work is casting the virtual address (pwm_addr
in your code) as a uint8_t*. I do not understand why the pointer should
change based on the type it is pointing to. To demonstrate this, the below
code prints both addresses to the console to show the same result.
Also of note is that reading in this manner seems to take ~.36 us. Not PRU
fast, but I presume this is as fast as we can get it in userspace land.
I apologize for the long code snipped but I wanted to post a complete
solution here for anyone curious.
#include <stdio.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#define QPOSCNT 0x0000
#define EQEP0_BASE 0x48300180
#define QREVID 0x005C
int main(){
int fd;
volatile unsigned long *map_base;
unsigned long target=EQEP0_BASE;
long eqep0_pos, revid;
struct timeval tv1, tv2;
unsigned long map_size = getpagesize();
unsigned long map_mask = map_size-1;
//O_SYNC makes the memory uncacheable
if ((fd = open("/dev/mem", O_RDWR | O_SYNC))==-1) {
printf("Could not open /dev/mem\n");
return 0;
}
printf("opened /dev/mem\n");
//&~map_mask truncates the address to the base of the 4K Page
map_base=(uint8_t*)mmap(0, map_size, PROT_READ|PROT_WRITE,MAP_SHARED,fd,target
&~map_mask);
if(map_base == (void *) -1) {
printf("Unable to mmap eqep\n");
exit(-1);
}
//&map_mask adds the position within the page to the address, this is
0x180 for eEQP0
unsigned long* eqep0_addr = (uint8_t *)map_base + (target&map_mask);
//time a read-loop to assess speed
int num_reads = 1000000;
int i;
gettimeofday(&tv1,NULL);
for(i=0;i<num_reads;i++){
eqep0_pos = *((uint8_t *)eqep0_addr + QPOSCNT);
}
gettimeofday(&tv2,NULL);
revid = *(uint32_t *)((uint8_t *)eqep0_addr+QREVID);
//find difference between start and end time
unsigned long dt_micros = (1000000 * tv2.tv_sec + tv2.tv_usec)-(1000000 *tv1
.tv_sec + tv1.tv_usec);
float time_per_read = (float)dt_micros/num_reads;
printf("last position %ld\n", eqep0_pos);
printf("micros per read %f\n", time_per_read);
printf("pagesize %d\n", getpagesize());
printf("map_base 0x%lX\n",map_base);
printf("(uint8_t *)map_base 0x%lX\n", (uint8_t *)map_base);
printf("target&map_mask 0x%lX\n",target&map_mask);
printf("revid 0x%lX (should read 44D31103)\n",revid);
close(fd);
return 0;
}
output:
opened /dev/mem
last position 71
micros per read 0.389692
pagesize 4096
map_base 0xB6FF1000
(uint8_t *)map_base 0xB6FF1000
target&map_mask 0x180
revid 0x44D31103 (should read 44D31103)
On Sunday, April 27, 2014 9:43:16 PM UTC-7, James Zapico wrote:
>
> mmapping must be aligned with memory pages. It just so happens that the
> GPIO module is already aligned, but since the eQEP is a subset of the PWM
> module, the eQEP base address is not page aligned.
>
> I chose to page align mmap in a more dynamic way to try to avoid some of
> that confusion later. getpagesize() is included with the unistd.h header.
> eQEP_address_ is one of the eQEP addresses you expect ending in 180:
>
> int masked_address = eQEP_address_ & ~(getpagesize()-1);
> pwm_addr = (void *) mmap(NULL, PWM_BLOCK_LENGTH,
> PROT_READ | PROT_WRITE, MAP_SHARED, eQEPFd, masked_address);
>
> int offset = eQEP_address_ & (getpagesize()-1);
> position_p = (uint32_t *) ((uint8_t *)pwm_addr + QPOSCNT + offset);
> pos_init_p = (uint32_t *) ((uint8_t *)pwm_addr + QPOSINIT + offset);
> max_pos_p = (uint32_t *) ((uint8_t *)pwm_addr + QPOSMAX + offset);
>
> -
> James
>
> On Sunday, April 27, 2014 10:46:46 PM UTC-5, Strawson wrote:
>>
>> Cheers, John! Your suggestion of 0xFA300180 matches the MMU translations
>> and places it in the linux-arm map range: vmalloc : 0xf0000000 -
>> 0xff000000. This address does return values at both the base and the
>> hardware revision offset, but not correct ones :-/
>>
>> A little more stumbling on the internet turned up the below example of
>> mmapping the GPIO peripheral at GPIO1_START_ADDR 0x4804C000 which matches
>> exactly to the address in page 159 of the AM335x Technical Reference Manual.
>>
>> https://github.com/chiragnagpal/beaglebone_mmap/blob/master/gpi.c
>>
>> I'm not sure why this would work for GPIO but not the eQEP module.
>> Guiseppe, do you mind sharing the output of your physical address
>> function call? eqepMap.at(channel).phAddress
>>
>> Just to confirm, I have the PWM and eQEP modules enabled, pinmux set
>> correctly, and correct encoder readings being displayed with the eqep
>> driver in the original post.
>>
>> Thanks for any further input,
>> Strawson
>>
>> On Saturday, April 26, 2014 10:55:40 AM UTC-7, john3909 wrote:
>>>
>>>
>>> From: Strawson <[email protected]>
>>> Reply-To: <[email protected]>
>>> Date: Saturday, April 26, 2014 at 1:46 AM
>>> To: <[email protected]>
>>> Subject: Re: [beagleboard] Official eQEP driver Support
>>>
>>> Thank you Guiseppe. I had tried an failed in the past to use mmap, but I
>>> see your solution to the error I had before is to mmap starting at the base
>>> address of the PWM peripheral instead of just at eQEP with
>>> (target&~MAP_MASK). Since ~MAP_MASK is 0xFFFFFFFFF000, the mmap starts
>>> at address 0x48300000 instead of 0x48300180 (for eqep0). Then you seem to
>>> be getting back to the base eQEP position register by adding
>>> target&MAP_MASK
>>> (0x180) back onto the virt_addr. Why the mmap function call fails
>>> without using the MAP_MASK is not entirely clear to me, but I won't worry
>>> about it.
>>>
>>> While my mmap errors are gone, I'm still only ever reading 0x83 when
>>> accessing the position value pointed to by virt-addr. If I try to access
>>> other parts of the eqep module I only get 0. For example the eqep hardware
>>> revision code which is at offset 0x5C according to the AM335xx reference
>>> manual returns 0 but should contain a value.
>>>
>>> Take a look at this E2E posting:
>>>
>>>
>>> http://e2e.ti.com/support/arm/sitara_arm/f/791/p/330787/1156969.aspx#1156969
>>>
>>> I think the address you need is 0xFA300180
>>>
>>> I have attached a printout of the MMU translations. Hopefully this
>>> helps.
>>>
>>> Regards,
>>> John
>>>
>>>
>>> My test code is below. Any thoughts?
>>>
>>> Thank you all,
>>> Strawson
>>>
>>> #include <stdio.h>
>>> #include <stdio.h>
>>> #include <stdlib.h>
>>> #include <sys/types.h>
>>> #include <sys/stat.h>
>>> #include <unistd.h>
>>> #include <fcntl.h>
>>> #include <sys/mman.h>
>>>
>>> #define MAP_SIZE 4096UL
>>> #define MAP_MASK (MAP_SIZE - 1)
>>> #define EQEP0_BASE 0x48300180
>>> #define EQEP1_BASE 0x48302180
>>> #define EQEP2_BASE 0x48304180
>>>
>>>
>>> int main(){
>>> int fd;
>>> volatile unsigned long *virt_addr;
>>> volatile unsigned long *map_base;
>>> unsigned long read_result;
>>> int i;
>>> for(i=0;i<10000;i++){
>>> unsigned long target=EQEP2_BASE;
>>> //O_SYNC makes the memory uncacheable
>>> if ((fd = open("/dev/mem", O_RDWR | O_SYNC))==-1) {
>>> printf("Could not open memory\n");
>>> return 0;
>>> }
>>> //printf("opened /dev/mem \n");
>>> //EQEP0_BASE&~MAP_MASK truncates the last 12 bits of the target address
>>> map_base=(ulong*)mmap(0, 4096, PROT_READ|PROT_WRITE,MAP_SHARED,fd,
>>> target&~MAP_MASK);
>>> if(map_base == (void *) -1) {
>>> printf("Unable to mmap eqep\n");
>>> exit(-1);
>>> }
>>> //add the last 12 bits (0x180 for eqep) back onto to the address
>>> //also add 0x5C to offset to hardware revision code
>>> virt_addr = map_base + (target & MAP_MASK)+0x5C;
>>> read_result = *((unsigned long *) virt_addr);
>>> printf("\rValue at addr: 0x%lX (%p): 0x%lX ", map_base, virt_addr,
>>> read_result);
>>> close(fd);
>>> usleep(10000);
>>> }
>>> close(fd);
>>> return 0;
>>> }
>>>
>>>
>>>
>>> --
>>> For more options, visit http://beagleboard.org/discuss
>>> ---
>>> You received this message because you are subscribed to the Google
>>> Groups "BeagleBoard" group.
>>> To unsubscribe from this group and stop receiving emails from it, send
>>> an email to [email protected].
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>>
--
For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to the Google Groups
"BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.