The hal_pi_gpio component seems to be beta stage
meaning I don't see anything wrong.
When I programmed full-time this was when I had to hand it over to
others to peer.
Where do I look for howto write a man page?
About the comp -----------------------------------------
Written in old-school c
For rpi's only
( so far anyway, the scheme is flexible for similar, opi bpi maybe bbb
bbw bbg )
It's real-time ( well it starts with loadrt under userspace linuxcnc
tested on rpi33 )
It only needs 1 thread and has re-settable pins
( and has a setp'able param reset_time ,
set in integer of nS units (up to 1/4 thread period))
It has inputs and in-nots
It has outputs and out-inverts and out-resets
It uses a man readable config format ( a teeny language to describe each
pin , see end of msg)
It reads switches and blinks leds
( an old boss assured me more blinky lights was a good thing )
I can send a copy to anyone wanting to try, hell its text, see attachment.
How to build in a rip installation:-------------------------
I compile it by backing up the hal_pi_gpio.c in my rip directory
~/linuxcnc-dev/src/hal/drivers/hal_pi_gpio.c (move to another dir,
don't just rename it)
then copying my file into there using the same name as the pi gpio.c
file that was there before.
Then touch it (to insure it is changed so make will notice and rebuild it )
type touch hal_pi_gpio.c in the src/hal.driver dir
Then make modules from the ~/linuxcnc/src dir
(this trickery is due to the .c file
needing other .c files in that dir,
and a simple gcc command is beyond my ken )
I get no errors no notes no warnings, its a clean make here
Once it makes, you use it with halrun like this
halrun
loadrt hal_pi_gpio pi_pins={03,<,+,x}{05,>,1,Y}{07,>,0,}{40,>,1,Y}
loadrt threads name1=slow period1=150000
addf hal_pi_gpio.read slow
add hal_pi_gpio.write slow
addf hal_pi_gpio.reset slow
setp hal_pi_gpio.reset_time 30000 (easier to see than my 2000)
start
Then use show pin and show param to watch things work
(and halmeter and halscope too)
You'll need a scope to catch the reset pins.
They change inside one thread period so halscope cant see 'em.
Hook up a bread board and switches and leds ( 3.3V i/o here!!)
( as simple as hooking up an output to an input will show you something )
About the pi_pins config
line--------------------------------------------------
It's simple but strict rules
Each pin is described by a 'quad' ( a 4 param thingy )
Each quad has the format _like_
{03,>,0,N}
This describes the rpi header pin#3
and is an output ( the '>' is used for out )
and has an initial power on value of 0 ( zero )
and does NOT have a reset feature
{05,>,1,Y}
describes header pin 5
which is an output '>'
and has intial value of 1
and has the reset feature ( requires set hal_pi_gpio.reset_time
2000 for 2us example)
{07,<,+,x}
describes header pin 7
which is an input
and has an in-not pin due to '+'
and the last param is meaningless, tested lightly with a few single
chars in that place
{12,<,-,x}
describes header pin 12
which is an input
and does not get an in-not pin , due to '-'
and last parm is meaningless
hokay
I am sure i didn't write near 1000 lines w/o mistakes
lemmeno
tomp
/********************************************************************
* 15feb2021
* compiles clean no errs not notes no warnings
*
* resets seems to work need scope trap to be sure
* scope can see 2uS pulse on step pins but halscope cannot
*
* i now have a param hal_pi_gpio.reset_time and i can setp it
* ( halrun show param shows in hex but ok its alive)
* TODO i can setp it but i suppose a default is good ideA
*
*/
#include "rtapi.h" /* RTAPI realtime OS API */
#include "rtapi_bitops.h"
#include "rtapi_app.h" /* RTAPI realtime module decls */
/* this also includes config.h */
#include "hal.h" /* HAL public API decls */
#include "bcm2835.h"
#include "cpuinfo.h"
#define BCM2708_PERI_BASE 0x20000000
#define BCM2708_GPIO_BASE (BCM2708_PERI_BASE + 0x200000)
#define BCM2709_PERI_BASE 0x3F000000
#define BCM2709_GPIO_BASE (BCM2709_PERI_BASE + 0x200000)
//#define RTAPI
#define RTAPI_BIT(nr) (1UL << (nr))
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h> /* for printf key chars*/
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
/*-------------------------------------------------------------------------------------------------------*/
/*------------------------------beg-----new arrays --------------------------*/
// i use an array witj entries for npiuns+1 0 thru and includiong npins like 0thru26 for old pi's and 0thru41 for 'new'er pis
//// these vvvv arrays can be used to lookup gpionum for rpi2 using hdrpinnum as index, a value of 0 means the pin is NOT available for gpio
// Rev 1 Raspberry: only 26 hdr pins for rpi rev2
static unsigned char nurev1_gpios[] =
{0, 0, 0, 0, 0, 1, 0, 4,14, 0,15,17,18,21, 0,22,23, 0,24,10, 0, 9,25,11, 8, 0, 7};
/*{0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26}; ruler line ruler line not code*/
// NB hdrpin 3 is gpio 0 on rpi rev1 gpio0 is not used on any other pi
// Rev2 Raspberry: only 26 hdr pins for rpi rev2 ( not same as rpi2 )
static unsigned char nurev2_gpios[] =
{0, 0, 0, 2, 0, 3, 0, 4,14, 0,15,17,18,27, 0,22,23, 0,24,10, 0, 9,25,11, 8, 0, 7};
/*{0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26}; ruler line not code */
// Raspberry2/3/4: 40 hdr pins
static unsigned char nurpi2_gpios[] =
{0, 0, 0, 2, 0, 3, 0, 4, 0, 0, 0,17,18,27, 0,22,23, 0,24,10, 0, 9,11,25, 8, 0, 7, 0, 0, 5, 0, 6,12,13, 0,19,16,26,20, 0,21};
/*{0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38.39,40}; ruler line ruler line not code*/
/*--------------------------------end---new arrays --------------------------*/
#define pinsMAX 40
/* i was using an array of struct,
// mistakenly thinking that the 1st element .pval,
// could be used as the place where the system stored the input value
// or the desired value of an output
// no, the struct/pval is just NEVER USED as is .pvalinv
// the .pval is not used because the read func stores the input pin's read value at the ptr *pinfoarray[n]
// and vs:vs the real pin in the rpi bcm2835 space is set or clr'd according to *(pinfoarray[n])
*/
/* TODO what use is this struct now?? 13feb2021 well its still good for holding the pin infprmation
// which is usefull untill the pin is created ( pnum gpionum pdir pinv pres
// 12feb2021 i chgd do pnum followed by gpionum were 1st WHY???
*/
struct pinfo
{
char pnum; // 13feb2021 chg order to pnum gpionum pval pvalinv pdir pinv pres may not be needed after hal_pin_bit_newf returns success
char gpionum;
hal_bit_t pdir; // direction 0 means OUTPUT 1 means INPUT
hal_bit_t pinv; // wether the in-not pin is wanted ( only for INPUTS)
hal_bit_t pres; // wether the erset features is wanted (only for OUTPUTS)
hal_bit_t pval;
hal_bit_t pvalinv; // TODO looks unused, remove it??
};
hal_bit_t **pinfoarray;
hal_bit_t *out_inverts;
hal_bit_t *out_resets;
static struct pinfo pinarray[41]; // 0th memeber not used, index with pinhdrnum 1-40 // pin1stuff;
// I/O access this is a ptr into the mmap'd mirror of the real gpio
static volatile unsigned *gpio;
static int npins;
static int mem_fd;
// vvv 13feb2021 needed for reset()
static unsigned long ns2tsc_factor;
#define ns2tsc(x) (((x) * (unsigned long long)ns2tsc_factor) >> 12)
static long long write_time;
hal_u32_t * reset_time_addr; /* min ns between write and reset */
int num_pins=40; // the rpi2/3/4 hdr has 40 pins, a subset are valid for gpio use
static char * cp; // a pointer into string instructing pin features
static int pin_num = 0; // an index into arrays
MODULE_AUTHOR("Michael Haberler");
MODULE_DESCRIPTION("Driver for Raspberry Pi GPIO pins");
MODULE_LICENSE("GPL");
static char * pi_pins="{0,<,+,x}";
// pi_pins is the name of the cfg strg it is not dclrd here it is magically handed to this code by Obiwan K.
RTAPI_MP_STRING(pi_pins,"quads describing each desired pin");
static int comp_id; /* component ID */
static unsigned char *gpios;
static void read_port(void *arg, long period);
static void write_port(void *arg, long period);
static void reset_port(void *arg, long period);
/* **************** all vvvvv BCM2835funcs are same code in orig ***************** */
// TODO what is the worry message about?
static __inline__ uint32_t bcm2835_peri_read(volatile uint32_t* paddr){
/* Make sure we dont return the _last_ read which might get lost
// if subsequent code changes to a different peripheral*/
volatile uint32_t ret = *paddr;
return ret;
}
// Read input pin >>gpio<< is the addr rtnd from the mmap
static __inline__ uint8_t bcm2835_gpio_lev(uint8_t pin){
volatile uint32_t* paddr = gpio + BCM2835_GPLEV0/4 + pin/32;
uint8_t shift = pin % 32;
uint32_t value = bcm2835_peri_read(paddr);
return (value & (1 << shift)) ? HIGH : LOW;
}
// TODO is this redundancy VVVVV about the caveat to always readgpio before writegpio??
static __inline__ void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value){
// Make sure we don't rely on the first write, which may get
// lost if the previous access was to a different peripheral.
*paddr = value;
*paddr = value;
}
// Set output pin >>gpio<< is the addr rtnd from mmap
static __inline__ void bcm2835_gpio_set(uint8_t pin){
volatile uint32_t* paddr = gpio + BCM2835_GPSET0/4 + pin/32;
uint8_t shift = pin % 32;
bcm2835_peri_write(paddr, 1 << shift);
}
// Clear output pin >>gpio<< is the addr rtnd from mmap
static __inline__ void bcm2835_gpio_clr(uint8_t pin){
volatile uint32_t* paddr = gpio + BCM2835_GPCLR0/4 + pin/32;
uint8_t shift = pin % 32;
bcm2835_peri_write(paddr, 1 << shift);
}
// Set/clear only the bits in value covered by the mask
static __inline__ void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask){
uint32_t v = bcm2835_peri_read(paddr);
v = (v & ~mask) | (value & mask);
bcm2835_peri_write(paddr, v);
}
/* Function select as in 'select the function of the pin'
// pin is a BCM2835 GPIO pin number NOT RPi pin number
// There are 6 control registers, each control the functions of a block
// of 10 pins.
// Each control register has 10 sets of 3 bits per GPIO pin:
//
// 000 = GPIO Pin X is an input
// 001 = GPIO Pin X is an output
// 100 = GPIO Pin X takes alternate function 0
// 101 = GPIO Pin X takes alternate function 1
// 110 = GPIO Pin X takes alternate function 2
// 111 = GPIO Pin X takes alternate function 3
// 011 = GPIO Pin X takes alternate function 4
// 010 = GPIO Pin X takes alternate function 5
//
// So the 3 bits for port X are:
// X / 10 + ((X % 10) * 3)
*/
void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode){
// >>gpio<< is the addr rtnd from mmap
// Function selects are 10 pins per 32 bit word, 3 bits per pin
volatile uint32_t* paddr = gpio + BCM2835_GPFSEL0/4 + (pin/10);
uint8_t shift = (pin % 10) * 3;
uint32_t mask = BCM2835_GPIO_FSEL_MASK << shift;
uint32_t value = mode << shift;
bcm2835_peri_set_bits(paddr, value, mask);
}
// 2 funcs to setup access to the gpio, one is alwys run, 2nd conditionally run not well understood
// TODO the fd should not be left open till exiym close asap
static int setup_gpiomem_access(void){
if ((mem_fd = open("/dev/gpiomem", O_RDWR|O_SYNC)) < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,"HAL_PI_GPIO: can't open /dev/gpiomem: %d - %s\n"
"If the error is 'permission denied' then try adding the user who runs\n"
"LinuxCNC to the gpio group: sudo gpasswd -a username gpio\n", errno, strerror(errno));
return -1;
}
gpio = mmap(NULL, BCM2835_BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, 0);
if (gpio == MAP_FAILED) {
close(mem_fd);
mem_fd = -1;
rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: mmap failed: %d - %s\n", errno, strerror(errno));
return -1;
}
return 0;
}
static int setup_gpio_access(int rev, int ncores){
// open /dev/mem
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,"HAL_PI_GPIO: can't open /dev/mem: %d - %s\n", errno, strerror(errno));
return -1;
}
if (rev <= 2 || ncores <= 2){
gpio = mmap(NULL, BCM2835_BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, BCM2708_GPIO_BASE);
}else{
gpio = mmap(NULL, BCM2835_BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, BCM2709_GPIO_BASE);
}
if (gpio == MAP_FAILED) {
rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: mmap failed: %d - %s\n", errno, strerror(errno));
return -1;;
}
return 0;
}
// TODO CHECKME with rpi4 there was some error detecting revision and an italian gent was working on it, i never heard a final solution, i did report the prob
static int number_of_cores(void){
char str[256];
int procCount = 0;
FILE *fp;
if( (fp = fopen("/proc/cpuinfo", "r")) ) {
while(fgets(str, sizeof str, fp)){
if( !memcmp(str, "processor", 9) ){
procCount++;
}
}
}
if( !procCount ) {
rtapi_print_msg(RTAPI_MSG_ERR,"HAL_PI_GPIO: Unable to get proc count. Defaulting to 2");
procCount = 2;
}
return procCount;
}
/* **************** all ^^^^^ BCM2835funcs same code in orig ***************** */
/*-beg------------------------------------------the parsing func and helpers----------------------------
// these parsing helper func return the data serached for or error -1
the number is ascii formatted to 2 chars always (0n for nums <10)
copy 2 chars to a tempstrg, cnvrt to int (or fail) and rtn int (or failure notice)
get pinnum, i expect 2 ascii chars and expect number described to be 01 to 99
*/
int pinValidForGPIO( int i ){
return(nurpi2_gpios[i]); // a return of 0 is bad
}
//
int get_pin_num(char * cp) { // gets ptr to string, returns in 01 to 00 or err -1
char buf1[3];
int numread;
int ret1;
strncpy(buf1, cp, 2);
if(*(cp+2) != ','){
printf("ERR get_pin_num() , pin number is not 2 digits followed by comma\n");
return -1;
}
buf1[2]='\0';
// checking each char is a digit is not necc, as atoi will rtn err code if conversion is bad
numread= atoi(buf1);
ret1 = pinValidForGPIO(numread);
if( ! ret1){
printf("ERR get_pin_num() , pin #%d is not valid for Rpi2 or 3 or 4, returning -1\n", numread);
return -1;
}
return numread;
}
int get_dir(char * cp) { // dir = direction, this indentifies the pin as input or output
int dir;
if(*cp == '>'){
dir = 0;
}else{
if(*cp == '<') {
dir = 1;
}else{
dir = -1;
}
}
return dir;
}
int get_initial(char * cp) {
int initial_value;
if(*cp == '0'){
initial_value = 0;
}else{
if(*cp =='1'){
initial_value = 1;
}else{
initial_value = -1;
}
}
return initial_value;
}
int get_inv_mark(char * cp){ // check if input pin wants and invert pin
int inv_wanted;
if( *cp == '+'){
inv_wanted=1;
}else{
if( *cp == '-'){
inv_wanted=0;
}else{
inv_wanted=-1; // err bad char
}
}
return inv_wanted;
}
int get_reset_mark(char * cp){ // check if output pin wants the reset feature
char ctemp=toupper(*cp);
int reset_wanted;
if(ctemp=='Y'){
reset_wanted = 1;
}else{
if(ctemp=='N'){
reset_wanted = 0;
}else{
reset_wanted = -1;
}
}
return reset_wanted;
}
/* decodeone decodes a single 'quad' eg {03,>,0,y}
it uses several helper funcs, one for each typre of data
*the helper funcs return the serached for data or -1
* the returned data should be assembled into the nth array element of pinarray[n] ( an array of structs type pinfo )
* the data can be stored using pinarray[5].pnum=5;
* once filled in, the struct's address can be added to pinfoarray ( an array of ptrs to structs) using pinfoarry[n] = &pinarray[n];
* after the ptrs are saved, the indiv elements can be
* written to pinfoarray[3]->pval = 1
* read from lastreadstate = pinfoarray[11].pval
*/
int decodeone(char * instrg ){ // read a 'quad' like {03,>,0,Y} whuich describes a single pin
/* the 'quad' begins with 2 char pinnum ( the rpi header number)
// the 2nd data is < or > for input or output, this becomes boolean 0 for out and 1 for in
// the 3rd param:
// if pin is IN then 3rd is wether a,'invert' pin is wanted , + means wanted, - means not wanted, there become 1 and 0 resp
// if pin is OUT then 3rd data is initial value for 1st thread loop, AND ( if reset feature used, this is the value the pin returns to after reset_delay
// the 3th data is not used by inputs, expected 'x' char, but any single char is ok, becomes 0 for all input pins
// if the pin is OUTPUT then this is expected Y or N meaning rested_wanted or not becomes 1 or 0
*/
int pin_dir;
int initial_value;
int invert_wanted;
int reset_wanted;
cp = instrg;
if(*cp != '{'){ // for this chunk (data from { till } , insure 1st char is '{'
printf("ERR decodeone(), 1st char not '{', it is %c\n", *cp);
return -1;
}
cp++; // the 0th char is { , point to next char which should be a digit
/* get pin_num which a 2 char strg cnvrtd to char sized int
and pnum is kept during this loop as idenitfier and index to storage arrays */
pin_num = get_pin_num(cp); // HERE cp or *cp?? cp i think
if(pin_num < 1){
printf("ERR invalid pin_num %d, returning -1\n", pin_num);
return -1;
}
cp+=3; // skip past NN and comma to where '+' or '-' is expected
pinarray[pin_num].pnum=pin_num; // assign value to the local arrays NOT to pointers yet
// TODO test all the other kinds of rpi's
pinarray[pin_num].gpionum=nurpi2_gpios[pin_num];
// get_dir, i expect *cp = '>' or '<' and return value of 0(out) or 1(in), else err
pin_dir=get_dir(cp);
if(pin_dir<0){
printf("ERR pin #%d pin_dir invalid, must be '0'(out) or '1'(in)\n", pin_num);
return -1;
}else{
pinarray[pin_num].pdir=pin_dir;
}
cp+=2;
/* the next data to parse depends on wether input or output
if input, next char defines wether invert pin is wanted or not
if output, next char is initial state of pin 0or1,
also it is the state of the pin after any reset */
if(pin_dir==0){ // if output, get initial value if inputs get invert_wanted
initial_value=get_initial(cp);
if(initial_value < 0){
printf("ERR output[%d] .pval initial value must be 0 or 1\n", pin_num);
return -1;
}else{
pinarray[pin_num].pval=initial_value;
}
}else{ // this pin is an input, find out if invert wanted
invert_wanted=get_inv_mark(cp);
if(invert_wanted > -1){
pinarray[pin_num].pinv=invert_wanted;
}else{
printf("ERR input[%d] invert char must be '+' or '-'\n", pin_num);
return -1;
}
}
cp+=2; // advance to last field, only used by outputs
/* get reset_wanted for outputs, ignore anything for inputs */
if(! pinarray[pin_num].pdir){ // only check for reset on outputs, ignore tail for inputs
reset_wanted=get_reset_mark(cp);
if(reset_wanted == -1){
printf("ERR output[%d] reset mark was none of these chars: y Y n N\n",pin_num);
return -1;
}else{
pinarray[pin_num].pres=reset_wanted;
}
}
cp+=2; // should be pointing to another begin marker '{' or endofstring '\n'
return 0;
}
/*-end------------------------------------------the parsing func and helpers----------------------------*/
int rtapi_app_main(void){
int n, retval = 0;
int ret2;
int rev, ncores;
// vvv for deadline calc in reset()
#ifdef __KERNEL__
// this calculation fits in a 32-bit unsigned
// as long as CPUs are under about 6GHz
ns2tsc_factor = (cpu_khz << 6) / 15625ul;
#else
ns2tsc_factor = 1ll<<12;
#endif
if ((rev = get_rpi_revision()) < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "unrecognized Raspberry revision, see /proc/cpuinfo\n");
return -EINVAL;
}
ncores = number_of_cores();
rtapi_print_msg(RTAPI_MSG_INFO, "%d cores rev %d", ncores, rev);
/* 07feb2021 i chgd the arrays 'rpi2_gpiopins' (et al) so the data is indexed by the hdr pin num
// and i dont use the old rpi_pins at all ( useless complexity given the simple new cfg strg method using quads
// so index is chgd at least
*/
switch (rev) {
case 5:
rtapi_print_msg(RTAPI_MSG_INFO, "Raspberry4\n");
//pins = rpi2_pins; // NEW 06feb2021 not used
gpios = nurpi2_gpios; // >>gpios<< is an array indexed by the actual rpi hdr pin
npins = sizeof(nurpi2_gpios);
break;
case 4:
rtapi_print_msg(RTAPI_MSG_INFO, "Raspberry3\n");
gpios = nurpi2_gpios;
npins = sizeof(nurpi2_gpios);
break;
case 3:
rtapi_print_msg(RTAPI_MSG_INFO, "Raspberry2\n");
gpios = nurpi2_gpios;
npins = sizeof(nurpi2_gpios);
break;
case 2:
rtapi_print_msg(RTAPI_MSG_INFO, "Raspberry1 rev 2.0\n");
gpios = nurev2_gpios;
npins = sizeof(nurev2_gpios);
break;
case 1:
rtapi_print_msg(RTAPI_MSG_INFO, "Raspberry1 rev 1.0\n");
gpios = nurev1_gpios;
npins = sizeof(nurev1_gpios);
break;
default:
rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: board revision %d not supported\n", rev);
return -EINVAL;
}
// 13feb2021 pinfoarray already has 80 posns make it 82
pinfoarray = hal_malloc( ( pinsMAX + 1) * 2 * sizeof(void *));
if (pinfoarray == 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: hal_malloc() pinfoarray failed\n");
hal_exit(comp_id);
return -1;
}
// 13feb2021 add array of 41 out_inverts
out_inverts = hal_malloc( ( pinsMAX + 1) * sizeof(void *));
if (out_inverts == 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: hal_malloc() out_inverts failed\n");
hal_exit(comp_id);
return -1;
}
reset_time_addr = hal_malloc( sizeof(hal_u32_t)); // make room for VALUE of hal_u32_t
if (reset_time_addr == 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: hal_malloc() addr for reset_time (addr hal_u32_t *) failed\n");
hal_exit(comp_id);
return -1;
}
// 13feb2021 add array of 41 out_resets
out_resets = hal_malloc( ( pinsMAX + 1) * sizeof(void *));
if (out_resets == 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: hal_malloc() out_resets failed\n");
hal_exit(comp_id);
return -1;
}
if (pi_pins == 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: no config string\n");
return -1;
}
/* NB decodeone assigns the data parsed into the local arrays of structs pinarray[n].pnum...pres
// and DOES NOT assign the address of the new struct into the ptr array pinfoarray[n] pinfoarray[pin_num] = &pinarray[pin_num];
// the loop increment inside decodeone by adding to 'cp'
*/
cp = pi_pins;
while( (ret2 = (decodeone(cp)) == 0 ) ) {
if(*cp!='{'){ // if the next char is NOT a left brace, then exit the while loop
break;
}
}
/* this code is same as orig i am not sure BOTH calls are needed,
// hint: the data returned from 1st call is clobbered by 2nd call
// but 'mostly harmless'
*/
// mmap the file /dev/gpiomem into memory and get the start address 'gpio'
if (setup_gpiomem_access()) {
if (setup_gpio_access(rev, ncores)){
return -1;
}
}
// register the comp so the hal world knows its there
comp_id = hal_init("hal_pi_gpio");
if (comp_id < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: hal_init() failed\n");
return -1;
}
// --- beg --- create & export hal_pins
for (n = 0; n < npins; n++) {
if( pinarray[n].gpionum ){
// NADA fall into ensuing code
}else{ /* this pin is not used so skip to top of loop */
continue;
}
if( ! pinarray[n].pdir ){ // if OUTPUT pin wanted ( ! .pdir would be true ) then setup phys pin
bcm2835_gpio_fsel(gpios[n], BCM2835_GPIO_FSEL_OUTP);
// 13feb2021 i want to use even indices for inputs and outputs, and odd indices for in-not is it wanted, index 0 and 1 not used
if ((retval = hal_pin_bit_newf(HAL_IN, (hal_bit_t **)(&(pinfoarray[n+n])), comp_id, "hal_pi_gpio.pin-%02d-out", n)) < 0) {
break;
}
if (( retval = hal_param_bit_newf(HAL_RW, &(out_inverts[n]), comp_id, "hal_pi_gpio.pin-%02d-out-invert", n)) < 0) {
break;
}
if( pinarray[n].pres ) {
if (( retval = hal_param_bit_newf(HAL_RW, &(out_resets[n]), comp_id, "hal_pi_gpio.pin-%02d-out-reset", n)) < 0) {
break;
}
*(&(out_resets[n])) = 1; // set the bool true high 1 hokay
}
}else{ // if INPUT pin wanted ( ! .pdir would be false )
bcm2835_gpio_fsel(gpios[n], BCM2835_GPIO_FSEL_INPT);
if ((retval = hal_pin_bit_newf(HAL_OUT, (hal_bit_t **)&pinfoarray[n+n], comp_id, "hal_pi_gpio.pin-%02d-in", n)) < 0) {
break;
}
if(pinarray[n].pinv){
if ((retval = hal_pin_bit_newf(HAL_OUT, (hal_bit_t **)&pinfoarray[n+n+1], comp_id, "hal_pi_gpio.pin-%02d-in-not", n)) < 0) {
break;
}
}
}
}
// --- end --- create and export hal_pins
if (retval < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: pin %d export failed with err=%i\n", n,retval);
hal_exit(comp_id);
return -1;
}
// --- beg create and export hal_params
if ( (retval = hal_param_u32_newf(HAL_RW, reset_time_addr, comp_id, "hal_pi_gpio.reset_time")) < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: param reset_time failed with err=%i\n", retval);
hal_exit(comp_id);
return -1;
}
// --- end create and export hal_params
// export funcs, TODO need new reset func()
retval = hal_export_funct("hal_pi_gpio.write", write_port, 0, 0, 0, comp_id);
if (retval < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: write funct export failed\n");
hal_exit(comp_id);
return -1;
}
// ok the func is named read_port but the name read is what's exported i thought this was a disconnect
retval = hal_export_funct("hal_pi_gpio.read", read_port, 0, 0, 0, comp_id);
if (retval < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,"HAL_PI_GPIO: ERROR: read funct export failed\n");
hal_exit(comp_id);
return -1;
}
retval = hal_export_funct("hal_pi_gpio.reset", reset_port, 0, 0, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "HAL_PI_GPIO: ERROR: reset funct export failed\n");
hal_exit(comp_id);
return -1;
}
rtapi_print_msg(RTAPI_MSG_INFO,"HAL_PI_GPIO: installed driver\n");
hal_ready(comp_id);
// dbg printf("driver installed, hal_ready\n");
// output pins can have an intial value, so set them and the out-invert pins as well
for(n=0;n<41;n++){
if( !(pinarray[n].pdir) && (pinarray[n].gpionum) ){ // if this pin is an output and is in use ( has gpionum assigned )
if(pinarray[n].pval){
*pinfoarray[n+n] = HIGH;
*(&(out_inverts[n])) = LOW;
}else{
*pinfoarray[n+n] = LOW;
*(&(out_inverts[n])) = HIGH;
}
}
}
return 0;
}
// TODO why would file descriptor still be open? should be closed asap
void rtapi_app_exit(void){
if (gpio){ // >>gpio<< is a mmap object
munmap((void *) gpio, BCM2835_BLOCK_SIZE);
}
if (mem_fd > -1){
close(mem_fd);
}
hal_exit(comp_id);
}
/* this will write all output pins, even those with reset feature
then will record the time when the write occured so reset can use that + the reset_time to create a 'deadline'
*/
static void write_port(void *arg, long period){
int n;
for (n = 0; n < npins; n++) {
if( (!pinarray[n].gpionum) || (pinarray[n].pdir ) ){ // if this pin is not assigned a gpionum(unused) OR input, skip to next
continue;
}
// if here pin is used (has gpionum )
if(! pinarray[n].pdir) { // i say dir=0 means out, so, !pinfoarray[n].pdir means if NOT 0
// if here pin is used AND is output (.pdir is false )
if ( *(pinfoarray[n+n]) ) { // if value of logical pin is 1 then set the physical pin
bcm2835_gpio_set(gpios[n]);
*(&(out_inverts[n])) = (hal_bit_t) LOW;
} else {
bcm2835_gpio_clr(gpios[n]);
*(&(out_inverts[n])) = (hal_bit_t) HIGH;
}
}
}
/* AFTER all writes, grab the system time */
write_time = rtapi_get_clocks(); // the time NOW, the time of the write
}
static void read_port(void *arg, long period){
int n;
for (n = 0; n < npins; n++) { // beware that pinfoarray is 80 elements! not 40
if ( (pinarray[n].pdir) && (pinarray[n].gpionum) ) { // if hasDir1 && has_gpionum, then its an used input
*pinfoarray[n+n] = bcm2835_gpio_lev(gpios[n]);
if(pinarray[n].pinv){
*pinfoarray[n+n+1] = !(bcm2835_gpio_lev(gpios[n]));
}
} // there is no else, only input pins are of interest to a read func
}
/*
// this is WRITE code but i need its work done before the write or reset funcs
// try force value on pin 5 it worked now on all outs ( and out inverts)
for(n=0;n<41;n++){
if( !(pinarray[n].pdir) && (pinarray[n].gpionum) ){ // if this pin is an output and is in use ( has gpionum assigned )
if(pinarray[n].pval){
*pinfoarray[n+n] = HIGH;
*(&(out_inverts[n])) = LOW;
}else{
*pinfoarray[n+n] = LOW;
*(&(out_inverts[n])) = HIGH;
}
}
}
*/
// dbg ?? does .pval stay same despite setp?? YES so the struct us storing the value to be reset to
//printf("pinarray[5].pval = %d\t pinarray[5]pres = %d\n",pinarray[5].pval, pinarray[5].pres);
}
/* i now have a global write_time a long long (64bit) of the systen time after the write func finished
// write_time + reset_time = deadline for when reset should be done
*/
static void reset_port(void *arg, long period){
int n;
long long deadline;
long long reset_time_tsc;
// dbg printf(" reset_time is %d\n", *(reset_time_addr) );
// 'period' seems to be the thread period that the module is addf'd to
// ---------------beg---- wait for deadline time to expire -------------------------------
if(*(reset_time_addr) > period/4){ // works i setpd 60000 with a 100000 thread and got 0x00000061A8 = 25000 ;-)
*(reset_time_addr) = period/4;
}
reset_time_tsc = ns2tsc(*(reset_time_addr)); // convert the reset_time ( given in nSecs with a setp right now ) TO decimal seconds??
deadline = reset_time_tsc + write_time;
while(rtapi_get_clocks() < deadline) {
// NADA just wait
}
// ---------------end---- wait for deadline time to expire -------------------------------
// --------------- beg --- loop thru all resettable pins and flip them-------------------NB this relies on pin JUST chgd by write()
for (n = 0; n < npins; n++) {
if( (pinarray[n].gpionum)&&(!(pinarray[n].pdir))&&(pinarray[n].pres) ){ // if this pin is A)used B)output with C)reset feature, else skip it
if(*(pinfoarray[n+n]) != pinarray[n].pval){// test skipping pins that are already = .pval
if(pinarray[n].pval){ // if the reset state is 1 HIGH TRUE then make the phys pin 1 HIGH TRUE SET, and the out_inverts 0 LOW FALSE CLR
bcm2835_gpio_set(gpios[n]); // reset to initial value 1 HIGH TRUE SET the val in pinarray[n].pval
*(&(out_inverts[n])) = (hal_bit_t) LOW;
}else{
bcm2835_gpio_clr(gpios[n]); // reset to initial value 0 LOW FALSE CLR the val in pinarray[n].pval
*(&(out_inverts[n])) = (hal_bit_t) HIGH;
}
// reset the pin desire/request to the intial value .pval
*(pinfoarray[n+n]) = pinarray[n].pval;
}
}else{
continue; // skip me, the droids you are looking for are not here
}
}
// --------------- end --- loop thru all resettable pins and set them to initial state-------------------
}
_______________________________________________
Emc-developers mailing list
Emc-developers@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/emc-developers